Skip to content

Commit 6f61c37

Browse files
authored
feat: adds lesson_28 content (#616)
Signed-off-by: Anthony D. Mays <[email protected]>
1 parent 736fa54 commit 6f61c37

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+3101
-0
lines changed

lesson_28/createdb/createdb.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
import pandas as pd
3+
import numpy as np
4+
import sqlite3
5+
6+
# Step 1: Load the CSV file into a pandas DataFrame
7+
media_items_df = pd.read_csv('/workspaces/code-differently-24-q1/lesson_12/io/io_app/src/main/resources/csv/media_items.csv')
8+
guests_df = pd.read_csv('/workspaces/code-differently-24-q1/lesson_12/io/io_app/src/main/resources/csv/guests.csv')
9+
checked_out_items_df = pd.read_csv('/workspaces/code-differently-24-q1/lesson_12/io/io_app/src/main/resources/csv/checked_out_items.csv')
10+
checked_out_items_df['due_date'] = pd.to_datetime(checked_out_items_df['due_date']).values.astype(np.int64)
11+
12+
# Step 2: Create a connection to the SQLite database
13+
# Note: This will create the database file if it doesn't exist already
14+
os.makedirs('/workspaces/code-differently-24-q1/lesson_28/db/db_app/src/main/resources/sqlite/', exist_ok=True)
15+
conn = sqlite3.connect('../db/db_app/src/main/resources/sqlite/data.db')
16+
17+
# Step 3: Write the DataFrame to the SQLite database
18+
media_items_df.to_sql('media_items', conn, if_exists='replace', index=False)
19+
guests_df.to_sql('guests', conn, if_exists='replace', index=False)
20+
checked_out_items_df.to_sql('checked_out_items', conn, if_exists='replace', index=False)
21+
22+
# Don't forget to close the connection
23+
conn.close()

lesson_28/createdb/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pandas

lesson_28/db/.gitattributes

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# Linux start script should use lf
5+
/gradlew text eol=lf
6+
7+
# These are Windows script files and should use crlf
8+
*.bat text eol=crlf
9+

lesson_28/db/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build

lesson_28/db/db_app/build.gradle.kts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
plugins {
2+
// Apply the application plugin to add support for building a CLI application in Java.
3+
application
4+
eclipse
5+
id("com.diffplug.spotless") version "6.25.0"
6+
id("org.springframework.boot") version "3.4.0"
7+
id("com.adarshr.test-logger") version "4.0.0"
8+
id("io.freefair.lombok") version "8.6"
9+
}
10+
11+
apply(plugin = "io.spring.dependency-management")
12+
13+
repositories {
14+
// Use Maven Central for resolving dependencies.
15+
mavenCentral()
16+
}
17+
18+
dependencies {
19+
// Use JUnit Jupiter for testing.
20+
testImplementation("com.codedifferently.instructional:instructional-lib")
21+
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
22+
testImplementation("org.springframework.boot:spring-boot-starter-test")
23+
testImplementation("org.assertj:assertj-core:3.26.3")
24+
testImplementation("at.favre.lib:bcrypt:0.10.2")
25+
26+
// This dependency is used by the application.
27+
implementation("com.codedifferently.instructional:instructional-lib")
28+
implementation("com.google.guava:guava:33.3.1-jre")
29+
implementation("com.google.code.gson:gson:2.11.0")
30+
implementation("commons-cli:commons-cli:1.6.0")
31+
implementation("org.springframework.boot:spring-boot-starter")
32+
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
33+
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.0")
34+
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3")
35+
implementation("com.opencsv:opencsv:5.9")
36+
implementation("org.apache.commons:commons-csv:1.10.0")
37+
implementation("org.xerial:sqlite-jdbc:3.36.0")
38+
implementation("org.hibernate.orm:hibernate-community-dialects:6.2.7.Final")
39+
}
40+
41+
application {
42+
// Define the main class for the application.
43+
mainClass.set("com.codedifferently.lesson28.Lesson28")
44+
}
45+
46+
tasks.named<JavaExec>("run") {
47+
standardInput = System.`in`
48+
}
49+
50+
tasks.named<Test>("test") {
51+
// Use JUnit Platform for unit tests.
52+
useJUnitPlatform()
53+
}
54+
55+
56+
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
57+
58+
format("misc", {
59+
// define the files to apply `misc` to
60+
target("*.gradle", ".gitattributes", ".gitignore")
61+
62+
// define the steps to apply to those files
63+
trimTrailingWhitespace()
64+
indentWithTabs() // or spaces. Takes an integer argument if you don't like 4
65+
endWithNewline()
66+
})
67+
68+
java {
69+
// don't need to set target, it is inferred from java
70+
71+
// apply a specific flavor of google-java-format
72+
googleJavaFormat()
73+
// fix formatting of type annotations
74+
formatAnnotations()
75+
}
76+
}

