diff --git a/lesson_09/types/types_app/DWREADME.md b/lesson_09/types/types_app/DWREADME.md new file mode 100644 index 000000000..04372401c --- /dev/null +++ b/lesson_09/types/types_app/DWREADME.md @@ -0,0 +1,70 @@ +Bulk Sample Generator + +In addition to completing the above assignment, you are tasked with adding a flag to the app that will allow it to bulk generate sample files dynamically for each DataProvider implementation. + +## How to Run the Bulk Generator + +The app now supports a bulk generation mode that will automatically discover all DataProvider implementations and generate sample JSON files for each one. + +### Command Usage + +To run the bulk generator, use either of these command-line flags: + +```bash +# Using the long flag +./gradlew bootRun --args="--bulk" + +# Using the short flag +./gradlew bootRun --args="-b" +``` + +### What It Does + +When you run the app with the `--bulk` or `-b` flag, it will: + +1. **Discover all DataProvider implementations** - The app automatically finds all classes that extend `DataProvider` and are annotated with `@Service` +2. **Loop through each provider** - For each discovered provider (e.g., `AnthonyMaysProvider`, `DeanWalstonProvider`, etc.) +3. **Generate sample data** - Creates 10 rows of sample data based on each provider's column type specifications +4. **Save to JSON files** - Saves each provider's sample data to `{providerName}.json` in the `src/main/resources/data/` directory + +### Example Output + +If the app finds providers like `AnthonyMaysProvider` and `DeanWalstonProvider`, it will generate: +- `anthonymays.json` - Sample data based on AnthonyMaysProvider's column specifications +- `deanwalston.json` - Sample data based on DeanWalstonProvider's column specifications + +### Sample Console Output + +``` +Bulk generating sample files for all DataProvider implementations... +Found 2 DataProvider implementations: +- Generating sample file for: anthonymays + Column specifications for anthonymays: + - column1: Integer + - column2: String + - column3: Boolean + - column4: Float + - column5: Double + - column6: Long + - column7: Short + ✓ Generated: anthonymays.json +- Generating sample file for: deanwalston + Column specifications for deanwalston: + - column1: Long + - column2: Short + - column3: Double + - column4: Integer + - column5: Boolean + - column6: String + - column7: Float + ✓ Generated: deanwalston.json +Bulk generation completed! Files saved to: /path/to/src/main/resources/data +``` + +### Regular Usage (Single Provider) + +The app still supports generating files for individual providers: + +```bash +./gradlew bootRun --args="deanwalston" +``` \ No newline at end of file diff --git a/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/Lesson9.java b/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/Lesson9.java index 537012759..a59b4daaa 100644 --- a/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/Lesson9.java +++ b/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/Lesson9.java @@ -1,8 +1,12 @@ package com.codedifferently.lesson9; +import com.codedifferently.lesson9.dataprovider.DataProvider; +import com.codedifferently.lesson9.generator.BulkSampleFileGenerator; import com.codedifferently.lesson9.generator.SampleFileGenerator; import java.io.File; import java.nio.file.Paths; +import java.util.Collection; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -12,22 +16,34 @@ @SpringBootApplication(scanBasePackages = "com.codedifferently") public class Lesson9 implements CommandLineRunner { + @Autowired private Collection dataProviders; + public static void main(String[] args) { var application = new SpringApplication(Lesson9.class); application.run(args); } + @Override public void run(String... args) throws Exception { if (args.length == 0) { return; } + String path = getDataPath(); + + // Check for bulk generation flag + if (args[0].equals("--bulk") || args[0].equals("-b")) { + System.out.println("Bulk generating sample files for all DataProvider implementations..."); + var bulkGenerator = new BulkSampleFileGenerator(); + bulkGenerator.createBulkTestFiles(path, dataProviders); + return; + } + var providerName = args[0]; if (providerName == null) { throw new IllegalArgumentException("Provider name is required"); } - String path = getDataPath(); var fileGenerator = new SampleFileGenerator(); fileGenerator.createTestFile(path, providerName); } diff --git a/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/dataprovider/DeanWalstonProvider.java b/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/dataprovider/DeanWalstonProvider.java new file mode 100644 index 000000000..515390ab6 --- /dev/null +++ b/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/dataprovider/DeanWalstonProvider.java @@ -0,0 +1,23 @@ +package com.codedifferently.lesson9.dataprovider; + +import java.util.Map; +import org.springframework.stereotype.Service; + +@Service +public class DeanWalstonProvider extends DataProvider { + + public String getProviderName() { + return "deanwalston"; + } + + public Map getColumnTypeByName() { + return Map.of( + "column1", Long.class, + "column2", Short.class, + "column3", Double.class, + "column4", Integer.class, + "column5", Boolean.class, + "column6", String.class, + "column7", Float.class); + } +} diff --git a/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/generator/BulkSampleFileGenerator.java b/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/generator/BulkSampleFileGenerator.java new file mode 100644 index 000000000..0442f9923 --- /dev/null +++ b/lesson_09/types/types_app/src/main/java/com/codedifferently/lesson9/generator/BulkSampleFileGenerator.java @@ -0,0 +1,130 @@ +package com.codedifferently.lesson9.generator; + +import com.codedifferently.lesson9.dataprovider.DataProvider; +import com.codedifferently.lesson9.generator.Generators.BooleanValueGenerator; +import com.codedifferently.lesson9.generator.Generators.DoubleValueGenerator; +import com.codedifferently.lesson9.generator.Generators.FloatValueGenerator; +import com.codedifferently.lesson9.generator.Generators.IntValueGenerator; +import com.codedifferently.lesson9.generator.Generators.LongValueGenerator; +import com.codedifferently.lesson9.generator.Generators.ShortValueGenerator; +import com.codedifferently.lesson9.generator.Generators.StringValueGenerator; +import com.google.gson.GsonBuilder; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +/** A class to bulk generate sample files with random data for all DataProvider implementations. */ +public class BulkSampleFileGenerator { + + private static final Map, ValueGenerator> GENERATOR_BY_TYPE = + Map.of( + Integer.class, new IntValueGenerator(), + String.class, new StringValueGenerator(), + Double.class, new DoubleValueGenerator(), + Short.class, new ShortValueGenerator(), + Long.class, new LongValueGenerator(), + Float.class, new FloatValueGenerator(), + Boolean.class, new BooleanValueGenerator()); + + /** + * Create test files with sample data for all provided DataProvider implementations. + * + * @param path the path to the directory where the files will be created + * @param dataProviders the collection of all DataProvider implementations + */ + public void createBulkTestFiles(String path, Collection dataProviders) { + System.out.println("Found " + dataProviders.size() + " DataProvider implementations:"); + + for (DataProvider provider : dataProviders) { + System.out.println("- Generating sample file for: " + provider.getProviderName()); + createTestFileForProvider(path, provider); + } + + System.out.println("Bulk generation completed! Files saved to: " + path); + } + + /** + * Create a test file with sample data for a specific DataProvider. + * + * @param path the path to the directory where the file will be created + * @param provider the DataProvider implementation + */ + private void createTestFileForProvider(String path, DataProvider provider) { + Map columnTypeByName = provider.getColumnTypeByName(); + + System.out.println(" Column specifications for " + provider.getProviderName() + ":"); + columnTypeByName.forEach( + (column, type) -> System.out.println(" - " + column + ": " + type.getSimpleName())); + + ArrayList> rows = createSampleDataForProvider(columnTypeByName); + saveToJsonFile(path, provider.getProviderName(), rows); + } + + /** + * Create sample data rows based on the column type mapping from a DataProvider. + * + * @param columnTypeByName mapping of column names to their Java types + * @return list of sample data rows + */ + private ArrayList> createSampleDataForProvider( + Map columnTypeByName) { + var rows = new ArrayList>(); + for (var i = 0; i < 10; ++i) { + Map row = createRowForProvider(columnTypeByName); + rows.add(row); + } + return rows; + } + + /** + * Create a single row of sample data based on column type mapping. + * + * @param columnTypeByName mapping of column names to their Java types + * @return a single sample data row + */ + private Map createRowForProvider(Map columnTypeByName) { + var row = new LinkedHashMap(); + for (Map.Entry entry : columnTypeByName.entrySet()) { + String columnName = entry.getKey(); + Class columnType = entry.getValue(); + + ValueGenerator generator = GENERATOR_BY_TYPE.get(columnType); + if (generator != null) { + row.put(columnName, generator.generateValue()); + } else { + System.err.println( + "Warning: No generator found for type " + + columnType.getName() + + " in column " + + columnName + + ". Using default string value."); + row.put(columnName, "unknown_type_" + columnType.getSimpleName()); + } + } + return row; + } + + /** + * Save the sample data to a JSON file. + * + * @param path the directory path + * @param providerName the name of the provider (used as filename) + * @param rows the sample data rows + */ + private void saveToJsonFile( + String path, String providerName, ArrayList> rows) { + var file = new File(path + File.separator + providerName + ".json"); + file.getParentFile().mkdirs(); + var gson = new GsonBuilder().setPrettyPrinting().create(); + try (var writer = new FileWriter(file, false)) { + writer.write(gson.toJson(rows)); + System.out.println(" ✓ Generated: " + file.getName()); + } catch (IOException e) { + System.err.println(" ✗ Failed to generate file for " + providerName + ": " + e.getMessage()); + } + } +} diff --git a/lesson_09/types/types_app/src/main/resources/data/deanwalston.json b/lesson_09/types/types_app/src/main/resources/data/deanwalston.json new file mode 100644 index 000000000..cd048ddf2 --- /dev/null +++ b/lesson_09/types/types_app/src/main/resources/data/deanwalston.json @@ -0,0 +1,92 @@ +[ + { + "column4": "389971082", + "column5": "false", + "column6": "nshdc", + "column7": "6.135183E36", + "column1": "3383887488801300480", + "column2": "1566", + "column3": "2.815708965672392E307" + }, + { + "column4": "375126042", + "column5": "true", + "column6": "yxpo0", + "column7": "3.3008857E38", + "column1": "4982624173995602944", + "column2": "27838", + "column3": "1.6758536573713958E308" + }, + { + "column4": "574661113", + "column5": "false", + "column6": "atg4b7f", + "column7": "1.9907885E38", + "column1": "2889725125587536384", + "column2": "797", + "column3": "8.985254883099615E307" + }, + { + "column4": "1983492216", + "column5": "false", + "column6": "gpchwf3skxmq", + "column7": "2.2199312E38", + "column1": "3642242784260707328", + "column2": "26373", + "column3": "4.832903782287359E307" + }, + { + "column4": "683979250", + "column5": "false", + "column6": "3aw7tn", + "column7": "8.124872E37", + "column1": "2380753608752845312", + "column2": "20936", + "column3": "6.508463623033705E307" + }, + { + "column4": "1029159949", + "column5": "false", + "column6": "tpul1yg", + "column7": "1.5425769E37", + "column1": "8093440394763274240", + "column2": "4228", + "column3": "3.5232401779434665E306" + }, + { + "column4": "201841596", + "column5": "true", + "column6": "xhtopi2n9", + "column7": "2.654272E37", + "column1": "5247748052229204992", + "column2": "30751", + "column3": "8.465599922833556E307" + }, + { + "column4": "941802560", + "column5": "false", + "column6": "ui6d7zhvl5", + "column7": "3.3986773E38", + "column1": "7733210581626198016", + "column2": "20785", + "column3": "1.6842403765767673E308" + }, + { + "column4": "699391538", + "column5": "false", + "column6": "wtvdghcs23b", + "column7": "1.8785126E38", + "column1": "5024689015537510400", + "column2": "29837", + "column3": "1.5566962567613336E308" + }, + { + "column4": "1585384699", + "column5": "false", + "column6": "nqbjfw9mdt0", + "column7": "1.855844E38", + "column1": "7907910887862272000", + "column2": "21227", + "column3": "1.3471632227822855E307" + } +] \ No newline at end of file