lesson_28/db/db_app/lombok.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# This file is generated by the 'io.freefair.lombok' Gradle plugin
2+
config.stopBubbling = true
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.codedifferently.lesson28;
2+
3+
import com.codedifferently.lesson28.cli.LibraryApp;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.boot.CommandLineRunner;
6+
import org.springframework.boot.SpringApplication;
7+
import org.springframework.boot.autoconfigure.SpringBootApplication;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
@Configuration
11+
@SpringBootApplication(scanBasePackages = "com.codedifferently")
12+
public class Lesson28 implements CommandLineRunner {
13+
@Autowired private LibraryApp libraryApp;
14+
15+
public static void main(String[] args) {
16+
var application = new SpringApplication(Lesson28.class);
17+
application.run(args);
18+
}
19+
20+
@Override
21+
public void run(String... args) throws Exception {
22+
// Don't run as an app if we're running as a JUnit test.
23+
if (isJUnitTest()) {
24+
return;
25+
}
26+
27+
libraryApp.run(args);
28+
}
29+
30+
private static boolean isJUnitTest() {
31+
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
32+
if (element.getClassName().startsWith("org.junit.")) {
33+
return true;
34+
}
35+
}
36+
return false;
37+
}
38+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package com.codedifferently.lesson28.cli;
2+
3+
import com.codedifferently.lesson28.factory.LibraryDataLoader;
4+
import com.codedifferently.lesson28.factory.LibraryDbDataLoader;
5+
import com.codedifferently.lesson28.factory.LibraryFactory;
6+
import com.codedifferently.lesson28.library.Book;
7+
import com.codedifferently.lesson28.library.Library;
8+
import com.codedifferently.lesson28.library.LibraryInfo;
9+
import com.codedifferently.lesson28.library.MediaItem;
10+
import com.codedifferently.lesson28.library.search.SearchCriteria;
11+
import java.util.Map;
12+
import java.util.Scanner;
13+
import java.util.Set;
14+
import org.apache.commons.cli.CommandLine;
15+
import org.apache.commons.cli.CommandLineParser;
16+
import org.apache.commons.cli.DefaultParser;
17+
import org.apache.commons.cli.HelpFormatter;
18+
import org.apache.commons.cli.Option;
19+
import org.apache.commons.cli.Options;
20+
import org.apache.commons.cli.ParseException;
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.stereotype.Service;
23+
24+
@Service
25+
public final class LibraryApp {
26+
@Autowired private LibraryDbDataLoader defaultLibraryDataLoader;
27+
28+
public void run(String[] args) throws Exception {
29+
// Load the library using the specified loader from the command line or the default.
30+
LibraryDataLoader loader = getLoaderOrDefault(args, defaultLibraryDataLoader);
31+
Library library = LibraryFactory.createWithLoader(loader);
32+
33+
// Show stats about the loaded library to the user.
34+
printLibraryInfo(library);
35+
36+
try (var scanner = new Scanner(System.in)) {
37+
LibraryCommand command;
38+
// Main application loop.
39+
while ((command = promptForCommand(scanner)) != LibraryCommand.EXIT) {
40+
switch (command) {
41+
case SEARCH -> doSearch(scanner, library);
42+
default -> System.out.println("\nNot ready yet, coming soon!");
43+
}
44+
}
45+
}
46+
}
47+
48+
private void printLibraryInfo(Library library) {
49+
LibraryInfo info = library.getInfo();
50+
Map<String, Set<MediaItem>> checkedOutItemsByGuest = info.getCheckedOutItemsByGuest();
51+
int numCheckedOutItems = checkedOutItemsByGuest.values().stream().mapToInt(Set::size).sum();
52+
System.out.println();
53+
System.out.println("========================================");
54+
System.out.println("Library id: " + library.getId());
55+
System.out.println("Number of items: " + info.getItems().size());
56+
System.out.println("Number of guests: " + info.getGuests().size());
57+
System.out.println("Number of checked out items: " + numCheckedOutItems);
58+
System.out.println("========================================");
59+
System.out.println();
60+
}
61+
62+
private static LibraryDataLoader getLoaderOrDefault(
63+
String[] args, LibraryDataLoader defaultLoader) throws Exception {
64+
String loaderType = getLoaderFromCommandLine(args);
65+
return loaderType == null
66+
? defaultLoader
67+
: Class.forName(loaderType)
68+
.asSubclass(LibraryDataLoader.class)
69+
.getDeclaredConstructor()
70+
.newInstance();
71+
}
72+
73+
private static String getLoaderFromCommandLine(String[] args) throws IllegalArgumentException {
74+
Options options = new Options();
75+
Option input = new Option("l", "loader", true, "data loader type");
76+
input.setRequired(false);
77+
options.addOption(input);
78+
CommandLineParser parser = new DefaultParser();
79+
HelpFormatter formatter = new HelpFormatter();
80+
try {
81+
CommandLine cmd = parser.parse(options, args);
82+
return cmd.getOptionValue("loader");
83+
} catch (ParseException e) {
84+
System.out.println();
85+
System.out.println(e.getMessage());
86+
formatter.printHelp("utility-name", options);
87+
88+
System.exit(1);
89+
}
90+
return null;
91+
}
92+
93+
private static LibraryCommand promptForCommand(Scanner scanner) {
94+
var command = LibraryCommand.UNKNOWN;
95+
while (command == LibraryCommand.UNKNOWN) {
96+
printMenu();
97+
var input = scanner.nextLine();
98+
try {
99+
command = LibraryCommand.fromValue(Integer.parseInt(input.trim()));
100+
} catch (IllegalArgumentException e) {
101+
System.out.println("Invalid command: " + input);
102+
}
103+
}
104+
return command;
105+
}
106+
107+
private static void printMenu() {
108+
System.out.println("\nEnter the number of the desired command:");
109+
System.out.println("1) << EXIT");
110+
System.out.println("2) SEARCH");
111+
System.out.println("3) CHECKOUT");
112+
System.out.println("4) RETURN");
113+
System.out.print("command> ");
114+
}
115+
116+
private void doSearch(Scanner scanner, Library library) {
117+
LibrarySearchCommand command = promptForSearchCommand(scanner);
118+
if (command == LibrarySearchCommand.RETURN) {
119+
return;
120+
}
121+
SearchCriteria criteria = getSearchCriteria(scanner, command);
122+
Set<MediaItem> results = library.search(criteria);
123+
printSearchResults(results);
124+
}
125+
126+
private LibrarySearchCommand promptForSearchCommand(Scanner scanner) {
127+
var command = LibrarySearchCommand.UNKNOWN;
128+
while (command == LibrarySearchCommand.UNKNOWN) {
129+
printSearchMenu();
130+
var input = scanner.nextLine();
131+
try {
132+
command = LibrarySearchCommand.fromValue(Integer.parseInt(input.trim()));
133+
} catch (IllegalArgumentException e) {
134+
System.out.println("Invalid command: " + input);
135+
}
136+
}
137+
return command;
138+
}
139+
140+
private void printSearchMenu() {
141+
System.out.println("\nEnter the number of the desired search criteria:");
142+
System.out.println("1) << RETURN");
143+
System.out.println("2) TITLE");
144+
System.out.println("3) AUTHOR");
145+
System.out.println("4) TYPE");
146+
System.out.print("search> ");
147+
}
148+
149+
private SearchCriteria getSearchCriteria(Scanner scanner, LibrarySearchCommand command) {
150+
System.out.println();
151+
switch (command) {
152+
case TITLE -> {
153+
System.out.println("Enter the title to search for: ");
154+
System.out.print("title> ");
155+
var title = scanner.nextLine();
156+
return SearchCriteria.builder().title(title).build();
157+
}
158+
case AUTHOR -> {
159+
System.out.println("Enter the author to search for: ");
160+
System.out.print("author> ");
161+
var author = scanner.nextLine();
162+
return SearchCriteria.builder().author(author).build();
163+
}
164+
case TYPE -> {
165+
System.out.println("Enter the type to search for: ");
166+
System.out.print("type> ");
167+
var type = scanner.nextLine();
168+
return SearchCriteria.builder().type(type).build();
169+
}
170+
default -> System.out.println("Invalid search command: " + command);
171+
}
172+
return null;
173+
}
174+
175+
private void printSearchResults(Set<MediaItem> results) {
176+
System.out.println();
177+
178+
if (results.isEmpty()) {
179+
System.out.println("No results found.");
180+
return;
181+
}
182+
183+
System.out.println("Search results:\n");
184+
for (MediaItem item : results) {
185+
System.out.println("ID: " + item.getId());
186+
System.out.println("TITLE: " + item.getTitle());
187+
if (item instanceof Book book) {
188+
System.out.println("AUTHOR(S): " + String.join(", ", book.getAuthors()));
189+
}
190+
System.out.println("TYPE: " + item.getType().toUpperCase());
191+
System.out.println();
192+
}
193+
System.out.println("Found " + results.size() + " result(s).\n");
194+
}
195+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.codedifferently.lesson28.cli;
2+
3+
public enum LibraryCommand {
4+
UNKNOWN(0),
5+
EXIT(1),
6+
SEARCH(2),
7+
CHECKOUT(3),
8+
RETURN(4);
9+
10+
private final int value;
11+
12+
LibraryCommand(int value) {
13+
this.value = value;
14+
}
15+
16+
public int getValue() {
17+
return value;
18+
}
19+
20+
public static LibraryCommand fromValue(int value) {
21+
for (LibraryCommand command : LibraryCommand.values()) {
22+
if (command.getValue() == value) {
23+
return command;
24+
}
25+
}
26+
return UNKNOWN;
27+
}
28+
}

0 commit comments

Comments
 (0)