diff --git a/.gitignore b/.gitignore index d93b7a0..9ac1a69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ *venv* *.ipynb_checkpoints* -.vscode - -node_modules -test-results +.vscode \ No newline at end of file diff --git a/beta_use_cases/README.md b/beta_use_cases/README.md new file mode 100644 index 0000000..535f85b --- /dev/null +++ b/beta_use_cases/README.md @@ -0,0 +1,31 @@ +# Use Case Datasets + +This directory includes datasets for use on the "use cases" landing pages for mathesar.org. + +## Loading into Mathesar + +Each dataset has a `schema.sql` and `generated_data.sql` file which can be loaded into Mathesar. Each `schema.sql` file will drop an existing schema with the same name and create a new one. + +Here's an example of loading these into a locally-running Mathesar instance. + +```shell +# (Optional) Generate the data +python {use_case_name}/generate_data.py + +# First load the schema and tables +docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < {use_case_name}/schema.sql +# Then the sample data +docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < {use_case_name}/generated_data.sql +``` + +## Philosophy + +These datasets use a mix of "low fidelity" faker data and more domain-specific hardcoded strings to create fake, but plausible, datasets for various Mathesar use cases. + +Timestamp columns that would be used for auditing, soft deletes, and so on have been omitted to reduce clutter. + +Column IDs are always `BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY`. + +## Development + +The only requirement is to install dependencies with `pip install -r requirements.txt`. diff --git a/beta_use_cases/bike_service_shop/README.md b/beta_use_cases/bike_service_shop/README.md new file mode 100644 index 0000000..a4e43e1 --- /dev/null +++ b/beta_use_cases/bike_service_shop/README.md @@ -0,0 +1,60 @@ +# Bike Shop sample data + +This sample dataset represents a bicycle shop managing their customer service requests. + +```mermaid +%% https://mermaid.js.org/syntax/entityRelationshipDiagram.html + +erDiagram + Customers { + BIGINT id PK + TEXT first_name "NOT NULL" + TEXT last_name "NOT NULL" + TEXT email + TEXT phone + } + EquipmentTypes { + BIGINT id PK + TEXT name "NOT NULL" + } + Equipment { + BIGINT id PK + TEXT serial_number "NOT NULL UNIQUE" + TEXT notes + BIGINT type_id FK + } + Mechanics { + BIGINT id PK + TEXT first_name "NOT NULL" + TEXT last_name "NOT NULL" + } + ServiceStatuses { + BIGINT id PK + TEXT name "NOT NULL UNIQUE" + } + ServiceRequests { + BIGINT id PK + BIGINT customer_id FK + BIGINT equipment_id FK + BIGINT mechanic_id FK + TEXT request_description "NOT NULL" + NUMERIC_10_2 cost + TIMESTAMP time_in + TIMESTAMP time_out + } + ServiceMilestones { + BIGINT id PK + BIGINT service_request_id FK + BIGINT status_id FK + TIMESTAMP update_time "DEFAULT NOW()" + TEXT notes + } + + Equipment ||--|| EquipmentTypes : "type_id" + ServiceRequests ||--|| Customers : "customer_id" + ServiceRequests ||--|| Equipment : "equipment_id" + ServiceRequests ||--|| Mechanics : "mechanic_id" + ServiceMilestones ||--|| ServiceRequests : "service_request_id" + ServiceMilestones ||--|| ServiceStatuses : "status_id" + +``` diff --git a/beta_use_cases/bike_service_shop/generate_data.py b/beta_use_cases/bike_service_shop/generate_data.py new file mode 100644 index 0000000..21e7b32 --- /dev/null +++ b/beta_use_cases/bike_service_shop/generate_data.py @@ -0,0 +1,205 @@ +import os +import random +from datetime import timedelta, datetime +from faker import Faker + +fake = Faker() + +# Helper functions +def clean_value(value): + """Clean a value for SQL COPY operations.""" + if value is None: + return r"\N" + if isinstance(value, str): + return value.replace("\t", " ").replace("\n", " ") + return str(value) + +def write_to_sql_file(output_path, search_path, tables): + """Write the generated data to an SQL file.""" + with open(output_path, "w") as f: + f.write(f'SET search_path="{search_path}";\n\n') + for table_name, generator in tables.items(): + f.write(f'COPY "{table_name}" FROM stdin;\n') + for row in generator: + cleaned_row = "\t".join(map(clean_value, row)) + f.write(f"{cleaned_row}\n") + f.write("\\.\n\n") + print(f"SQL file generated: {output_path}") + +def get_output_file_path(filename): + """Get the output file path relative to the current script's directory.""" + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_file_dir, filename) + +# Constants +NUM_CUSTOMERS = 20 +NUM_MECHANICS = 5 +NUM_EQUIPMENT_TYPES = 8 +NUM_EQUIPMENT = 50 +NUM_SERVICE_REQUESTS = 30 +NUM_SERVICE_MILESTONES = 100 + +EQUIPMENT_TYPES = [ + "Mountain Bike", + "Road Bike", + "Hybrid Bike", + "Electric Bike", + "BMX Bike", + "Cyclocross Bike", + "Folding Bike", + "Touring Bike" +] + +PARTS_AND_NOTES = { + "Frame": [ + "Small dent on the top tube identified during inspection.", + "Frame cleaned and polished; customer commented on how shiny it looked.", + "Noticed a crack near the bottom bracket; recommended a replacement.", + ], + "Wheels": [ + "Bent rear rim; trued the wheel successfully.", + "Replaced a broken spoke on the front wheel.", + "Customer pleased with how smooth the wheels now spin.", + ], + "Tires": [ + "Replaced a worn-out rear tire; customer opted for puncture-resistant model.", + "Front tire inflated; slow leak detected and patched.", + "Customer appreciated advice on tire pressure for road biking.", + ], + "Brakes": [ + "Adjusted brake pads for better stopping power.", + "Rear brake cable frayed; replaced with a new one.", + "Customer remarked how responsive the brakes feel now.", + ], + "Gears": [ + "Shifted gears sticking; replaced derailleur hanger.", + "Customer reported skipping gears; adjusted indexing.", + "Lubricated drivetrain; customer noticed quieter pedaling.", + ], + "Handlebars": [ + "Re-wrapped handlebar tape; customer loved the color choice.", + "Handlebar alignment corrected; was slightly off-center.", + "Installed new ergonomic grips; customer was excited about the comfort.", + ], + "Pedals": [ + "Left pedal bearings replaced due to grinding noise.", + "Upgraded pedals to a clipless system; customer very happy.", + "Mechanic noticed loose threads on right pedal spindle; tightened securely.", + ], + "Seat": [ + "Seatpost adjusted for proper height; customer reported better comfort.", + "Replaced torn saddle with a new gel-padded seat.", + "Customer commented that the saddle now feels like new.", + ], +} + +REQUEST_DESCRIPTIONS = [ + "Bike makes a clicking noise while pedaling.", + "Brakes feel soft and don't stop effectively.", + "Gears are not shifting smoothly.", + "Rear wheel wobbles; possible rim issue.", + "Flat tire; needs replacement or repair.", + "Customer wants a full tune-up before a race.", + "Looking to upgrade to tubeless tires.", + "Front fork feels stiff; possible suspension issue.", + "Customer complained about an uncomfortable saddle.", + "Handlebars feel loose and need adjustment.", + "Chain keeps falling off during rides.", + "Rear derailleur seems bent after a crash.", + "Customer wants clipless pedals installed.", + "Headset creaks when turning the handlebars.", + "Electric bike battery isn't holding charge.", + "Customer wants help installing accessories (e.g., lights, rack).", + "Bike feels heavy and sluggish; might need a drivetrain cleaning.", + "Suspension setup needs adjusting for rider weight.", + "Customer reported squeaky brakes after riding in wet conditions.", + "Child seat needs to be installed securely on the frame.", +] + +SERVICE_STATUSES = ["Received", "In Progress", "Awaiting Part", "Completed"] + +def generate_customers(): + for i in range(1, NUM_CUSTOMERS + 1): + yield [ + i, + fake.first_name(), + fake.last_name(), + fake.email(), + fake.phone_number() + ] + +def generate_equipment_types(): + for i, name in enumerate(EQUIPMENT_TYPES, start=1): + yield [i, name] + +def generate_mechanics(): + for i in range(1, NUM_MECHANICS + 1): + yield [ + i, + fake.first_name(), + fake.last_name() + ] + +def generate_service_statuses(): + for i, name in enumerate(SERVICE_STATUSES, start=1): + yield [i, name] + +def generate_equipment(equipment_type_ids): + for i in range(1, NUM_EQUIPMENT + 1): + yield [ + i, + random.choice(equipment_type_ids), # Valid type_id + fake.unique.ean13(), # serial number + "" + ] + +def generate_service_requests(customer_ids, equipment_ids, mechanic_ids): + for i in range(1, NUM_SERVICE_REQUESTS + 1): + yield [ + i, + random.choice(customer_ids), # Valid customer_id + random.choice(equipment_ids), # Valid equipment_id + random.choice(mechanic_ids), # Valid mechanic_id + random.choice(REQUEST_DESCRIPTIONS), # Realistic request description + round(random.uniform(20, 500), 2), + fake.date_time_this_year(), + fake.date_time_this_year() if random.random() < 0.5 else None + ] + +def generate_service_milestones(service_request_ids, status_ids): + for i in range(1, NUM_SERVICE_MILESTONES + 1): + part, notes = random.choice(list(PARTS_AND_NOTES.items())) + yield [ + i, + random.choice(service_request_ids), # Valid service_request_id + random.choice(status_ids), # Valid status_id + fake.date_time_this_year(), + f"{part}: {random.choice(notes)}" # Realistic service note + ] + +if __name__ == "__main__": + # Generate valid IDs based on schema + customer_ids = list(range(1, NUM_CUSTOMERS + 1)) + equipment_type_ids = list(range(1, NUM_EQUIPMENT_TYPES + 1)) + equipment_ids = list(range(1, NUM_EQUIPMENT + 1)) + mechanic_ids = list(range(1, NUM_MECHANICS + 1)) + service_request_ids = list(range(1, NUM_SERVICE_REQUESTS + 1)) + status_ids = list(range(1, len(SERVICE_STATUSES) + 1)) + + # Generate tables + equipment = list(generate_equipment(equipment_type_ids)) + service_requests = list(generate_service_requests(customer_ids, equipment_ids, mechanic_ids)) + service_milestones = list(generate_service_milestones(service_request_ids, status_ids)) + + tables = { + "Customers": generate_customers(), + "Equipment Types": generate_equipment_types(), + "Equipment": iter(equipment), # Pre-generated equipment + "Mechanics": generate_mechanics(), + "Service Statuses": generate_service_statuses(), + "Service Requests": iter(service_requests), # Pre-generated service requests + "Service Milestones": iter(service_milestones), # Pre-generated milestones + } + + sql_file = get_output_file_path("generated_data.sql") + write_to_sql_file(sql_file, "Bike Shop", tables) diff --git a/beta_use_cases/bike_service_shop/generated_data.sql b/beta_use_cases/bike_service_shop/generated_data.sql new file mode 100644 index 0000000..e9e334f --- /dev/null +++ b/beta_use_cases/bike_service_shop/generated_data.sql @@ -0,0 +1,240 @@ +SET search_path="Bike Shop"; + +COPY "Customers" FROM stdin; +1 Dana Hammond brianlucero@example.net 500.434.0936x02343 +2 Andrew Haley qjackson@example.com 402-383-1537 +3 Latoya Ryan amystanton@example.org 5734421348 +4 Luis Craig garyowens@example.org 764-473-7881 +5 Anna Wilson patricialogan@example.org +1-596-698-4888 +6 Jennifer Jones andrew77@example.net 287-769-1758x09930 +7 John Dickson ericgilbert@example.com 4703913317 +8 Joshua Morrow ydorsey@example.net 2309613577 +9 Tracy Knapp jonesmichael@example.com +1-507-277-0079x0202 +10 Matthew Castro patrickhughes@example.net 001-608-917-0321x7060 +11 Alexis Matthews hermanpaul@example.org 849-702-8934x365 +12 Tracey Arnold suzanne42@example.com 975.894.8159x7223 +13 Jonathan Kennedy rogerschristopher@example.net (325)363-2885 +14 Dawn Martinez vsmith@example.net 001-881-825-7502 +15 Elizabeth Abbott mccoykelly@example.com 001-367-630-4020x2308 +16 Lucas Jones taylorheather@example.net 618.618.6664 +17 Caitlin Cohen brittanymiller@example.net 632.511.4592 +18 Tamara Williams mary88@example.org 240-204-5496 +19 James Smith walkerkatherine@example.org 6562600692 +20 Mark Ramos javier21@example.net 969.592.1524x3010 +\. + +COPY "Equipment Types" FROM stdin; +1 Mountain Bike +2 Road Bike +3 Hybrid Bike +4 Electric Bike +5 BMX Bike +6 Cyclocross Bike +7 Folding Bike +8 Touring Bike +\. + +COPY "Equipment" FROM stdin; +1 6 6458772687570 +2 4 6594089210329 +3 6 5190009593483 +4 4 6718554847050 +5 5 9200252313991 +6 3 4457791543531 +7 1 1313596868138 +8 6 7597318412708 +9 8 4822292633102 +10 8 6928696723901 +11 2 9087530160153 +12 7 8156041618444 +13 8 6339115962717 +14 6 0239931969258 +15 5 7605596045249 +16 6 1321956525430 +17 4 7310667388148 +18 7 5412973340598 +19 2 5904086137092 +20 2 3143755868147 +21 8 7043091616982 +22 4 8977680662663 +23 2 7093982076906 +24 3 3005620372046 +25 7 0986708322357 +26 5 3879027954651 +27 1 6396350982436 +28 8 8160438444721 +29 2 1647803671328 +30 4 1530779227975 +31 2 7459687096371 +32 8 0696592306301 +33 3 5561083523862 +34 1 5021832255203 +35 1 7788337724167 +36 2 5297339846811 +37 6 6663537305655 +38 3 9531881398159 +39 1 9778005257590 +40 8 9497182259592 +41 4 7832125962412 +42 6 0255046378549 +43 2 2204600333980 +44 3 0511149076134 +45 4 1905685515315 +46 5 7838907236399 +47 6 5354962280679 +48 5 1278560664928 +49 8 2620475088747 +50 2 0982582070834 +\. + +COPY "Mechanics" FROM stdin; +1 Charles Adkins +2 Robert Thompson +3 Megan Thomas +4 Matthew Wheeler +5 Tracy Charles +\. + +COPY "Service Statuses" FROM stdin; +1 Received +2 In Progress +3 Awaiting Part +4 Completed +\. + +COPY "Service Requests" FROM stdin; +1 10 20 2 Front fork feels stiff; possible suspension issue. 458.1 2025-01-05 10:11:28.768122 2025-01-15 10:24:10.038635 +2 10 48 5 Front fork feels stiff; possible suspension issue. 261.49 2025-01-08 16:10:27.364208 2025-01-15 17:25:56.997396 +3 16 32 2 Flat tire; needs replacement or repair. 483.91 2025-01-04 05:48:32.389068 \N +4 17 17 3 Chain keeps falling off during rides. 453.96 2025-01-08 03:27:45.850520 2025-01-05 05:13:56.358573 +5 1 32 2 Flat tire; needs replacement or repair. 103.24 2025-01-10 02:20:00.766097 2025-01-11 15:35:10.915119 +6 15 18 1 Electric bike battery isn't holding charge. 197.54 2025-01-01 12:03:58.042472 \N +7 7 3 4 Bike makes a clicking noise while pedaling. 233.9 2025-01-13 11:20:58.417285 \N +8 2 21 3 Customer wants clipless pedals installed. 485.6 2025-01-09 13:27:47.273917 2025-01-11 09:47:39.761001 +9 20 8 2 Bike makes a clicking noise while pedaling. 81.49 2025-01-06 19:51:24.391168 \N +10 3 14 5 Chain keeps falling off during rides. 27.05 2025-01-11 19:53:08.606539 \N +11 13 18 5 Gears are not shifting smoothly. 481.54 2025-01-09 06:56:16.379648 \N +12 2 16 4 Headset creaks when turning the handlebars. 472.97 2025-01-11 05:58:06.703393 \N +13 14 10 5 Customer wants clipless pedals installed. 286.19 2025-01-14 00:44:47.515397 2025-01-01 10:35:45.207151 +14 11 34 1 Bike makes a clicking noise while pedaling. 216.63 2025-01-14 23:39:50.833382 2025-01-14 00:24:09.762261 +15 10 14 2 Rear wheel wobbles; possible rim issue. 231.79 2025-01-06 20:24:41.615423 \N +16 5 48 2 Looking to upgrade to tubeless tires. 469.17 2025-01-15 16:32:01.041626 \N +17 5 13 3 Headset creaks when turning the handlebars. 271.76 2025-01-11 13:03:39.234821 \N +18 2 8 5 Flat tire; needs replacement or repair. 336.89 2025-01-02 20:24:57.577083 \N +19 2 44 1 Rear derailleur seems bent after a crash. 234.89 2025-01-01 04:41:49.123657 2025-01-08 03:11:36.165671 +20 2 2 2 Handlebars feel loose and need adjustment. 68.9 2025-01-04 15:39:04.496455 2025-01-09 15:19:04.966949 +21 7 33 3 Chain keeps falling off during rides. 482.64 2025-01-06 22:59:03.201623 2025-01-04 11:08:54.975466 +22 2 8 2 Rear derailleur seems bent after a crash. 294.89 2025-01-03 03:16:14.928212 \N +23 7 5 3 Rear derailleur seems bent after a crash. 93.91 2025-01-06 11:17:17.671771 2025-01-11 01:01:42.800117 +24 4 18 4 Brakes feel soft and don't stop effectively. 174.67 2025-01-02 21:19:45.129610 2025-01-07 07:31:27.930341 +25 12 11 5 Brakes feel soft and don't stop effectively. 430.31 2025-01-12 01:24:57.391244 \N +26 19 50 2 Child seat needs to be installed securely on the frame. 431.75 2025-01-16 00:37:20.169067 \N +27 11 27 2 Customer wants clipless pedals installed. 116.08 2025-01-14 13:16:53.938555 \N +28 17 37 3 Electric bike battery isn't holding charge. 184.13 2025-01-10 14:40:12.640842 \N +29 8 44 2 Child seat needs to be installed securely on the frame. 166.73 2025-01-06 21:30:57.229592 2025-01-09 11:43:42.579269 +30 20 33 5 Customer wants help installing accessories (e.g., lights, rack). 440.01 2025-01-10 19:04:08.686107 \N +\. + +COPY "Service Milestones" FROM stdin; +1 10 2 2025-01-07 08:47:46.217143 Frame: Noticed a crack near the bottom bracket; recommended a replacement. +2 10 2 2025-01-14 21:53:49.646056 Wheels: Customer pleased with how smooth the wheels now spin. +3 2 2 2025-01-16 03:49:19.193744 Seat: Replaced torn saddle with a new gel-padded seat. +4 11 2 2025-01-01 14:47:41.900180 Gears: Lubricated drivetrain; customer noticed quieter pedaling. +5 15 3 2025-01-03 20:15:51.170396 Handlebars: Handlebar alignment corrected; was slightly off-center. +6 17 1 2025-01-08 17:22:39.155486 Pedals: Upgraded pedals to a clipless system; customer very happy. +7 26 1 2025-01-10 10:38:02.995214 Brakes: Adjusted brake pads for better stopping power. +8 19 4 2025-01-09 04:36:45.996913 Brakes: Adjusted brake pads for better stopping power. +9 8 4 2025-01-08 18:23:47.468222 Brakes: Rear brake cable frayed; replaced with a new one. +10 26 3 2025-01-08 09:12:41.813921 Frame: Noticed a crack near the bottom bracket; recommended a replacement. +11 8 2 2025-01-08 20:10:16.393944 Brakes: Adjusted brake pads for better stopping power. +12 16 2 2025-01-12 10:50:06.552320 Seat: Customer commented that the saddle now feels like new. +13 30 2 2025-01-03 19:01:00.816007 Tires: Replaced a worn-out rear tire; customer opted for puncture-resistant model. +14 10 2 2025-01-15 07:55:45.629029 Tires: Customer appreciated advice on tire pressure for road biking. +15 30 4 2025-01-01 06:48:38.057140 Gears: Lubricated drivetrain; customer noticed quieter pedaling. +16 28 1 2025-01-03 04:44:26.454817 Pedals: Upgraded pedals to a clipless system; customer very happy. +17 18 4 2025-01-09 10:01:37.291468 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +18 6 3 2025-01-05 00:56:05.460386 Tires: Customer appreciated advice on tire pressure for road biking. +19 8 1 2025-01-16 16:12:54.908881 Brakes: Rear brake cable frayed; replaced with a new one. +20 22 4 2025-01-10 01:31:00.976382 Pedals: Upgraded pedals to a clipless system; customer very happy. +21 24 1 2025-01-13 01:57:55.013202 Frame: Frame cleaned and polished; customer commented on how shiny it looked. +22 6 2 2025-01-08 02:57:08.638848 Seat: Customer commented that the saddle now feels like new. +23 13 2 2025-01-08 11:12:10.861305 Brakes: Customer remarked how responsive the brakes feel now. +24 7 1 2025-01-03 23:27:02.387476 Pedals: Upgraded pedals to a clipless system; customer very happy. +25 12 1 2025-01-09 16:56:10.217908 Wheels: Replaced a broken spoke on the front wheel. +26 21 1 2025-01-01 20:13:06.888947 Frame: Noticed a crack near the bottom bracket; recommended a replacement. +27 18 2 2025-01-05 21:03:53.670845 Pedals: Mechanic noticed loose threads on right pedal spindle; tightened securely. +28 6 4 2025-01-10 02:57:38.500126 Seat: Replaced torn saddle with a new gel-padded seat. +29 21 1 2025-01-15 03:17:37.690491 Frame: Small dent on the top tube identified during inspection. +30 2 1 2025-01-06 17:35:00.879927 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +31 18 1 2025-01-07 18:13:48.771544 Pedals: Upgraded pedals to a clipless system; customer very happy. +32 18 2 2025-01-12 07:02:03.466806 Seat: Customer commented that the saddle now feels like new. +33 22 2 2025-01-02 22:43:37.408209 Frame: Small dent on the top tube identified during inspection. +34 10 4 2025-01-06 14:07:43.797434 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +35 1 4 2025-01-09 10:32:20.447052 Seat: Customer commented that the saddle now feels like new. +36 26 2 2025-01-03 04:55:00.043649 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +37 18 2 2025-01-04 03:00:33.453246 Gears: Customer reported skipping gears; adjusted indexing. +38 4 1 2025-01-12 02:49:09.994843 Frame: Small dent on the top tube identified during inspection. +39 8 1 2025-01-02 13:32:22.550040 Brakes: Rear brake cable frayed; replaced with a new one. +40 29 3 2025-01-01 14:47:35.608643 Brakes: Customer remarked how responsive the brakes feel now. +41 24 1 2025-01-12 19:07:40.671851 Handlebars: Handlebar alignment corrected; was slightly off-center. +42 5 1 2025-01-03 11:24:44.784795 Wheels: Replaced a broken spoke on the front wheel. +43 11 2 2025-01-07 09:07:49.404417 Brakes: Adjusted brake pads for better stopping power. +44 26 2 2025-01-05 23:07:49.298178 Brakes: Customer remarked how responsive the brakes feel now. +45 9 2 2025-01-04 07:33:35.602183 Tires: Customer appreciated advice on tire pressure for road biking. +46 18 3 2025-01-12 16:21:27.611156 Wheels: Bent rear rim; trued the wheel successfully. +47 5 3 2025-01-01 05:17:01.035835 Frame: Frame cleaned and polished; customer commented on how shiny it looked. +48 30 4 2025-01-12 23:37:54.705064 Wheels: Customer pleased with how smooth the wheels now spin. +49 29 4 2025-01-08 23:42:24.226790 Pedals: Upgraded pedals to a clipless system; customer very happy. +50 3 1 2025-01-14 04:23:15.225983 Pedals: Mechanic noticed loose threads on right pedal spindle; tightened securely. +51 10 1 2025-01-04 13:03:41.987045 Seat: Customer commented that the saddle now feels like new. +52 20 2 2025-01-08 20:16:51.693332 Pedals: Upgraded pedals to a clipless system; customer very happy. +53 30 3 2025-01-16 08:44:05.328637 Tires: Replaced a worn-out rear tire; customer opted for puncture-resistant model. +54 25 4 2025-01-07 22:58:50.430439 Brakes: Adjusted brake pads for better stopping power. +55 19 1 2025-01-12 03:34:01.011877 Pedals: Left pedal bearings replaced due to grinding noise. +56 6 1 2025-01-03 20:19:40.075034 Pedals: Left pedal bearings replaced due to grinding noise. +57 1 3 2025-01-15 11:55:15.440209 Wheels: Replaced a broken spoke on the front wheel. +58 14 2 2025-01-03 03:33:45.567633 Brakes: Customer remarked how responsive the brakes feel now. +59 3 2 2025-01-11 17:19:42.898977 Frame: Small dent on the top tube identified during inspection. +60 26 1 2025-01-06 19:15:00.420295 Brakes: Rear brake cable frayed; replaced with a new one. +61 13 1 2025-01-14 12:16:37.648064 Brakes: Adjusted brake pads for better stopping power. +62 1 2 2025-01-03 08:26:01.927882 Brakes: Customer remarked how responsive the brakes feel now. +63 29 2 2025-01-14 00:16:34.229102 Tires: Replaced a worn-out rear tire; customer opted for puncture-resistant model. +64 20 4 2025-01-10 21:03:09.608222 Handlebars: Re-wrapped handlebar tape; customer loved the color choice. +65 29 4 2025-01-07 07:00:07.904576 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +66 18 1 2025-01-02 10:05:24.989370 Wheels: Bent rear rim; trued the wheel successfully. +67 9 4 2025-01-02 05:23:49.940989 Brakes: Adjusted brake pads for better stopping power. +68 29 1 2025-01-05 12:44:50.590624 Brakes: Customer remarked how responsive the brakes feel now. +69 14 1 2025-01-08 13:34:34.011710 Pedals: Mechanic noticed loose threads on right pedal spindle; tightened securely. +70 16 1 2025-01-08 18:06:58.447946 Handlebars: Handlebar alignment corrected; was slightly off-center. +71 12 4 2025-01-06 01:12:12.590045 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +72 6 1 2025-01-07 15:06:05.361839 Frame: Small dent on the top tube identified during inspection. +73 3 2 2025-01-09 08:58:18.849503 Tires: Front tire inflated; slow leak detected and patched. +74 2 4 2025-01-11 23:16:55.910491 Pedals: Left pedal bearings replaced due to grinding noise. +75 13 4 2025-01-13 04:47:30.801282 Pedals: Upgraded pedals to a clipless system; customer very happy. +76 2 1 2025-01-09 13:41:58.636115 Tires: Replaced a worn-out rear tire; customer opted for puncture-resistant model. +77 29 2 2025-01-10 06:53:45.983297 Gears: Lubricated drivetrain; customer noticed quieter pedaling. +78 30 2 2025-01-07 12:32:40.057594 Brakes: Adjusted brake pads for better stopping power. +79 24 4 2025-01-07 15:44:32.816038 Wheels: Replaced a broken spoke on the front wheel. +80 12 3 2025-01-07 02:46:14.069672 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +81 17 2 2025-01-12 10:44:53.000051 Frame: Frame cleaned and polished; customer commented on how shiny it looked. +82 21 1 2025-01-05 12:32:51.917834 Brakes: Adjusted brake pads for better stopping power. +83 10 2 2025-01-09 23:35:26.263723 Frame: Noticed a crack near the bottom bracket; recommended a replacement. +84 3 1 2025-01-02 21:12:29.416106 Tires: Customer appreciated advice on tire pressure for road biking. +85 25 4 2025-01-15 21:36:30.448941 Gears: Shifted gears sticking; replaced derailleur hanger. +86 15 2 2025-01-14 18:45:36.484494 Wheels: Replaced a broken spoke on the front wheel. +87 22 1 2025-01-02 18:26:36.340023 Brakes: Adjusted brake pads for better stopping power. +88 21 1 2025-01-02 03:40:01.158450 Wheels: Replaced a broken spoke on the front wheel. +89 18 3 2025-01-15 18:18:53.681251 Gears: Customer reported skipping gears; adjusted indexing. +90 1 2 2025-01-02 03:17:35.638653 Handlebars: Handlebar alignment corrected; was slightly off-center. +91 24 4 2025-01-03 09:20:37.081151 Tires: Customer appreciated advice on tire pressure for road biking. +92 2 2 2025-01-08 13:35:06.177571 Handlebars: Installed new ergonomic grips; customer was excited about the comfort. +93 6 4 2025-01-06 19:12:43.223897 Frame: Frame cleaned and polished; customer commented on how shiny it looked. +94 28 3 2025-01-08 17:30:58.228861 Tires: Front tire inflated; slow leak detected and patched. +95 17 3 2025-01-09 11:28:29.178429 Brakes: Adjusted brake pads for better stopping power. +96 1 3 2025-01-02 05:15:28.978011 Frame: Noticed a crack near the bottom bracket; recommended a replacement. +97 2 1 2025-01-12 01:59:04.712426 Frame: Noticed a crack near the bottom bracket; recommended a replacement. +98 22 4 2025-01-12 22:50:53.991210 Tires: Front tire inflated; slow leak detected and patched. +99 3 4 2025-01-12 14:45:07.111919 Gears: Shifted gears sticking; replaced derailleur hanger. +100 29 2 2025-01-07 03:32:51.205724 Wheels: Replaced a broken spoke on the front wheel. +\. + diff --git a/beta_use_cases/bike_service_shop/schema.sql b/beta_use_cases/bike_service_shop/schema.sql new file mode 100644 index 0000000..27a9820 --- /dev/null +++ b/beta_use_cases/bike_service_shop/schema.sql @@ -0,0 +1,53 @@ +DROP SCHEMA IF EXISTS "Bike Shop" CASCADE; +CREATE SCHEMA "Bike Shop"; +SET search_path = "Bike Shop"; + +CREATE TABLE "Customers" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name TEXT NOT NULL, + last_name TEXT NOT NULL, + email TEXT, + phone TEXT +); + +CREATE TABLE "Equipment Types" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL +); + +CREATE TABLE "Equipment" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + type_id BIGINT REFERENCES "Equipment Types" (id), + serial_number TEXT NOT NULL UNIQUE, + notes TEXT +); + +CREATE TABLE "Mechanics" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + first_name TEXT NOT NULL, + last_name TEXT NOT NULL +); + +CREATE TABLE "Service Statuses" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL UNIQUE +); + +CREATE TABLE "Service Requests" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + customer_id BIGINT REFERENCES "Customers" (id), + equipment_id BIGINT REFERENCES "Equipment" (id), + mechanic_id BIGINT REFERENCES "Mechanics" (id), + request_description TEXT NOT NULL, + cost NUMERIC(10, 2), + time_in TIMESTAMP, + time_out TIMESTAMP +); + +CREATE TABLE "Service Milestones" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + service_request_id BIGINT REFERENCES "Service Requests" (id), + status_id BIGINT REFERENCES "Service Statuses" (id), + update_time TIMESTAMP DEFAULT NOW(), + notes TEXT +); diff --git a/beta_use_cases/hardware_store/README.md b/beta_use_cases/hardware_store/README.md index 497fdb1..dd8a833 100644 --- a/beta_use_cases/hardware_store/README.md +++ b/beta_use_cases/hardware_store/README.md @@ -59,21 +59,3 @@ erDiagram "Transactions" ||--|{ "Customers" : "customer_id" "Rentals" ||--|| "Transactions" : "transaction_id" ``` - - -## Loading Data - -The generated SQL file, `generate_data/load_data.sql`, contains all the necessary COPY commands to import data into your database. The data (and the load data file) are produced by the `generate_data.py` file, which can be adjusted and re-run to alter the data if needed. - -Load the data into a locally-running Mathesar instance like this: - -```shell -# First load the schema and tables -docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < schema.sql -# Then the sample data -docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < generated_data.sql -``` - -## Development - -The only requirement is to install dependencies with `pip install -r requirements.txt`. diff --git a/beta_use_cases/hardware_store/generate_data.py b/beta_use_cases/hardware_store/generate_data.py index d5a022a..770ed0a 100644 --- a/beta_use_cases/hardware_store/generate_data.py +++ b/beta_use_cases/hardware_store/generate_data.py @@ -1,54 +1,68 @@ import os import random +from datetime import timedelta, datetime from faker import Faker -import faker_commerce -from datetime import timedelta fake = Faker() -fake.add_provider(faker_commerce.Provider) -# Number of rows to generate +# Helper functions +def clean_value(value): + """Clean a value for SQL COPY operations.""" + if value is None: + return r"\N" + if isinstance(value, str): + return value.replace("\t", " ").replace("\n", " ") + return str(value) + +def write_to_sql_file(output_path, search_path, tables): + """Write the generated data to an SQL file.""" + with open(output_path, "w") as f: + f.write(f'SET search_path="{search_path}";\n\n') + for table_name, generator in tables.items(): + f.write(f'COPY "{table_name}" FROM stdin;\n') + for row in generator: + cleaned_row = "\t".join(map(clean_value, row)) + f.write(f"{cleaned_row}\n") + f.write("\\.\n\n") + print(f"SQL file generated: {output_path}") + +def get_output_file_path(filename): + """Get the output file path relative to the current script's directory.""" + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_file_dir, filename) + +# Constants NUM_STORES = 5 NUM_CUSTOMERS = 20 NUM_ASSETS = 50 NUM_TRANSACTIONS = 60 NUM_RENTALS = 30 -# Base product names with allowed descriptors BASE_PRODUCTS = { - "lawn mower": ["size", "feature", "quality"], - "socket set": ["size", "feature", "set_type"], + "air compressor": ["size", "feature", "quality"], "chainsaw": ["size", "feature", "quality"], + "drill": ["size", "feature", "quality"], "hammer": ["quality", "feature"], "hand axe": ["size", "quality"], - "drill": ["size", "feature", "quality"], + "lawn mower": ["size", "feature", "quality"], "leaf blower": ["size", "feature"], - "wrench set": ["size", "set_type", "quality"], - "screwdriver set": ["size", "feature", "set_type"], - "power saw": ["feature", "quality"], "pliers set": ["size", "set_type"], + "power saw": ["feature", "quality"], + "screwdriver set": ["size", "feature", "set_type"], "shovel": ["size", "quality"], + "socket set": ["size", "feature", "set_type"], "wheelbarrow": ["size", "quality"], "workbench": ["size", "feature", "quality"], - "air compressor": ["size", "feature", "quality"], + "wrench set": ["size", "set_type", "quality"], } -# Descriptor categories DESCRIPTORS = { - "size": ["xl", "sm", "compact", "oversized", "portable"], "feature": ["adjustable", "collapsible", "ergonomic", "cordless", "heavy-duty"], "quality": ["deluxe", "professional", "basic", "lightweight", "industrial"], "set_type": ["10pc", "20pc", "5pc", "15pc"], + "size": ["xl", "sm", "compact", "oversized", "portable"], } -# Helper function to clean values for SQL COPY -def clean_value(value): - if value is None: - return r"\N" - if isinstance(value, str): - return value.replace("\t", " ").replace("\n", " ") - return str(value) - # Helper function to generate asset names def generate_asset_name(): base_product = random.choice(list(BASE_PRODUCTS.keys())) @@ -60,7 +74,6 @@ def generate_asset_name(): descriptor = random.choice(DESCRIPTORS[category]) name_parts.insert(0, descriptor) # Add descriptor before the base product name - # Title case the final product name return " ".join(name_parts).title() # Table Data Generation @@ -89,7 +102,7 @@ def generate_assets(store_ids): ) yield [ i, - generate_asset_name(), # Use the custom asset name generator + generate_asset_name(), fake.unique.ean13(), rental_price, sale_price, @@ -115,31 +128,20 @@ def generate_rentals(transaction_ids): rental_time = rental_end - rental_start yield [i, transaction_id, rental_start, rental_end, rental_start, rental_end, rental_time] -# Generate Data -store_ids = list(range(1, NUM_STORES + 1)) -customer_ids = list(range(1, NUM_CUSTOMERS + 1)) -asset_ids = list(range(1, NUM_ASSETS + 1)) -transaction_ids = list(range(1, NUM_TRANSACTIONS + 1)) - -tables = { - "Store Locations": generate_store_locations(), - "Customers": generate_customers(), - "Assets": generate_assets(store_ids), - "Transactions": generate_transactions(asset_ids, customer_ids), - "Rentals": generate_rentals(transaction_ids), -} - -# Write to SQL file -sql_file = os.path.join(os.getcwd(), "generated_data.sql") - -with open(sql_file, "w") as f: - f.write('SET search_path="Hardware Store";\n\n') - - for table_name, generator in tables.items(): - f.write(f'COPY "{table_name}" FROM stdin;\n') - for row in generator: - cleaned_row = "\t".join(map(clean_value, row)) - f.write(f"{cleaned_row}\n") - f.write("\\.\n\n") - -print(f"SQL file generated: {sql_file}") +# Main Script +if __name__ == "__main__": + store_ids = list(range(1, NUM_STORES + 1)) + customer_ids = list(range(1, NUM_CUSTOMERS + 1)) + asset_ids = list(range(1, NUM_ASSETS + 1)) + transaction_ids = list(range(1, NUM_TRANSACTIONS + 1)) + + tables = { + "Store Locations": generate_store_locations(), + "Customers": generate_customers(), + "Assets": generate_assets(store_ids), + "Transactions": generate_transactions(asset_ids, customer_ids), + "Rentals": generate_rentals(transaction_ids), + } + + sql_file = get_output_file_path("generated_data.sql") + write_to_sql_file(sql_file, "Hardware Store", tables) diff --git a/beta_use_cases/hardware_store/generated_data.sql b/beta_use_cases/hardware_store/generated_data.sql index d8766e4..38d1f26 100644 --- a/beta_use_cases/hardware_store/generated_data.sql +++ b/beta_use_cases/hardware_store/generated_data.sql @@ -1,182 +1,182 @@ SET search_path="Hardware Store"; COPY "Store Locations" FROM stdin; -1 Trevino Group 71605 Andrews Parks Suite 687 Brandonstad, PA 99796 -2 Brewer Inc 02266 Cherry Locks Suite 010 Bartonport, MT 94578 -3 Sanders-King 6995 Petty Wall Lake Tiffany, IL 62558 -4 Mercado, Rogers and Brown 225 Day Junction West Breannaberg, PA 88210 -5 Price-Nichols 96728 Katie Heights West Brian, MD 51538 +1 Hurley-Solis 536 Harris Lodge Suite 008 North Thomaston, NH 71798 +2 Thompson, Patel and Fernandez 89803 Huerta Fields Apt. 469 Marytown, NV 46853 +3 Kramer Group 92494 Adam Prairie Suite 859 North Lisaport, MT 75809 +4 Garcia, Colon and Greene 489 Gill Run Suite 432 Campbellside, AR 57973 +5 Black, Evans and Larson 679 Bradley Vista Darlenemouth, ID 66594 \. COPY "Customers" FROM stdin; -1 Ashley Ward scott22@example.net +1-459-556-9681x0513 6063 Gregory Hills Suite 535 Acostastad, FM 71631 -2 Dustin Caldwell cortezkaren@example.com 001-834-270-6744x842 0007 Amber Common East Corey, MA 59143 -3 Gabrielle Baker sarah33@example.com 001-990-383-2710x67743 06813 Allen Motorway Suite 843 New Alicia, OR 46076 -4 Erik Peterson cohenchad@example.net (366)572-8254x71350 7671 Katherine Forge Frankshire, MD 07599 -5 Kevin Evans jasonbarnett@example.com (466)305-9881 46125 Jeffrey Gateway Suite 565 West Thomasbury, ID 24569 -6 John Webb johnsonlori@example.org (737)710-3216x46641 1940 King Unions Suite 415 Whiteland, TX 68939 -7 Peter Merritt edward25@example.org 8617950872 45850 Wilkinson Rue Lake Anthonyfort, MP 89881 -8 Brandon Rodriguez michaelaadams@example.com (298)949-0999 3974 Miller Loaf Gilbertfurt, MN 17810 -9 Rebecca Curry shelbyjackson@example.com 8516621372 PSC 6178, Box 0751 APO AE 21593 -10 Jeffrey Johnson isaiahmeza@example.com 372-686-6707x4126 72132 Victoria Rapids Port Diana, IL 70209 -11 Darren Estrada alexandra25@example.com 001-518-586-9532 3318 Wilkins Fall Lake Karen, MS 75639 -12 Eric Welch holmesaimee@example.org 798.666.4566x616 4608 Smith Canyon Suite 432 East Eugenetown, ND 72115 -13 Katie Baker danielgarner@example.net (986)514-0418x125 5679 Edwards Streets Apt. 911 Boydchester, OH 22398 -14 Brandon Clark moralesderek@example.org +1-663-778-2532x421 79055 Fletcher Path Robertfurt, NY 15838 -15 William Townsend rbailey@example.org 506.776.8121x3772 4322 Michael Streets Apt. 870 Ortizfurt, FL 42290 -16 Alex Brooks snydertony@example.com 4203760327 PSC 6413, Box 0489 APO AP 10741 -17 Katie Jacobs rebeccathompson@example.org 682.881.8241x67063 3605 Patrick Springs New Zacharyville, VA 94333 -18 Michelle Allen omiller@example.com 001-887-617-1236x0552 3636 Davis Lakes Apt. 702 East Debraport, MN 85425 -19 Theresa Bradley robert79@example.com 359-723-0264 269 Audrey Spur Apt. 224 Linshire, CA 04630 -20 Brandi Morris kayladaniels@example.org 384-405-6200x75369 29153 Jack Extension Graveschester, OK 68775 +1 Thomas Jackson jgonzalez@example.org 362-837-5867x2899 609 White Viaduct South Cynthiaborough, MD 83104 +2 Jennifer Johnson randyherman@example.net 728-444-8558 06541 Jonathan Cape Sullivanburgh, MD 63946 +3 Ashley Love mark13@example.com 901.428.0252x586 648 Thomas Haven Suite 578 East Kenneth, MA 39887 +4 Whitney Smith woodsregina@example.com +1-695-532-4668x89672 0102 Brock Mission Donnastad, VA 97687 +5 Jeremiah Martin suarezryan@example.net +1-217-943-7072 7510 Hernandez Center Suite 573 Barnesberg, UT 89733 +6 Kayla Johnson alexis63@example.net 7519843054 092 Stanley Throughway Tinafort, TX 99119 +7 Joy May anita14@example.com (927)920-0205 891 Laura Islands East Ariel, NV 94940 +8 Jennifer Acosta joshua33@example.net 001-214-901-0253 522 Li Rue West Colinchester, GA 42286 +9 Katrina Lee chelsea00@example.com (360)257-2698x51410 4981 Robert Circle Apt. 498 Knightport, MD 20332 +10 William Mendez johnjones@example.net 235-779-7223 78543 Crosby Walk Deborahland, FL 63430 +11 James Ryan natasha70@example.com 389.685.3529 93495 James Mount Annaside, CA 82169 +12 Gloria Li ihernandez@example.org 336.927.6257 36963 Wu Heights Port Anabury, TX 44715 +13 Kim Beltran marshaaron@example.com 529-681-7888x38684 7475 Logan Crest Apt. 371 Monicachester, FL 67857 +14 Cassandra Burns margaretweiss@example.com (470)918-6426 1277 Burton Track Thompsonborough, NY 59472 +15 Andrea Solomon katiemack@example.com +1-810-775-7845x530 625 Becky Skyway Suite 833 Stacyview, PW 12442 +16 James Jones wayne73@example.net 001-745-443-1959 772 Davis Branch West Kellyfurt, NV 36159 +17 Lisa Dixon christina78@example.org 3799989111 384 Taylor Glens Apt. 082 Mooreport, RI 83558 +18 Matthew Adams collinsamy@example.com 231-387-3903x91236 8840 Ryan Manors Apt. 036 New Jeremyview, MP 35584 +19 Nicole Price halljimmy@example.net 001-519-235-3715x80381 2202 Christine Stravenue Pettyborough, AS 95530 +20 Lori Marsh perezjonathan@example.net (675)596-2593x8823 766 Hanson Junction Apt. 926 Juanborough, ME 30242 \. COPY "Assets" FROM stdin; -1 Compact Workbench 4764648751354 64.9 \N weekly Aisle 17 - Shelf 5 2 -2 5Pc Collapsible Xl Socket Set 0693950909884 86.98 \N monthly Aisle 12 - Shelf 3 5 -3 Hammer 0359539144960 44.24 \N weekly Aisle 17 - Shelf 5 4 -4 Compact Workbench 2147208530446 37.28 \N weekly Aisle 9 - Shelf 6 4 -5 Basic 10Pc Wrench Set 5963850343947 47.81 \N monthly Aisle 14 - Shelf 1 4 -6 Professional Collapsible Portable Workbench 3997985927064 81.04 \N weekly Aisle 13 - Shelf 7 4 -7 Lightweight Xl Wheelbarrow 4394073379078 45.9 \N monthly Aisle 5 - Shelf 3 1 -8 10Pc Cordless Socket Set 8701477472612 37.52 \N weekly Aisle 11 - Shelf 8 4 -9 Lightweight Xl Wheelbarrow 9807094026623 90.55 \N daily Aisle 20 - Shelf 10 5 -10 Lightweight 5Pc Xl Wrench Set 4058705276493 85.36 \N daily Aisle 17 - Shelf 9 2 -11 Lightweight Ergonomic Power Saw 1915300091919 22.79 \N monthly Aisle 7 - Shelf 1 5 -12 Industrial 5Pc Oversized Wrench Set 1072012338524 10.59 5.97 weekly Aisle 8 - Shelf 9 3 -13 Collapsible Xl Leaf Blower 9823535206072 31.38 19.15 daily Aisle 13 - Shelf 4 5 -14 Sm Lawn Mower 1611063723578 58.26 \N weekly Aisle 8 - Shelf 1 2 -15 Portable Wheelbarrow 2039239952000 67.73 \N daily Aisle 19 - Shelf 10 5 -16 Industrial Hammer 9939312769798 92.19 \N daily Aisle 14 - Shelf 9 5 -17 Compact Leaf Blower 2745014889828 95.48 \N weekly Aisle 2 - Shelf 6 5 -18 Professional Portable Hand Axe 7452365900441 70.37 \N monthly Aisle 3 - Shelf 4 2 -19 Adjustable Portable Leaf Blower 8006841347245 74.22 \N monthly Aisle 20 - Shelf 1 5 -20 Adjustable Sm Screwdriver Set 3923792733420 88.32 \N weekly Aisle 20 - Shelf 3 2 -21 20Pc Wrench Set 2846183087104 37.97 \N monthly Aisle 2 - Shelf 10 2 -22 Basic Shovel 7233252203780 27.21 \N monthly Aisle 13 - Shelf 4 3 -23 Industrial Cordless Portable Workbench 4982498374862 66.43 \N monthly Aisle 20 - Shelf 10 5 -24 Air Compressor 7640743027402 10.74 6.64 weekly Aisle 10 - Shelf 6 2 -25 Professional Compact Shovel 8295562014684 38.39 \N daily Aisle 4 - Shelf 5 5 -26 Adjustable Air Compressor 3644877144800 7.95 \N monthly Aisle 20 - Shelf 2 3 -27 Industrial Heavy-Duty Power Saw 9805106083701 56.45 \N daily Aisle 15 - Shelf 2 5 -28 20Pc Ergonomic Portable Socket Set 6075017960634 30.33 \N daily Aisle 3 - Shelf 7 4 -29 Basic Portable Hand Axe 5075274507710 10.12 7.35 monthly Aisle 11 - Shelf 3 3 -30 Lightweight Wrench Set 2081585744990 31.79 \N monthly Aisle 4 - Shelf 4 4 -31 Basic Sm Chainsaw 6841447246224 43.26 \N daily Aisle 20 - Shelf 9 3 -32 Basic 10Pc Compact Wrench Set 5922008160713 74.25 \N weekly Aisle 12 - Shelf 6 5 -33 15Pc Pliers Set 2647656594217 50.93 \N weekly Aisle 7 - Shelf 8 5 -34 Deluxe 10Pc Wrench Set 1236030580468 68.57 \N monthly Aisle 3 - Shelf 7 5 -35 Leaf Blower 3429746782594 95.79 \N monthly Aisle 2 - Shelf 4 2 -36 Heavy-Duty Hammer 5708790566632 87.33 57.81 daily Aisle 20 - Shelf 3 5 -37 Lightweight Compact Hand Axe 1618822852180 47.96 34.07 weekly Aisle 3 - Shelf 10 1 -38 Xl Shovel 6895488918480 97.63 \N monthly Aisle 17 - Shelf 7 4 -39 Deluxe Hammer 0580594807181 42.42 \N weekly Aisle 19 - Shelf 10 3 -40 Cordless Oversized Chainsaw 8523528320723 88.6 \N monthly Aisle 10 - Shelf 9 4 -41 Professional 5Pc Portable Wrench Set 6464548168952 46.02 \N daily Aisle 16 - Shelf 3 4 -42 5Pc Pliers Set 4989176347521 57.23 \N weekly Aisle 20 - Shelf 4 5 -43 15Pc Cordless Compact Socket Set 3889867276080 39.76 \N monthly Aisle 14 - Shelf 6 4 -44 Shovel 6583295690841 65.29 \N monthly Aisle 18 - Shelf 1 4 -45 Deluxe Compact Wheelbarrow 9141358850495 67.18 \N monthly Aisle 6 - Shelf 9 4 -46 Lightweight Cordless Sm Lawn Mower 8010330382884 60.49 \N weekly Aisle 5 - Shelf 3 5 -47 Compact Lawn Mower 5067919914588 53.57 \N weekly Aisle 4 - Shelf 4 3 -48 20Pc Compact Socket Set 7456728595190 38.8 \N daily Aisle 4 - Shelf 2 3 -49 15Pc Xl Wrench Set 0285394729090 31.91 \N weekly Aisle 2 - Shelf 10 3 -50 Basic Oversized Wrench Set 1671457610006 47.13 \N monthly Aisle 19 - Shelf 5 1 +1 Cordless Sm Leaf Blower 0574437137986 31.34 \N monthly Aisle 18 - Shelf 3 3 +2 Lightweight Ergonomic Xl Drill 6488355187432 17.86 \N weekly Aisle 7 - Shelf 9 1 +3 Heavy-Duty Leaf Blower 2663332158740 53.26 \N monthly Aisle 2 - Shelf 5 2 +4 10Pc Adjustable Oversized Socket Set 1059198945593 71.26 52.76 monthly Aisle 17 - Shelf 2 5 +5 Deluxe Xl Wheelbarrow 1317106696587 18.72 \N daily Aisle 10 - Shelf 9 2 +6 Industrial Collapsible Oversized Drill 8963774291192 44.87 \N monthly Aisle 14 - Shelf 3 2 +7 Industrial Collapsible Air Compressor 7560418869840 15.03 \N monthly Aisle 3 - Shelf 4 3 +8 Industrial Compact Hand Axe 1217013427888 59.52 \N weekly Aisle 1 - Shelf 6 5 +9 Sm Leaf Blower 6130913686481 12.56 \N monthly Aisle 9 - Shelf 6 3 +10 Professional Cordless Power Saw 6416306814335 57.82 \N monthly Aisle 7 - Shelf 10 2 +11 Compact Pliers Set 4777970730638 80.93 \N monthly Aisle 6 - Shelf 10 4 +12 Lightweight Sm Workbench 8153516242807 34.69 \N monthly Aisle 10 - Shelf 4 1 +13 Basic Oversized Chainsaw 2918456724631 59.68 \N daily Aisle 3 - Shelf 9 5 +14 Professional Oversized Drill 7260544423892 9.53 5.0 monthly Aisle 14 - Shelf 5 3 +15 Workbench 7172419939233 12.36 \N weekly Aisle 4 - Shelf 5 3 +16 Industrial 5Pc Oversized Wrench Set 8565854998207 44.74 \N monthly Aisle 17 - Shelf 7 3 +17 10Pc Ergonomic Portable Screwdriver Set 2941284297916 16.55 10.27 weekly Aisle 15 - Shelf 6 5 +18 Lightweight Heavy-Duty Xl Lawn Mower 5995520241918 92.19 65.69 daily Aisle 1 - Shelf 5 5 +19 5Pc Cordless Oversized Socket Set 2460274532054 17.18 \N weekly Aisle 10 - Shelf 3 5 +20 Industrial Cordless Lawn Mower 3146538540308 82.13 \N monthly Aisle 15 - Shelf 10 2 +21 Basic Collapsible Sm Chainsaw 3582832945898 30.91 \N weekly Aisle 2 - Shelf 10 4 +22 Adjustable Power Saw 8850535651930 87.84 65.62 monthly Aisle 13 - Shelf 5 2 +23 Deluxe Adjustable Oversized Workbench 9227790843063 91.04 \N monthly Aisle 8 - Shelf 1 4 +24 20Pc Sm Pliers Set 4307793540154 34.12 \N weekly Aisle 10 - Shelf 8 1 +25 Basic Cordless Portable Air Compressor 7129179035668 72.9 \N weekly Aisle 7 - Shelf 10 4 +26 Heavy-Duty Xl Leaf Blower 0919413045174 55.96 \N monthly Aisle 7 - Shelf 10 3 +27 Industrial Chainsaw 0517149954441 8.25 \N weekly Aisle 15 - Shelf 2 4 +28 Deluxe Hand Axe 9291143638118 88.23 \N monthly Aisle 1 - Shelf 5 3 +29 10Pc Pliers Set 6050005970017 36.41 \N weekly Aisle 12 - Shelf 9 4 +30 Socket Set 6903549366955 55.35 \N weekly Aisle 3 - Shelf 3 3 +31 Industrial Sm Hand Axe 8709844754552 73.38 \N weekly Aisle 12 - Shelf 5 2 +32 Xl Socket Set 7223302823778 75.44 40.27 monthly Aisle 14 - Shelf 2 4 +33 Adjustable Industrial Hammer 8447912337305 73.32 \N weekly Aisle 8 - Shelf 4 5 +34 Cordless Professional Hammer 1025389301721 16.21 10.64 monthly Aisle 7 - Shelf 6 3 +35 Professional Ergonomic Power Saw 5449975865316 43.97 \N weekly Aisle 8 - Shelf 7 2 +36 Portable Leaf Blower 7904884317946 64.91 \N monthly Aisle 11 - Shelf 3 1 +37 20Pc Heavy-Duty Sm Screwdriver Set 9953725511294 73.74 48.2 monthly Aisle 8 - Shelf 2 5 +38 Sm Screwdriver Set 2549934771405 74.81 59.76 monthly Aisle 4 - Shelf 7 5 +39 Professional Hammer 2000904792320 62.4 \N monthly Aisle 11 - Shelf 1 4 +40 Screwdriver Set 4061449507921 42.97 \N monthly Aisle 2 - Shelf 8 2 +41 Heavy-Duty Leaf Blower 5113291161376 74.27 48.15 weekly Aisle 11 - Shelf 8 5 +42 Basic Adjustable Compact Lawn Mower 6686523447108 76.4 \N weekly Aisle 15 - Shelf 1 3 +43 10Pc Screwdriver Set 4785884903321 82.8 \N daily Aisle 5 - Shelf 7 4 +44 Professional Collapsible Compact Air Compressor 4037593164037 76.56 43.75 weekly Aisle 18 - Shelf 3 1 +45 5Pc Ergonomic Screwdriver Set 6810216720814 25.92 17.52 weekly Aisle 13 - Shelf 7 2 +46 Collapsible Chainsaw 6225496600051 69.57 \N weekly Aisle 8 - Shelf 4 4 +47 Professional Oversized Wrench Set 3136269884192 27.04 15.85 daily Aisle 13 - Shelf 4 1 +48 Deluxe Wrench Set 8854452317191 48.77 \N monthly Aisle 6 - Shelf 9 1 +49 Collapsible Portable Leaf Blower 6342074974271 63.71 \N monthly Aisle 16 - Shelf 9 4 +50 Basic Adjustable Compact Workbench 4960648616000 73.89 \N monthly Aisle 10 - Shelf 5 5 \. COPY "Transactions" FROM stdin; -1 38 14 Return 2025-01-11 10:29:38.641904 154.19 Television child stay simple as. -2 10 12 Rental 2025-01-14 17:53:15.142980 211.07 Race themselves them yet unit. -3 26 8 Rental 2025-01-01 15:17:08.017574 358.34 Little across moment allow early will sometimes. -4 34 11 Rental 2025-01-13 17:05:16.510237 275.76 Each full their clearly find marriage site spend. -5 14 12 Return 2025-01-07 05:20:41.305944 494.71 Might interesting scene opportunity. -6 30 10 Rental 2025-01-16 13:23:03.240775 450.3 Wide reason news high dream. -7 3 8 Rental 2025-01-14 00:46:20.580798 109.75 Product glass front candidate. -8 15 4 Return 2025-01-13 18:19:17.317374 26.44 Wrong fight deal home fish moment. -9 23 14 Return 2025-01-01 21:22:38.067740 61.49 Well bag blood performance somebody. -10 26 2 Rental 2025-01-09 13:12:06.210819 149.81 Inside begin stage message it art. -11 20 8 Return 2025-01-01 09:31:42.965164 403.28 Chance born interest couple project everybody treat. -12 50 20 Return 2025-01-15 18:42:29.604983 28.23 Change produce wall energy list public. -13 46 14 Rental 2025-01-01 06:17:38.359206 179.14 Three member collection forward young today nice. -14 32 20 Sale 2025-01-13 03:12:23.368580 176.83 Someone floor pay prevent paper Democrat. -15 30 9 Rental 2025-01-06 16:37:09.982674 168.57 Onto stop lay crime. -16 36 7 Sale 2025-01-11 15:13:15.639642 237.1 Compare official power positive loss. -17 43 13 Sale 2025-01-09 05:06:34.563619 380.9 Read receive without eight similar unit. -18 32 2 Sale 2025-01-10 17:38:13.758519 98.54 Within today over hundred. -19 37 8 Sale 2025-01-03 15:46:32.724208 313.6 Everyone stuff give when. -20 14 16 Sale 2025-01-13 21:03:17.200600 139.59 Address sign another simple book. -21 48 1 Return 2025-01-14 17:49:50.493488 299.81 Together voice too city day. -22 39 7 Sale 2025-01-06 14:40:28.259804 91.73 Politics degree improve day. -23 23 9 Rental 2025-01-14 09:26:19.755088 224.17 Last much great radio security. -24 31 13 Rental 2025-01-16 10:31:18.716804 133.42 Wife color issue bill. -25 43 4 Rental 2025-01-13 01:41:14.413522 272.9 Truth paper certainly. -26 19 1 Rental 2025-01-02 13:02:11.629915 413.65 Sometimes guy lot account bar total expect major. -27 4 12 Return 2025-01-15 22:53:35.743362 358.93 Direction red team seven cost sing five. -28 42 18 Sale 2025-01-11 15:18:31.178994 234.63 Stuff less activity same. -29 19 17 Sale 2025-01-13 08:20:10.697422 431.55 Realize old enough to never federal game. -30 33 20 Return 2025-01-06 10:30:03.636599 282.16 Wide night voice expect. -31 24 5 Rental 2025-01-13 09:44:07.467851 133.0 Level think popular note debate. -32 42 18 Sale 2025-01-04 17:32:17.971929 252.81 Ready what outside evening and capital. -33 17 16 Rental 2025-01-13 20:22:36.720834 141.13 Field have total section issue doctor people. -34 42 7 Sale 2025-01-13 03:22:20.801136 228.12 True help manage want season authority might. -35 16 5 Sale 2025-01-10 19:39:03.496366 52.52 American century full actually international commercial bag of. -36 49 1 Return 2025-01-11 01:19:48.236142 277.69 Course act become recent money to. -37 13 2 Sale 2025-01-10 10:09:21.684744 323.22 Politics truth now no last rather thing during. -38 36 6 Sale 2025-01-10 02:11:47.237427 490.87 Movie senior itself dark fly show. -39 23 15 Sale 2025-01-12 04:29:12.383708 276.7 Development face service list son per system. -40 8 6 Rental 2025-01-01 06:16:15.704939 205.21 Enjoy real hold likely suffer drive language. -41 21 1 Sale 2025-01-02 06:22:09.363593 342.24 Continue upon rise word rock name per husband. -42 4 11 Sale 2025-01-03 17:31:41.267555 13.13 Stop show community white not edge heavy. -43 23 17 Rental 2025-01-07 03:14:32.829909 487.24 Culture third action available century whatever. -44 47 12 Rental 2025-01-13 07:17:37.270696 180.43 South course clearly gas mother fill else style. -45 43 17 Rental 2025-01-04 13:33:33.843472 225.13 Respond clear respond identify. -46 50 1 Rental 2025-01-16 03:05:44.428561 361.25 Tell paper whether health finish win. -47 25 20 Return 2025-01-15 03:50:08.729208 76.95 Under production hundred enough note government. -48 29 13 Return 2025-01-11 16:32:45.006137 440.54 Structure at wide fine out another. -49 38 13 Sale 2025-01-16 11:12:28.585670 417.98 Human include admit indicate tax. -50 3 6 Return 2025-01-15 11:46:31.087954 258.48 That one order key reflect quite. -51 22 4 Sale 2025-01-09 02:56:13.875788 384.42 By discussion order resource small nothing. -52 29 16 Return 2025-01-09 10:55:03.673645 286.64 Government wide toward memory know management school. -53 11 2 Sale 2025-01-01 03:41:03.524182 368.28 Cell morning job. -54 38 19 Rental 2025-01-05 18:34:55.128522 386.51 Call campaign condition cell seat. -55 40 9 Rental 2025-01-15 22:35:10.772769 456.73 Guy anyone act same guess. -56 41 5 Return 2025-01-05 19:22:37.757410 11.74 Price language test image. -57 3 13 Rental 2025-01-04 10:30:15.977328 246.17 Field report your onto Mr local. -58 30 18 Return 2025-01-07 23:47:57.472533 248.4 Mean against write yourself understand family field. -59 30 14 Sale 2025-01-14 17:56:34.286029 207.07 Might interview whether cup fight employee. -60 24 4 Return 2025-01-01 23:55:47.053550 125.21 Kind thing employee. +1 44 6 Return 2025-01-14 01:12:15.717130 372.56 Hour voice physical child skin idea. +2 11 15 Sale 2025-01-10 16:36:17.374514 481.98 Machine group decide throughout exist weight. +3 10 2 Rental 2025-01-05 14:03:26.880986 61.41 Our house none author. +4 23 17 Sale 2025-01-02 20:53:55.237754 255.96 Shake arrive way reach difficult interesting. +5 34 19 Return 2025-01-09 14:50:12.946386 361.52 Per couple east PM. +6 19 17 Return 2025-01-05 19:59:45.238171 496.21 Forward eight political unit color. +7 2 19 Return 2025-01-03 07:41:02.759297 466.65 Actually court camera my. +8 27 18 Return 2025-01-05 11:16:01.558993 192.4 Book stock possible and city since only. +9 29 4 Return 2025-01-12 12:58:30.174510 284.69 Simply wish grow these better fly individual court. +10 36 8 Sale 2025-01-12 06:34:26.184055 145.83 Action send teach section prevent. +11 38 9 Sale 2025-01-06 09:04:29.809872 297.73 Decision reason light international. +12 14 12 Return 2025-01-05 16:55:11.441694 418.45 Hour deep over teacher how. +13 3 13 Sale 2025-01-15 15:09:08.714173 415.33 Pattern pick unit bad budget picture subject. +14 36 12 Sale 2025-01-15 17:46:17.720273 34.54 Even foot culture simple lay. +15 2 20 Rental 2025-01-07 19:01:11.503356 107.09 American environmental challenge both actually small network. +16 39 20 Rental 2025-01-07 15:19:43.028895 353.71 Deal response blood recognize wife. +17 16 19 Return 2025-01-13 12:30:20.008234 248.42 Address animal quite resource artist street. +18 15 16 Return 2025-01-10 11:58:24.509332 204.97 Network middle game. +19 34 16 Rental 2025-01-05 05:33:20.805026 104.96 Live late long will. +20 30 11 Return 2025-01-08 20:57:14.361743 49.3 Discover avoid focus throughout. +21 36 3 Rental 2025-01-04 21:05:46.269181 391.02 Right five heavy per which month list author. +22 12 6 Rental 2025-01-07 21:03:25.304081 235.41 Reduce base order despite. +23 18 17 Return 2025-01-02 04:31:17.728158 463.37 Few lawyer artist miss. +24 36 13 Return 2025-01-12 05:42:42.700571 94.84 Drive floor hard majority. +25 7 15 Sale 2025-01-08 19:20:13.032610 86.63 Minute marriage message. +26 38 8 Return 2025-01-11 02:02:47.264664 489.45 During important black term. +27 27 8 Return 2025-01-03 20:59:37.959371 207.93 Poor actually career protect better. +28 29 13 Rental 2025-01-16 01:13:51.361029 497.01 Laugh ok free center. +29 33 10 Return 2025-01-16 12:51:15.013598 228.4 Smile choose statement area. +30 25 10 Rental 2025-01-01 19:53:13.532603 160.27 Recent while game deep visit nor. +31 2 7 Rental 2025-01-10 15:16:00.754944 14.21 Good memory lot fast project. +32 36 3 Return 2025-01-12 07:17:53.331670 395.63 Baby go film major approach bed determine. +33 41 13 Sale 2025-01-12 04:48:31.866474 344.47 Good investment seem remember baby. +34 5 13 Rental 2025-01-13 15:54:05.788319 403.56 Determine to force degree paper technology. +35 26 11 Rental 2025-01-09 06:33:14.537233 182.81 Should action few anyone believe not. +36 36 20 Rental 2025-01-16 01:34:35.357456 465.57 Manage minute red continue consider image air. +37 48 4 Rental 2025-01-12 22:37:28.432510 127.22 Purpose few box. +38 2 18 Rental 2025-01-09 17:57:35.583112 128.78 Force along health message popular great. +39 40 8 Sale 2025-01-12 18:12:45.966812 218.51 Whose much see notice certainly. +40 39 18 Return 2025-01-05 02:48:30.732469 139.36 Imagine deep design claim institution. +41 28 19 Sale 2025-01-03 07:31:01.414544 193.07 Yourself five address arm character. +42 15 16 Return 2025-01-06 08:54:28.655614 313.55 Side sure likely interview north follow unit. +43 5 20 Sale 2025-01-01 19:06:02.820357 69.45 Marriage work here form particular. +44 15 10 Sale 2025-01-09 08:57:47.577757 299.72 Per news party themselves magazine. +45 30 8 Sale 2025-01-14 22:28:57.191851 154.51 Charge performance somebody much religious these war. +46 40 13 Return 2025-01-04 02:08:05.297604 63.93 How paper here serious wrong media safe let. +47 11 11 Sale 2025-01-01 11:27:05.672644 94.34 Blood throughout huge authority cover one edge. +48 10 4 Return 2025-01-12 10:23:06.752172 12.07 Themselves near image truth knowledge let draw. +49 13 16 Sale 2025-01-09 04:08:23.584846 296.43 Beyond vote drug north kitchen knowledge create. +50 16 9 Rental 2025-01-03 14:40:26.249933 63.62 Gun war by. +51 2 6 Sale 2025-01-08 10:01:02.917752 378.66 Reduce strong medical national organization. +52 31 3 Return 2025-01-15 11:36:43.645435 346.99 Full reason four sometimes option kind. +53 29 11 Rental 2025-01-10 02:16:43.360853 371.81 Firm trip here once response gun future. +54 4 2 Return 2025-01-14 14:25:19.336760 205.94 Impact without name process easy hand science head. +55 1 17 Rental 2025-01-09 17:18:50.763310 426.62 Ok up treat rock window generation speech. +56 23 5 Rental 2025-01-15 21:26:55.649941 119.73 Maybe war together school. +57 25 16 Rental 2025-01-11 12:42:07.584720 356.6 Follow public would else bag himself value. +58 34 7 Sale 2025-01-06 10:26:49.227023 408.63 Three miss leader. +59 2 12 Return 2025-01-11 12:27:05.764802 485.43 Board even community onto help him. +60 8 17 Sale 2025-01-11 00:08:08.258023 175.51 Cold current moment level road. \. COPY "Rentals" FROM stdin; -1 51 2025-01-08 02:57:11.052869 2025-01-11 03:19:30.184497 2025-01-08 02:57:11.052869 2025-01-11 03:19:30.184497 3 days, 0:22:19.131628 -2 57 2025-01-08 15:06:13.708507 2025-01-08 21:44:09.995479 2025-01-08 15:06:13.708507 2025-01-08 21:44:09.995479 6:37:56.286972 -3 35 2025-01-08 09:18:24.179434 2025-01-13 13:17:41.526040 2025-01-08 09:18:24.179434 2025-01-13 13:17:41.526040 5 days, 3:59:17.346606 -4 17 2025-01-04 07:11:17.644689 2025-01-10 23:58:41.902182 2025-01-04 07:11:17.644689 2025-01-10 23:58:41.902182 6 days, 16:47:24.257493 -5 17 2025-01-13 07:58:47.902249 2025-01-15 07:23:17.796954 2025-01-13 07:58:47.902249 2025-01-15 07:23:17.796954 1 day, 23:24:29.894705 -6 47 2025-01-13 23:48:52.076232 2025-01-15 19:24:36.521039 2025-01-13 23:48:52.076232 2025-01-15 19:24:36.521039 1 day, 19:35:44.444807 -7 58 2025-01-07 10:09:06.309845 2025-01-15 23:46:52.861104 2025-01-07 10:09:06.309845 2025-01-15 23:46:52.861104 8 days, 13:37:46.551259 -8 42 2025-01-15 12:17:34.657850 2025-01-16 10:26:39.580255 2025-01-15 12:17:34.657850 2025-01-16 10:26:39.580255 22:09:04.922405 -9 10 2025-01-03 16:32:06.042506 2025-01-14 02:11:36.444795 2025-01-03 16:32:06.042506 2025-01-14 02:11:36.444795 10 days, 9:39:30.402289 -10 5 2025-01-14 13:02:14.863178 2025-01-15 14:08:51.841079 2025-01-14 13:02:14.863178 2025-01-15 14:08:51.841079 1 day, 1:06:36.977901 -11 17 2025-01-14 09:45:56.160443 2025-01-14 23:51:20.247861 2025-01-14 09:45:56.160443 2025-01-14 23:51:20.247861 14:05:24.087418 -12 44 2025-01-10 19:48:13.045856 2025-01-13 07:00:49.559386 2025-01-10 19:48:13.045856 2025-01-13 07:00:49.559386 2 days, 11:12:36.513530 -13 15 2025-01-06 22:32:25.584957 2025-01-11 22:02:14.829104 2025-01-06 22:32:25.584957 2025-01-11 22:02:14.829104 4 days, 23:29:49.244147 -14 9 2025-01-12 22:22:03.437372 2025-01-15 09:36:36.880715 2025-01-12 22:22:03.437372 2025-01-15 09:36:36.880715 2 days, 11:14:33.443343 -15 56 2025-01-13 07:35:08.545202 2025-01-14 17:00:47.409960 2025-01-13 07:35:08.545202 2025-01-14 17:00:47.409960 1 day, 9:25:38.864758 -16 18 2025-01-06 19:33:30.641766 2025-01-07 05:03:26.074224 2025-01-06 19:33:30.641766 2025-01-07 05:03:26.074224 9:29:55.432458 -17 13 2025-01-05 08:13:36.901505 2025-01-14 15:52:12.278558 2025-01-05 08:13:36.901505 2025-01-14 15:52:12.278558 9 days, 7:38:35.377053 -18 13 2025-01-12 04:55:23.526789 2025-01-13 18:32:45.552281 2025-01-12 04:55:23.526789 2025-01-13 18:32:45.552281 1 day, 13:37:22.025492 -19 53 2025-01-12 02:52:10.584372 2025-01-15 03:03:33.009250 2025-01-12 02:52:10.584372 2025-01-15 03:03:33.009250 3 days, 0:11:22.424878 -20 19 2025-01-10 10:32:34.600275 2025-01-12 06:23:39.150088 2025-01-10 10:32:34.600275 2025-01-12 06:23:39.150088 1 day, 19:51:04.549813 -21 35 2025-01-10 12:16:54.921721 2025-01-13 18:37:00.603293 2025-01-10 12:16:54.921721 2025-01-13 18:37:00.603293 3 days, 6:20:05.681572 -22 51 2025-01-12 12:28:30.250854 2025-01-16 06:57:51.286906 2025-01-12 12:28:30.250854 2025-01-16 06:57:51.286906 3 days, 18:29:21.036052 -23 59 2025-01-10 20:23:45.440837 2025-01-16 11:17:17.055649 2025-01-10 20:23:45.440837 2025-01-16 11:17:17.055649 5 days, 14:53:31.614812 -24 55 2025-01-12 01:28:58.815454 2025-01-13 17:46:31.143322 2025-01-12 01:28:58.815454 2025-01-13 17:46:31.143322 1 day, 16:17:32.327868 -25 29 2025-01-07 12:30:10.076787 2025-01-08 10:42:50.812055 2025-01-07 12:30:10.076787 2025-01-08 10:42:50.812055 22:12:40.735268 -26 12 2025-01-05 11:17:33.459758 2025-01-09 02:03:13.046710 2025-01-05 11:17:33.459758 2025-01-09 02:03:13.046710 3 days, 14:45:39.586952 -27 3 2025-01-06 10:46:21.723989 2025-01-08 00:47:39.232241 2025-01-06 10:46:21.723989 2025-01-08 00:47:39.232241 1 day, 14:01:17.508252 -28 36 2025-01-16 12:58:22.985702 2025-01-16 13:13:28.307940 2025-01-16 12:58:22.985702 2025-01-16 13:13:28.307940 0:15:05.322238 -29 49 2025-01-13 15:57:26.112898 2025-01-16 04:22:54.553319 2025-01-13 15:57:26.112898 2025-01-16 04:22:54.553319 2 days, 12:25:28.440421 -30 47 2025-01-15 08:55:55.139504 2025-01-15 11:18:10.383965 2025-01-15 08:55:55.139504 2025-01-15 11:18:10.383965 2:22:15.244461 +1 1 2025-01-14 04:26:26.328177 2025-01-15 22:58:33.557403 2025-01-14 04:26:26.328177 2025-01-15 22:58:33.557403 1 day, 18:32:07.229226 +2 7 2025-01-11 14:56:41.332181 2025-01-16 09:30:04.717844 2025-01-11 14:56:41.332181 2025-01-16 09:30:04.717844 4 days, 18:33:23.385663 +3 42 2025-01-11 09:20:58.716115 2025-01-15 19:13:41.348131 2025-01-11 09:20:58.716115 2025-01-15 19:13:41.348131 4 days, 9:52:42.632016 +4 25 2025-01-11 00:40:10.462173 2025-01-11 10:49:54.928587 2025-01-11 00:40:10.462173 2025-01-11 10:49:54.928587 10:09:44.466414 +5 57 2025-01-08 05:21:36.066744 2025-01-15 16:22:16.706849 2025-01-08 05:21:36.066744 2025-01-15 16:22:16.706849 7 days, 11:00:40.640105 +6 44 2025-01-09 12:08:15.365088 2025-01-11 11:13:53.172388 2025-01-09 12:08:15.365088 2025-01-11 11:13:53.172388 1 day, 23:05:37.807300 +7 33 2025-01-14 17:19:55.122743 2025-01-16 02:04:22.282941 2025-01-14 17:19:55.122743 2025-01-16 02:04:22.282941 1 day, 8:44:27.160198 +8 12 2025-01-15 16:37:02.143343 2025-01-16 03:42:55.016759 2025-01-15 16:37:02.143343 2025-01-16 03:42:55.016759 11:05:52.873416 +9 33 2025-01-07 04:10:04.304050 2025-01-14 06:17:22.136856 2025-01-07 04:10:04.304050 2025-01-14 06:17:22.136856 7 days, 2:07:17.832806 +10 20 2025-01-15 14:09:47.385854 2025-01-15 18:44:30.038260 2025-01-15 14:09:47.385854 2025-01-15 18:44:30.038260 4:34:42.652406 +11 51 2025-01-01 00:59:52.596560 2025-01-13 06:43:48.551291 2025-01-01 00:59:52.596560 2025-01-13 06:43:48.551291 12 days, 5:43:55.954731 +12 28 2025-01-02 06:47:28.206004 2025-01-14 19:53:03.403793 2025-01-02 06:47:28.206004 2025-01-14 19:53:03.403793 12 days, 13:05:35.197789 +13 35 2025-01-08 22:14:02.275092 2025-01-11 13:22:11.275146 2025-01-08 22:14:02.275092 2025-01-11 13:22:11.275146 2 days, 15:08:09.000054 +14 17 2025-01-07 21:36:32.756117 2025-01-12 04:19:45.223709 2025-01-07 21:36:32.756117 2025-01-12 04:19:45.223709 4 days, 6:43:12.467592 +15 53 2025-01-10 00:34:32.546673 2025-01-13 22:10:33.702885 2025-01-10 00:34:32.546673 2025-01-13 22:10:33.702885 3 days, 21:36:01.156212 +16 41 2025-01-03 14:01:49.125384 2025-01-08 15:07:54.397550 2025-01-03 14:01:49.125384 2025-01-08 15:07:54.397550 5 days, 1:06:05.272166 +17 41 2025-01-06 19:04:06.435975 2025-01-09 02:27:22.195577 2025-01-06 19:04:06.435975 2025-01-09 02:27:22.195577 2 days, 7:23:15.759602 +18 29 2025-01-08 07:24:11.936044 2025-01-12 18:36:12.780490 2025-01-08 07:24:11.936044 2025-01-12 18:36:12.780490 4 days, 11:12:00.844446 +19 2 2025-01-04 10:46:35.348547 2025-01-11 11:52:22.688845 2025-01-04 10:46:35.348547 2025-01-11 11:52:22.688845 7 days, 1:05:47.340298 +20 10 2025-01-15 09:40:05.112260 2025-01-15 22:14:33.721039 2025-01-15 09:40:05.112260 2025-01-15 22:14:33.721039 12:34:28.608779 +21 15 2025-01-14 19:00:10.133783 2025-01-14 21:37:13.933264 2025-01-14 19:00:10.133783 2025-01-14 21:37:13.933264 2:37:03.799481 +22 42 2025-01-06 16:55:11.121820 2025-01-14 09:21:06.182009 2025-01-06 16:55:11.121820 2025-01-14 09:21:06.182009 7 days, 16:25:55.060189 +23 7 2025-01-04 20:04:51.794343 2025-01-08 00:52:56.852043 2025-01-04 20:04:51.794343 2025-01-08 00:52:56.852043 3 days, 4:48:05.057700 +24 12 2025-01-16 05:58:10.433179 2025-01-16 13:41:28.318825 2025-01-16 05:58:10.433179 2025-01-16 13:41:28.318825 7:43:17.885646 +25 28 2025-01-03 11:42:20.829689 2025-01-04 11:42:49.848795 2025-01-03 11:42:20.829689 2025-01-04 11:42:49.848795 1 day, 0:00:29.019106 +26 54 2025-01-14 23:16:57.359030 2025-01-15 01:39:05.735848 2025-01-14 23:16:57.359030 2025-01-15 01:39:05.735848 2:22:08.376818 +27 18 2025-01-13 07:47:10.263691 2025-01-14 02:44:11.996043 2025-01-13 07:47:10.263691 2025-01-14 02:44:11.996043 18:57:01.732352 +28 19 2025-01-10 01:51:29.314503 2025-01-16 00:29:42.513510 2025-01-10 01:51:29.314503 2025-01-16 00:29:42.513510 5 days, 22:38:13.199007 +29 49 2025-01-16 06:51:14.826388 2025-01-16 07:34:47.029669 2025-01-16 06:51:14.826388 2025-01-16 07:34:47.029669 0:43:32.203281 +30 50 2025-01-10 05:25:10.205822 2025-01-15 03:01:21.308648 2025-01-10 05:25:10.205822 2025-01-15 03:01:21.308648 4 days, 21:36:11.102826 \. diff --git a/beta_use_cases/hardware_store/schema.sql b/beta_use_cases/hardware_store/schema.sql index 0ebee02..00bdbb6 100644 --- a/beta_use_cases/hardware_store/schema.sql +++ b/beta_use_cases/hardware_store/schema.sql @@ -2,14 +2,12 @@ DROP SCHEMA IF EXISTS "Hardware Store" CASCADE; CREATE SCHEMA "Hardware Store"; SET search_path = "Hardware Store"; --- Store Locations Table CREATE TABLE "Store Locations" ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, name TEXT NOT NULL, address TEXT ); --- Customers Table CREATE TABLE "Customers" ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, first_name TEXT NOT NULL, @@ -19,7 +17,6 @@ CREATE TABLE "Customers" ( address TEXT ); --- Assets Table: Stores asset details CREATE TABLE "Assets" ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, name TEXT NOT NULL, @@ -33,7 +30,6 @@ CREATE TABLE "Assets" ( CREATE INDEX idx_assets_store_id ON "Assets" (store_id); --- Transactions Table: General transactions for sales, rentals, and returns CREATE TABLE "Transactions" ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, asset_id BIGINT NOT NULL REFERENCES "Assets"(id) ON DELETE CASCADE, @@ -47,7 +43,6 @@ CREATE TABLE "Transactions" ( CREATE INDEX idx_transactions_asset_id ON "Transactions" (asset_id); CREATE INDEX idx_transactions_customer_id ON "Transactions" (customer_id); --- Rentals Table: Tracks rental-specific details CREATE TABLE "Rentals" ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, transaction_id BIGINT NOT NULL REFERENCES "Transactions"(id) ON DELETE CASCADE, diff --git a/beta_use_cases/ice_cream_timesheets/README.md b/beta_use_cases/ice_cream_timesheets/README.md new file mode 100644 index 0000000..acad89c --- /dev/null +++ b/beta_use_cases/ice_cream_timesheets/README.md @@ -0,0 +1,34 @@ +# Ice Cream Employee Management + +This schema represents an employee management system for an ice cream distributor, tracking employees, their timesheets, and schedules. + +```mermaid +%% https://mermaid.js.org/syntax/entityRelationshipDiagram.html + +erDiagram + "Employees" { + BIGINT id PK + string name + string role + string email + } + + "Timesheets" { + BIGINT id PK + BIGINT employee_id FK + TIMESTAMP clock_in + TIMESTAMP clock_out + NUMERIC hours_worked + DATE schedule_date + } + + "Schedules" { + BIGINT id PK + BIGINT employee_id FK + TIMESTAMP start_time + TIMESTAMP end_time + string business_needs + } +%% Relationships + "Timesheets" ||--|| "Employees" : "employee_id" + "Schedules" ||--|| "Employees" : "employee_id" diff --git a/beta_use_cases/ice_cream_timesheets/generate_data.py b/beta_use_cases/ice_cream_timesheets/generate_data.py new file mode 100644 index 0000000..3c80877 --- /dev/null +++ b/beta_use_cases/ice_cream_timesheets/generate_data.py @@ -0,0 +1,90 @@ +import os +import random +from datetime import datetime, timedelta +from faker import Faker + +fake = Faker() + +def clean_value(value): + """Clean a value for SQL COPY operations.""" + if value is None: + return r"\N" + if isinstance(value, str): + return value.replace("\t", " ").replace("\n", " ") + return str(value) + +def write_to_sql_file(output_path, search_path, tables): + """Write the generated data to an SQL file.""" + with open(output_path, "w") as f: + f.write(f'SET search_path="{search_path}";\n\n') + for table_name, generator in tables.items(): + f.write(f'COPY "{table_name}" FROM stdin;\n') + for row in generator: + cleaned_row = "\t".join(map(clean_value, row)) + f.write(f"{cleaned_row}\n") + f.write("\\.\n\n") + print(f"SQL file generated: {output_path}") + +def get_output_file_path(filename): + """Get the output file path relative to the current script's directory.""" + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_file_dir, filename) + +# Constants +NUM_EMPLOYEES = 20 +NUM_TIMESHEETS = 50 +NUM_SCHEDULES = 40 + +ROLES = [ + "Warehouse Manager", + "Delivery Driver", + "Dispatcher", + "Order Picker", + "Forklift Operator", + "Operations Manager", + "Sales Representative", + "Inventory Clerk", + "Customer Service Rep", + "Quality Assurance Specialist", +] + +# Table Data Generation +def generate_employees(): + for i in range(1, NUM_EMPLOYEES + 1): + yield [i, fake.name(), random.choice(ROLES), fake.unique.email()] + +def generate_timesheets(employee_ids): + for i in range(1, NUM_TIMESHEETS + 1): + employee_id = random.choice(employee_ids) + clock_in = fake.date_time_this_year() + clock_out = clock_in + timedelta(hours=random.uniform(4, 8)) + hours_worked = round((clock_out - clock_in).total_seconds() / 3600, 2) + schedule_date = clock_in.date() + yield [i, employee_id, clock_in, clock_out, hours_worked, schedule_date] + +def generate_schedules(employee_ids): + for i in range(1, NUM_SCHEDULES + 1): + employee_id = random.choice(employee_ids) + start_time = fake.date_time_this_year() + end_time = start_time + timedelta(hours=random.uniform(4, 8)) + business_needs = random.choice([ + "Restocking inventory", + "Processing shipments", + "Loading delivery trucks", + "Quality control", + "Training session", + ]) + yield [i, employee_id, start_time, end_time, business_needs] + +# Main Script +if __name__ == "__main__": + employee_ids = list(range(1, NUM_EMPLOYEES + 1)) + + tables = { + "Employees": generate_employees(), + "Timesheets": generate_timesheets(employee_ids), + "Schedules": generate_schedules(employee_ids), + } + + sql_file = get_output_file_path("generated_data.sql") + write_to_sql_file(sql_file, "Ice Cream Employee Management", tables) diff --git a/beta_use_cases/ice_cream_timesheets/generated_data.sql b/beta_use_cases/ice_cream_timesheets/generated_data.sql new file mode 100644 index 0000000..690dd8d --- /dev/null +++ b/beta_use_cases/ice_cream_timesheets/generated_data.sql @@ -0,0 +1,121 @@ +SET search_path="Ice Cream Employee Management"; + +COPY "Employees" FROM stdin; +1 Amber Floyd Forklift Operator monicanewton@example.org +2 Mary Knight Inventory Clerk sheri20@example.net +3 Michelle Carter Inventory Clerk ycarroll@example.com +4 Gabrielle Diaz Operations Manager singhwesley@example.net +5 Eric Rodriguez Delivery Driver veronica74@example.com +6 Kimberly Hayden Quality Assurance Specialist vhughes@example.net +7 Crystal Kennedy Quality Assurance Specialist williamspaul@example.net +8 Melanie Cole Sales Representative jbright@example.net +9 Daniel Greene Dispatcher cisneroschelsey@example.net +10 Douglas Wood Delivery Driver joannecurtis@example.com +11 Douglas Jenkins Inventory Clerk andrew81@example.com +12 Edward Adams Sales Representative mccarthyderrick@example.org +13 Thomas Martinez Forklift Operator davisbarbara@example.org +14 Peter Hill Inventory Clerk christopherbrown@example.org +15 David Mclaughlin Dispatcher david51@example.net +16 Maria Hall Delivery Driver heather20@example.com +17 Eric Manning Forklift Operator jamesmunoz@example.org +18 Timothy Nguyen Customer Service Rep michael65@example.net +19 Kelli Turner Forklift Operator timothyyu@example.net +20 Hannah Brewer Warehouse Manager dixonkeith@example.com +\. + +COPY "Timesheets" FROM stdin; +1 19 2025-01-13 20:50:08.563111 2025-01-14 02:47:21.751524 5.95 2025-01-13 +2 19 2025-01-01 17:13:03.243277 2025-01-01 22:49:00.074219 5.6 2025-01-01 +3 15 2025-01-16 11:08:26.155973 2025-01-16 18:30:12.271467 7.36 2025-01-16 +4 12 2025-01-06 20:53:34.424381 2025-01-07 03:55:53.814798 7.04 2025-01-06 +5 20 2025-01-10 02:17:24.054437 2025-01-10 08:29:35.394059 6.2 2025-01-10 +6 10 2025-01-11 18:27:32.165774 2025-01-11 23:50:56.465913 5.39 2025-01-11 +7 20 2025-01-13 03:17:41.047021 2025-01-13 07:32:36.372273 4.25 2025-01-13 +8 3 2025-01-16 00:12:02.477727 2025-01-16 07:36:04.382052 7.4 2025-01-16 +9 20 2025-01-15 22:31:45.410231 2025-01-16 05:48:43.930281 7.28 2025-01-15 +10 2 2025-01-13 05:51:55.892037 2025-01-13 10:48:19.162608 4.94 2025-01-13 +11 5 2025-01-12 14:18:13.847977 2025-01-12 21:50:07.515121 7.53 2025-01-12 +12 15 2025-01-15 07:29:58.992815 2025-01-15 13:12:36.600722 5.71 2025-01-15 +13 11 2025-01-03 23:04:23.058294 2025-01-04 04:14:27.454460 5.17 2025-01-03 +14 4 2025-01-01 04:38:16.020697 2025-01-01 11:03:07.000861 6.41 2025-01-01 +15 3 2025-01-03 16:25:12.554060 2025-01-04 00:19:46.434074 7.91 2025-01-03 +16 6 2025-01-10 23:54:05.963402 2025-01-11 04:02:25.980970 4.14 2025-01-10 +17 19 2025-01-07 00:46:22.708651 2025-01-07 08:33:35.449447 7.79 2025-01-07 +18 10 2025-01-15 11:32:21.729011 2025-01-15 16:22:50.556381 4.84 2025-01-15 +19 7 2025-01-03 22:31:03.871479 2025-01-04 05:45:55.860805 7.25 2025-01-03 +20 8 2025-01-05 22:51:06.894219 2025-01-06 04:46:07.707107 5.92 2025-01-05 +21 12 2025-01-01 21:42:32.984710 2025-01-02 03:42:52.280405 6.01 2025-01-01 +22 8 2025-01-13 09:48:47.034528 2025-01-13 16:08:46.029829 6.33 2025-01-13 +23 7 2025-01-13 17:56:56.272060 2025-01-14 00:26:12.219642 6.49 2025-01-13 +24 7 2025-01-02 08:23:30.531367 2025-01-02 15:19:38.696501 6.94 2025-01-02 +25 20 2025-01-04 13:19:34.047049 2025-01-04 17:20:32.958968 4.02 2025-01-04 +26 6 2025-01-12 12:50:12.386865 2025-01-12 16:50:15.786879 4.0 2025-01-12 +27 17 2025-01-11 20:15:13.751392 2025-01-12 03:26:27.536201 7.19 2025-01-11 +28 18 2025-01-16 02:37:17.866056 2025-01-16 07:23:03.719130 4.76 2025-01-16 +29 11 2025-01-15 13:30:58.306273 2025-01-15 20:24:56.951643 6.9 2025-01-15 +30 18 2025-01-13 06:37:48.836574 2025-01-13 13:30:43.947849 6.88 2025-01-13 +31 16 2025-01-12 05:43:18.869477 2025-01-12 10:05:31.443834 4.37 2025-01-12 +32 11 2025-01-07 14:32:53.803081 2025-01-07 21:48:02.969306 7.25 2025-01-07 +33 3 2025-01-15 01:16:17.415556 2025-01-15 07:23:13.775709 6.12 2025-01-15 +34 5 2025-01-06 06:25:46.154505 2025-01-06 11:42:46.032721 5.28 2025-01-06 +35 9 2025-01-04 22:04:06.459901 2025-01-05 04:48:42.391666 6.74 2025-01-04 +36 2 2025-01-07 19:45:33.130274 2025-01-08 03:12:27.056897 7.45 2025-01-07 +37 12 2025-01-10 11:14:50.589301 2025-01-10 17:31:04.984809 6.27 2025-01-10 +38 9 2025-01-04 09:40:38.514596 2025-01-04 15:27:54.127815 5.79 2025-01-04 +39 12 2025-01-07 06:37:52.238430 2025-01-07 13:37:44.753917 7.0 2025-01-07 +40 11 2025-01-07 21:09:10.845505 2025-01-08 01:11:08.036562 4.03 2025-01-07 +41 1 2025-01-02 03:28:55.022101 2025-01-02 08:47:57.232268 5.32 2025-01-02 +42 20 2025-01-12 21:48:56.284322 2025-01-13 04:55:06.293602 7.1 2025-01-12 +43 20 2025-01-15 03:48:22.310376 2025-01-15 09:02:41.783954 5.24 2025-01-15 +44 18 2025-01-09 02:50:44.368068 2025-01-09 08:22:50.567494 5.54 2025-01-09 +45 15 2025-01-11 12:53:52.473470 2025-01-11 17:38:37.789720 4.75 2025-01-11 +46 14 2025-01-05 06:58:53.497452 2025-01-05 11:17:49.554537 4.32 2025-01-05 +47 8 2025-01-13 16:20:18.197104 2025-01-13 20:40:08.204040 4.33 2025-01-13 +48 2 2025-01-11 09:48:43.811543 2025-01-11 15:07:12.670014 5.31 2025-01-11 +49 20 2025-01-15 20:41:30.398623 2025-01-16 01:51:48.341750 5.17 2025-01-15 +50 14 2025-01-01 01:44:58.477481 2025-01-01 09:09:10.302117 7.4 2025-01-01 +\. + +COPY "Schedules" FROM stdin; +1 10 2025-01-13 02:25:56.994067 2025-01-13 09:48:02.070122 Quality control +2 1 2025-01-03 15:13:49.262238 2025-01-03 20:20:14.640924 Loading delivery trucks +3 13 2025-01-14 07:57:29.329422 2025-01-14 13:24:56.549714 Processing shipments +4 2 2025-01-15 16:32:47.894829 2025-01-15 22:15:28.502814 Loading delivery trucks +5 6 2025-01-09 12:51:38.823687 2025-01-09 18:35:38.041795 Training session +6 18 2025-01-15 17:29:34.920549 2025-01-15 23:31:59.774665 Training session +7 10 2025-01-06 01:22:46.081317 2025-01-06 05:41:40.377657 Training session +8 12 2025-01-10 19:42:06.597029 2025-01-11 00:07:38.993160 Restocking inventory +9 13 2025-01-04 00:46:57.181703 2025-01-04 06:29:47.911629 Quality control +10 20 2025-01-07 15:30:59.774489 2025-01-07 23:11:25.739526 Training session +11 20 2025-01-14 13:11:45.271858 2025-01-14 17:33:46.632323 Restocking inventory +12 16 2025-01-02 19:17:05.535340 2025-01-03 00:26:37.615243 Restocking inventory +13 11 2025-01-08 15:10:09.777190 2025-01-08 22:21:36.085676 Training session +14 3 2025-01-13 05:40:48.423594 2025-01-13 13:25:02.448176 Restocking inventory +15 4 2025-01-16 09:30:09.234002 2025-01-16 13:31:27.400791 Training session +16 2 2025-01-11 23:34:10.985305 2025-01-12 06:53:35.494328 Quality control +17 10 2025-01-13 07:14:26.963166 2025-01-13 14:29:00.834688 Quality control +18 15 2025-01-04 14:02:59.433708 2025-01-04 20:06:04.084059 Processing shipments +19 8 2025-01-07 00:18:03.104338 2025-01-07 06:57:53.901661 Loading delivery trucks +20 19 2025-01-12 01:31:12.594947 2025-01-12 09:11:08.514296 Quality control +21 7 2025-01-01 12:00:14.082933 2025-01-01 19:10:16.253894 Loading delivery trucks +22 13 2025-01-01 06:01:47.053241 2025-01-01 12:10:01.941483 Processing shipments +23 4 2025-01-09 13:07:44.147804 2025-01-09 20:13:58.711770 Processing shipments +24 9 2025-01-14 14:47:41.807832 2025-01-14 20:59:32.199412 Quality control +25 19 2025-01-07 09:04:12.905998 2025-01-07 14:07:49.034179 Training session +26 9 2025-01-10 17:52:14.365013 2025-01-10 22:54:56.793067 Quality control +27 4 2025-01-12 21:08:39.219214 2025-01-13 04:51:26.355417 Quality control +28 3 2025-01-05 18:57:36.886595 2025-01-06 01:40:09.371538 Training session +29 15 2025-01-16 00:15:20.513286 2025-01-16 07:17:36.713029 Training session +30 1 2025-01-01 15:34:05.702021 2025-01-01 20:47:29.832745 Processing shipments +31 2 2025-01-15 07:55:21.264659 2025-01-15 15:30:45.226639 Quality control +32 3 2025-01-10 20:54:41.645221 2025-01-11 03:41:45.949855 Training session +33 7 2025-01-14 18:44:55.143203 2025-01-15 00:34:33.649466 Restocking inventory +34 14 2025-01-16 12:12:29.955432 2025-01-16 16:15:11.872420 Processing shipments +35 4 2025-01-05 12:25:15.905673 2025-01-05 18:12:10.342993 Quality control +36 16 2025-01-01 16:29:51.259942 2025-01-01 22:10:30.385487 Training session +37 4 2025-01-09 14:12:29.808595 2025-01-09 20:20:28.803041 Restocking inventory +38 13 2025-01-11 21:31:02.562493 2025-01-12 01:56:56.885104 Loading delivery trucks +39 17 2025-01-10 15:39:14.564881 2025-01-10 20:17:50.718492 Training session +40 13 2025-01-14 07:16:23.048436 2025-01-14 14:38:57.367881 Processing shipments +\. + diff --git a/beta_use_cases/ice_cream_timesheets/schema.sql b/beta_use_cases/ice_cream_timesheets/schema.sql new file mode 100644 index 0000000..1002b82 --- /dev/null +++ b/beta_use_cases/ice_cream_timesheets/schema.sql @@ -0,0 +1,27 @@ +DROP SCHEMA IF EXISTS "Ice Cream Employee Management" CASCADE; +CREATE SCHEMA "Ice Cream Employee Management"; +SET search_path = "Ice Cream Employee Management"; + +CREATE TABLE "Employees" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + role TEXT NOT NULL, + email TEXT UNIQUE NOT NULL +); + +CREATE TABLE "Timesheets" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + employee_id BIGINT NOT NULL REFERENCES "Employees" (id) ON DELETE CASCADE, + clock_in TIMESTAMP WITH TIME ZONE NOT NULL, + clock_out TIMESTAMP WITH TIME ZONE, + hours_worked NUMERIC(5, 2), + schedule_date DATE NOT NULL +); + +CREATE TABLE "Schedules" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + employee_id BIGINT NOT NULL REFERENCES "Employees" (id) ON DELETE CASCADE, + start_time TIMESTAMP WITH TIME ZONE NOT NULL, + end_time TIMESTAMP WITH TIME ZONE NOT NULL, + business_needs TEXT +); diff --git a/beta_use_cases/justfile b/beta_use_cases/justfile new file mode 100644 index 0000000..0a57125 --- /dev/null +++ b/beta_use_cases/justfile @@ -0,0 +1,9 @@ +load DATASET: + #!/usr/bin/env bash + # First load the schema and tables + docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < {{DATASET}}/schema.sql + # Then the sample data + docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < {{DATASET}}/generated_data.sql + +generate DATASET: + python {{DATASET}}/generate_data.py diff --git a/beta_use_cases/library_makerspace/README.md b/beta_use_cases/library_makerspace/README.md new file mode 100644 index 0000000..5e3fa43 --- /dev/null +++ b/beta_use_cases/library_makerspace/README.md @@ -0,0 +1,72 @@ +Library Makerspace Sample Data + +This sample dataset represents a library makerspace managing equipment, patrons, and job requests with enforced training requirements for patrons. + +```mermaid +%% https://mermaid.js.org/syntax/entityRelationshipDiagram.html + +erDiagram + "Equipment" { + BIGINT id PK + string name + string location + BIGINT status_id FK + BIGINT type_id FK + } + + "Patrons" { + BIGINT id PK + string name + string email + } + + "Jobs" { + BIGINT id PK + BIGINT equipment_id FK + BIGINT patron_id FK + BIGINT status_id FK + int queue_order + TIMESTAMP requested_at + TIMESTAMP job_start + TIMESTAMP job_end + } + + "Equipment Statuses" { + BIGINT id PK + string name + } + + "Job Statuses" { + BIGINT id PK + string name + } + + "Equipment Types" { + BIGINT id PK + string name + } + + "Category Statuses" { + BIGINT id PK + BIGINT type_id FK + string status_name + } + + "Equipment Training" { + BIGINT id PK + BIGINT patron_id FK + BIGINT equipment_id FK + TIMESTAMP trained_at + } + +%% Relationships +%% See: https://mermaid.js.org/syntax/entityRelationshipDiagram.html#relationship-syntax + "Equipment" ||--|| "Equipment Statuses" : "status_id" + "Equipment" ||--|| "Equipment Types" : "type_id" + "Equipment" ||--o{ "Equipment Training" : "equipment_id" + "Equipment Types" ||--o{ "Category Statuses" : "type_id" + "Patrons" ||--o{ "Equipment Training" : "patron_id" + "Jobs" ||--|| "Equipment" : "equipment_id" + "Jobs" ||--|| "Patrons" : "patron_id" + "Jobs" ||--|| "Job Statuses" : "status_id" +``` diff --git a/beta_use_cases/library_makerspace/generate_data.py b/beta_use_cases/library_makerspace/generate_data.py new file mode 100644 index 0000000..f7236c1 --- /dev/null +++ b/beta_use_cases/library_makerspace/generate_data.py @@ -0,0 +1,133 @@ +import os +import random +from datetime import datetime +from faker import Faker + +fake = Faker() + +# Helper functions +def clean_value(value): + """Clean a value for SQL COPY operations.""" + if value is None: + return r"\N" + if isinstance(value, str): + return value.replace("\t", " ").replace("\n", " ") + return str(value) + +def write_to_sql_file(output_path, search_path, tables): + """Write the generated data to an SQL file.""" + with open(output_path, "w") as f: + f.write(f'SET search_path="{search_path}";\n\n') + for table_name, generator in tables.items(): + f.write(f'COPY "{table_name}" FROM stdin;\n') + for row in generator: + cleaned_row = "\t".join(map(clean_value, row)) + f.write(f"{cleaned_row}\n") + f.write("\\.\n\n") + print(f"SQL file generated: {output_path}") + +def get_output_file_path(filename): + """Get the output file path relative to the current script's directory.""" + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_file_dir, filename) + +# Constants +NUM_PATRONS = 20 +NUM_EQUIPMENT_TYPES = 7 +NUM_EQUIPMENT = 30 +NUM_JOBS = 50 +NUM_TRAINING_RECORDS = 40 +NUM_JOB_STATUSES = 5 +NUM_EQUIPMENT_STATUSES = 4 + +EQUIPMENT_TYPES = [ + "Sewing Machine", + "Laser Cutter", + "Vinyl Cutter", + "Sublimation Printer", + "3D Printer", +] + +BRANDS = { + "Sewing Machine": ["Brother", "Singer", "Janome"], + "Laser Cutter": ["Epilog", "Glowforge", "Trotec"], + "Vinyl Cutter": ["Silhouette", "Cricut"], + "Sublimation Printer": ["Sawgrass", "Epson", "Canon"], + "3D Printer": ["Creality", "Prusa", "MakerBot"], +} + +LOCATIONS = [ + "Main Library", + "Mount Pleasant", + "Rochambeau", + "Knight Memorial", +] + +JOB_STATUSES = ["Pending", "In Progress", "Completed", "Cancelled", "On Hold", "See Desk"] +EQUIPMENT_STATUSES = ["Available", "In Use", "Out of Order", "Under Maintenance", "On Loan"] + +def generate_patrons(): + for i in range(1, NUM_PATRONS + 1): + yield [i, fake.name(), fake.unique.email()] + +def generate_equipment_types(): + for i, name in enumerate(EQUIPMENT_TYPES, start=1): + yield [i, name] + +def generate_equipment(): + for i in range(1, NUM_EQUIPMENT + 1): + equipment_type = random.choice(EQUIPMENT_TYPES) + brand = random.choice(BRANDS[equipment_type]) + model = f"{fake.random_uppercase_letter()}{random.randint(100, 999)}" + yield [ + i, + f"{brand} {equipment_type} {model}", # Equipment name + random.choice(LOCATIONS), # Location + random.randint(1, NUM_EQUIPMENT_STATUSES), # Status ID + EQUIPMENT_TYPES.index(equipment_type) + 1, # Type ID (1-based index) + ] + +def generate_training_records(): + trained_pairs = set() + for _ in range(NUM_TRAINING_RECORDS): + patron_id = random.randint(1, NUM_PATRONS) + equipment_id = random.randint(1, NUM_EQUIPMENT) + if (patron_id, equipment_id) not in trained_pairs: + trained_pairs.add((patron_id, equipment_id)) + yield [len(trained_pairs), patron_id, equipment_id, fake.date_time_this_year()] + +def generate_jobs(training_records): + for i in range(1, NUM_JOBS + 1): + # Randomly select a valid training record + _, patron_id, equipment_id, _ = random.choice(training_records) + yield [ + i, + equipment_id, + patron_id, + random.randint(1, NUM_JOB_STATUSES), + i, # queue_order + fake.date_time_this_year(), + fake.date_time_this_year() if random.random() < 0.7 else None, + fake.date_time_this_year() if random.random() < 0.5 else None, + ] + +def generate_statuses(status_list): + for i, name in enumerate(status_list, start=1): + yield [i, name] + +if __name__ == "__main__": + # Pre-generate training records + training_records = list(generate_training_records()) + + tables = { + "Equipment Statuses": generate_statuses(EQUIPMENT_STATUSES), + "Job Statuses": generate_statuses(JOB_STATUSES), + "Equipment Types": generate_equipment_types(), + "Equipment": generate_equipment(), + "Patrons": generate_patrons(), + "Equipment Training": iter(training_records), + "Jobs": generate_jobs(training_records), + } + + sql_file = get_output_file_path("generated_data.sql") + write_to_sql_file(sql_file, "Library Makerspace", tables) diff --git a/beta_use_cases/library_makerspace/generated_data.sql b/beta_use_cases/library_makerspace/generated_data.sql new file mode 100644 index 0000000..51ca984 --- /dev/null +++ b/beta_use_cases/library_makerspace/generated_data.sql @@ -0,0 +1,177 @@ +SET search_path="Library Makerspace"; + +COPY "Equipment Statuses" FROM stdin; +1 Available +2 In Use +3 Out of Order +4 Under Maintenance +5 On Loan +\. + +COPY "Job Statuses" FROM stdin; +1 Pending +2 In Progress +3 Completed +4 Cancelled +5 On Hold +6 See Desk +\. + +COPY "Equipment Types" FROM stdin; +1 Sewing Machine +2 Laser Cutter +3 Vinyl Cutter +4 Sublimation Printer +5 3D Printer +\. + +COPY "Equipment" FROM stdin; +1 Sawgrass Sublimation Printer W987 Main Library 4 4 +2 MakerBot 3D Printer D224 Mount Pleasant 4 5 +3 Singer Sewing Machine N364 Main Library 3 1 +4 Cricut Vinyl Cutter S798 Mount Pleasant 2 3 +5 Epilog Laser Cutter N416 Main Library 4 2 +6 Epilog Laser Cutter F650 Rochambeau 1 2 +7 Cricut Vinyl Cutter J407 Main Library 2 3 +8 Brother Sewing Machine F892 Rochambeau 1 1 +9 MakerBot 3D Printer V670 Mount Pleasant 2 5 +10 Janome Sewing Machine C625 Mount Pleasant 4 1 +11 Canon Sublimation Printer H926 Main Library 2 4 +12 Creality 3D Printer J991 Knight Memorial 4 5 +13 MakerBot 3D Printer J805 Rochambeau 4 5 +14 Cricut Vinyl Cutter Q537 Main Library 1 3 +15 Glowforge Laser Cutter I191 Mount Pleasant 3 2 +16 Cricut Vinyl Cutter R145 Main Library 1 3 +17 Cricut Vinyl Cutter S184 Mount Pleasant 4 3 +18 Epilog Laser Cutter T355 Mount Pleasant 1 2 +19 Cricut Vinyl Cutter D133 Main Library 4 3 +20 Prusa 3D Printer G693 Knight Memorial 2 5 +21 Janome Sewing Machine X183 Main Library 2 1 +22 Epson Sublimation Printer G314 Main Library 3 4 +23 Silhouette Vinyl Cutter H407 Main Library 4 3 +24 Brother Sewing Machine T514 Rochambeau 2 1 +25 Brother Sewing Machine O206 Rochambeau 1 1 +26 Silhouette Vinyl Cutter C590 Mount Pleasant 2 3 +27 Creality 3D Printer V252 Main Library 3 5 +28 Janome Sewing Machine Y500 Knight Memorial 1 1 +29 Creality 3D Printer E740 Mount Pleasant 2 5 +30 Epson Sublimation Printer X133 Main Library 3 4 +\. + +COPY "Patrons" FROM stdin; +1 John Perkins larrygray@example.org +2 Michael Rodriguez raymondrandall@example.net +3 Rhonda Chung groberts@example.net +4 Curtis Hansen lindabarnett@example.net +5 Stephen Brown campbellclarence@example.net +6 Michael Small debramoon@example.com +7 Maureen Moore chad70@example.net +8 Allison Caldwell howardmelissa@example.net +9 Amanda Mills vprince@example.net +10 Wesley Warren ifisher@example.net +11 Wendy Garza josephcampbell@example.com +12 Ryan Serrano shannon59@example.com +13 Katie Ortega joyce15@example.com +14 Natalie Bryant wendymoore@example.org +15 Sandra Avery brandonhoward@example.net +16 Jennifer Williams daniel17@example.net +17 Nicole Jenkins bgarza@example.net +18 Alexandra Thompson maryclayton@example.org +19 Thomas Burke hhines@example.com +20 Wendy Good fmiller@example.org +\. + +COPY "Equipment Training" FROM stdin; +1 1 15 2025-01-03 23:26:40.199358 +2 13 20 2025-01-06 00:37:45.785448 +3 12 24 2025-01-10 10:02:55.453621 +4 3 10 2025-01-05 17:32:06.870889 +5 7 13 2025-01-03 11:59:26.861895 +6 1 25 2025-01-09 23:53:53.998417 +7 16 12 2025-01-01 10:23:42.330150 +8 5 19 2025-01-10 18:30:43.934640 +9 14 1 2025-01-06 03:51:29.090905 +10 17 22 2025-01-05 23:00:01.373499 +11 20 27 2025-01-14 18:13:08.864392 +12 17 7 2025-01-14 05:05:37.293080 +13 1 23 2025-01-11 02:51:56.943888 +14 8 4 2025-01-13 02:43:22.228214 +15 14 30 2025-01-07 15:32:08.082597 +16 15 8 2025-01-04 05:27:00.874010 +17 6 9 2025-01-10 07:01:43.345248 +18 16 19 2025-01-01 05:50:44.063754 +19 7 9 2025-01-06 21:37:49.904934 +20 6 15 2025-01-03 15:11:16.696003 +21 12 21 2025-01-03 20:22:13.194737 +22 9 10 2025-01-15 08:55:10.341485 +23 6 25 2025-01-10 09:56:28.768175 +24 6 21 2025-01-11 18:52:05.069437 +25 17 4 2025-01-05 11:19:51.536982 +26 14 17 2025-01-16 01:27:04.924575 +27 8 1 2025-01-01 23:59:13.566663 +28 20 18 2025-01-08 18:40:08.225922 +29 14 6 2025-01-13 02:34:10.492475 +30 10 15 2025-01-15 04:27:13.097361 +31 6 16 2025-01-01 18:48:31.680527 +32 3 13 2025-01-13 11:11:24.931279 +33 20 11 2025-01-01 19:53:42.656989 +34 14 19 2025-01-08 22:09:30.936280 +35 2 2 2025-01-01 20:38:42.682912 +36 2 26 2025-01-10 11:54:24.647240 +37 12 18 2025-01-02 13:48:54.654248 +38 15 3 2025-01-05 02:24:30.363100 +\. + +COPY "Jobs" FROM stdin; +1 4 8 1 1 2025-01-15 19:48:43.552434 2025-01-02 19:21:02.163269 2025-01-03 04:48:09.336896 +2 19 5 5 2 2025-01-13 00:17:52.839903 \N 2025-01-12 12:49:37.435127 +3 23 1 2 3 2025-01-06 05:32:06.767621 \N \N +4 30 14 4 4 2025-01-14 01:26:41.866860 2025-01-12 09:18:41.420974 2025-01-16 03:06:20.292391 +5 15 10 2 5 2025-01-12 10:07:31.349304 \N 2025-01-10 18:21:18.008380 +6 3 15 1 6 2025-01-15 06:53:33.834182 2025-01-05 22:51:36.625056 \N +7 2 2 4 7 2025-01-08 14:31:47.168803 2025-01-11 12:42:28.915767 \N +8 4 8 4 8 2025-01-04 07:37:02.303042 \N \N +9 15 10 2 9 2025-01-08 09:28:38.631279 \N 2025-01-10 09:55:59.987533 +10 26 2 3 10 2025-01-14 16:29:21.741730 2025-01-11 02:02:59.032064 \N +11 4 8 5 11 2025-01-01 19:41:13.006415 2025-01-15 22:35:54.650185 2025-01-06 05:26:27.725281 +12 30 14 1 12 2025-01-11 22:48:31.286979 2025-01-04 01:45:30.821146 \N +13 19 5 2 13 2025-01-05 18:14:09.834708 2025-01-01 08:21:49.295401 2025-01-12 15:31:48.839205 +14 19 5 1 14 2025-01-03 23:27:32.723922 2025-01-12 22:54:15.106789 2025-01-08 21:24:31.412678 +15 12 16 4 15 2025-01-11 08:05:05.644700 2025-01-09 03:02:06.710709 2025-01-15 07:45:53.251518 +16 10 9 1 16 2025-01-03 15:01:56.444147 2025-01-05 11:02:23.259763 \N +17 23 1 3 17 2025-01-15 08:58:16.058669 2025-01-05 19:17:24.303866 \N +18 11 20 4 18 2025-01-13 07:37:52.066643 2025-01-11 16:46:46.950879 2025-01-06 22:59:41.216824 +19 9 6 2 19 2025-01-16 13:35:48.445005 2025-01-01 00:39:17.821871 \N +20 6 14 1 20 2025-01-10 19:35:26.122582 \N \N +21 4 17 5 21 2025-01-12 07:19:35.253678 \N 2025-01-05 06:38:11.328380 +22 15 10 1 22 2025-01-09 20:42:41.967700 \N \N +23 21 12 5 23 2025-01-14 09:03:28.248323 2025-01-01 23:56:17.826654 \N +24 13 3 3 24 2025-01-09 21:36:48.511732 \N 2025-01-14 17:26:57.553673 +25 19 14 2 25 2025-01-15 02:39:47.890399 2025-01-03 21:24:25.330063 \N +26 2 2 1 26 2025-01-12 08:45:31.568590 \N \N +27 25 1 1 27 2025-01-05 06:25:57.856893 2025-01-04 13:45:29.698746 2025-01-03 02:25:12.923054 +28 19 16 2 28 2025-01-11 06:51:18.877261 2025-01-04 10:29:33.974414 \N +29 26 2 1 29 2025-01-12 13:20:50.112684 2025-01-16 18:41:40.439770 \N +30 19 16 4 30 2025-01-09 02:15:02.549183 2025-01-07 15:24:35.925649 2025-01-09 00:23:02.284361 +31 15 6 4 31 2025-01-14 04:25:22.753648 \N \N +32 6 14 1 32 2025-01-15 00:12:46.609896 2025-01-07 12:34:32.447647 \N +33 25 6 1 33 2025-01-10 06:10:45.086229 2025-01-02 04:41:57.962266 \N +34 15 1 5 34 2025-01-02 12:17:25.561854 \N 2025-01-15 08:14:34.964554 +35 2 2 4 35 2025-01-16 08:56:05.594327 \N \N +36 30 14 4 36 2025-01-03 22:43:17.443394 2025-01-08 18:15:33.932495 \N +37 27 20 3 37 2025-01-02 08:23:00.759751 \N \N +38 20 13 3 38 2025-01-08 08:04:53.197479 \N 2025-01-13 14:11:52.436438 +39 22 17 4 39 2025-01-05 14:56:29.796596 \N 2025-01-11 07:30:21.558733 +40 11 20 2 40 2025-01-15 06:38:18.053659 2025-01-03 07:37:42.236742 2025-01-10 03:16:06.794250 +41 8 15 2 41 2025-01-01 14:57:41.047105 \N 2025-01-02 21:43:02.817504 +42 12 16 2 42 2025-01-10 16:02:23.584153 \N \N +43 4 8 3 43 2025-01-15 13:12:20.947076 2025-01-02 05:57:52.754697 \N +44 25 1 4 44 2025-01-13 09:04:19.548939 2025-01-07 12:43:46.636346 \N +45 13 3 3 45 2025-01-15 04:38:52.617357 2025-01-15 05:13:20.018702 \N +46 19 16 5 46 2025-01-08 16:12:38.108931 2025-01-12 23:06:25.278850 2025-01-01 06:26:39.406554 +47 10 9 5 47 2025-01-10 08:36:06.709483 2025-01-11 23:05:11.542603 2025-01-06 02:24:45.792833 +48 11 20 4 48 2025-01-10 07:30:13.044270 2025-01-13 17:25:34.337202 \N +49 13 7 5 49 2025-01-05 07:02:38.064458 \N \N +50 1 14 1 50 2025-01-11 05:02:01.251550 2025-01-02 16:33:45.935645 2025-01-10 22:14:47.580793 +\. + diff --git a/beta_use_cases/library_makerspace/schema.sql b/beta_use_cases/library_makerspace/schema.sql new file mode 100644 index 0000000..35e2b9c --- /dev/null +++ b/beta_use_cases/library_makerspace/schema.sql @@ -0,0 +1,79 @@ +DROP SCHEMA IF EXISTS "Library Makerspace" CASCADE; +CREATE SCHEMA "Library Makerspace"; +SET search_path = "Library Makerspace"; + +CREATE TABLE "Equipment Statuses" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL UNIQUE +); + +CREATE TABLE "Job Statuses" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL UNIQUE +); + +CREATE TABLE "Equipment Types" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL UNIQUE +); + +CREATE TABLE "Equipment" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + location TEXT, + status_id BIGINT REFERENCES "Equipment Statuses" (id) ON DELETE CASCADE, + type_id BIGINT REFERENCES "Equipment Types" (id) ON DELETE CASCADE +); + +CREATE TABLE "Patrons" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + email TEXT UNIQUE NOT NULL +); + +CREATE TABLE "Jobs" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + equipment_id BIGINT REFERENCES "Equipment" (id) ON DELETE CASCADE, + patron_id BIGINT REFERENCES "Patrons" (id) ON DELETE CASCADE, + status_id BIGINT REFERENCES "Job Statuses" (id) ON DELETE CASCADE, + queue_order INT NOT NULL, + requested_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + job_start TIMESTAMP WITH TIME ZONE, + job_end TIMESTAMP WITH TIME ZONE +); + +CREATE TABLE "Category Statuses" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + type_id BIGINT REFERENCES "Equipment Types" (id) ON DELETE CASCADE, + status_name TEXT NOT NULL, + UNIQUE (type_id, status_name) +); + +CREATE TABLE "Equipment Training" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + patron_id BIGINT REFERENCES "Patrons" (id) ON DELETE CASCADE, + equipment_id BIGINT REFERENCES "Equipment" (id) ON DELETE CASCADE, + trained_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE (patron_id, equipment_id) +); + +CREATE OR REPLACE FUNCTION check_training() +RETURNS TRIGGER AS $$ +BEGIN + -- Check if the patron is trained for the equipment + IF NOT EXISTS ( + SELECT 1 + FROM "Equipment Training" + WHERE "Equipment Training".patron_id = NEW.patron_id + AND "Equipment Training".equipment_id = NEW.equipment_id + ) THEN + RAISE EXCEPTION 'Patron % is not trained on equipment %', NEW.patron_id, NEW.equipment_id; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER enforce_training +BEFORE INSERT OR UPDATE ON "Jobs" +FOR EACH ROW +EXECUTE FUNCTION check_training(); diff --git a/beta_use_cases/museum_exhibits/README.md b/beta_use_cases/museum_exhibits/README.md index 182ad60..4df4f59 100644 --- a/beta_use_cases/museum_exhibits/README.md +++ b/beta_use_cases/museum_exhibits/README.md @@ -44,29 +44,17 @@ erDiagram BIGINT exhibit_id FK } + "Item_Collections" { + BIGINT item_id FK + BIGINT collection_id FK + } + %% Relationships %% See: https://mermaid.js.org/syntax/entityRelationshipDiagram.html#relationship-syntax "Exhibits" ||--|| "Locations" : "location_id" "Items" ||--|| "Acquisition Types" : "acquisition_type_id" - "Items" ||--|| "Collections" : "collection_id" "Items" ||--|| "Exhibits" : "exhibit_id" + "Item_Collections" }|--|| "Items" : "item_id" + "Item_Collections" }|--|| "Collections" : "collection_id" ``` - - -## Loading Data - -The generated SQL file, `generate_data/load_data.sql`, contains all the necessary COPY commands to import data into your database. The data (and the load data file) are produced by the `generate_data.py` file, which can be adjusted and re-run to alter the data if needed. - -Load the data into a locally-running Mathesar instance like this: - -```shell -# First load the schema and tables -docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < schema.sql -# Then the sample data -docker exec -i mathesar_dev_db bash -c 'psql -U mathesar' < generated_data.sql -``` - -## Development - -The only requirement is to install dependencies with `pip install -r requirements.txt`. diff --git a/beta_use_cases/museum_exhibits/generate_data.py b/beta_use_cases/museum_exhibits/generate_data.py index 5a4c8fd..0b8fecb 100644 --- a/beta_use_cases/museum_exhibits/generate_data.py +++ b/beta_use_cases/museum_exhibits/generate_data.py @@ -1,36 +1,103 @@ import os import random -from faker import Faker from datetime import datetime, timedelta +from faker import Faker fake = Faker() -# Number of rows to generate -NUM_LOCATIONS = 5 -NUM_COLLECTIONS = 10 -NUM_ACQUISITION_TYPES = 5 -NUM_EXHIBITS = 15 -NUM_ITEMS = 50 - -# Helper function to clean values for COPY +# Helper functions def clean_value(value): + """Clean a value for SQL COPY operations.""" if value is None: return r"\N" if isinstance(value, str): return value.replace("\t", " ").replace("\n", " ") return str(value) -adjectives = ["Ancient", "Modern", "Historic", "Rare", "Exquisite"] -nouns = ["Art", "Relics", "Artifacts", "Paintings", "Manuscripts", "Sculptures", "Vases", "Bowls"] +def write_to_sql_file(output_path, search_path, tables): + """Write the generated data to an SQL file.""" + with open(output_path, "w") as f: + f.write(f'SET search_path="{search_path}";\n\n') + for table_name, generator in tables.items(): + f.write(f'COPY "{table_name}" FROM stdin;\n') + for row in generator: + cleaned_row = "\t".join(map(clean_value, row)) + f.write(f"{cleaned_row}\n") + f.write("\\.\n\n") + print(f"SQL file generated: {output_path}") + +def get_output_file_path(filename): + """Get the output file path relative to the current script's directory.""" + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_file_dir, filename) + +# Constants +NUM_LOCATIONS = 5 +NUM_COLLECTIONS = 10 +NUM_ACQUISITION_TYPES = 5 +NUM_EXHIBITS = 15 +NUM_ITEMS = 50 + +# Base and descriptor model for items +BASE_ITEMS = { + "artifact": ["region", "time_period", "material"], + "manuscript": ["region", "time_period", "content_type"], + "painting": ["art_style", "region", "texture"], + "sketches": ["art_style", "region", "texture", "content_type"], + "portrait series": ["art_style", "region", "texture"], + "drawing": ["themes", "art_style", "region", "texture"], + "sculpture": ["material", "art_style", "region"], + "vase": ["material", "region", "art_style"], + "pot": ["region", "art_style"], + "bowl and plate": ["material", "region", "art_style"], +} + +DESCRIPTORS = { + "themes": ["bird", "landscape", "butterfly", "isolation", "urban landscapes", "the home"], + "art_style": ["impressionist", "baroque", "abstract", "cubist", "modernist"], + "content_type": ["religious", "philosophical", "scientific", "literary"], + "material": ["marble", "bronze", "clay", "papyrus", "wood", "charcoal", "pen"], + "region": ["greek", "roman", "egyptian", "asian", "medieval european"], + "texture": ["smooth", "rough", "polished", "weathered"], + "time_period": ["ancient", "medieval", "renaissance", "19th century", "20th century"], +} + +COLLECTION_THEMES = [ + "decontextualizing", + "reclaiming the", + "liminal retreat in", + "voices in", + "silence: exploring", + "perspectives on", + "the evolution of", +] + +# Helper function to generate item names +def generate_item_name(): + base_item = random.choice(list(BASE_ITEMS.keys())) + allowed_descriptors = BASE_ITEMS[base_item] + name_parts = [base_item] + + for category in allowed_descriptors: + if random.random() < 0.7: # 70% chance to include a descriptor + descriptor = random.choice(DESCRIPTORS[category]) + name_parts.insert(0, descriptor) + + if random.random() < 0.5: # 50% chance to include a theme + theme = random.choice(DESCRIPTORS["themes"]) + name_parts.insert(0, theme) + + return " ".join(name_parts).capitalize() -# Table Data Generation def generate_locations(): for i in range(1, NUM_LOCATIONS + 1): yield [i, f"Museum Location {i}", fake.address()] def generate_collections(): for i in range(1, NUM_COLLECTIONS + 1): - name = f"{random.choice(adjectives)} {random.choice(nouns)} Collection" + art_style = random.choice(DESCRIPTORS["art_style"]) + time_period = random.choice(DESCRIPTORS["time_period"]) + name = f"{time_period} {art_style} Collection".title() yield [i, name, fake.text(max_nb_chars=50)] def generate_acquisition_types(): @@ -40,7 +107,10 @@ def generate_acquisition_types(): def generate_exhibits(location_ids): for i in range(1, NUM_EXHIBITS + 1): - name = f"{random.choice(adjectives)} {random.choice(nouns)} Exhibit" + base_item = random.choice(list(BASE_ITEMS.keys())) + exhibit_theme = random.choice(COLLECTION_THEMES) + item_theme = random.choice(DESCRIPTORS["themes"]) + name = f"{exhibit_theme} {item_theme} {base_item}".title() start_date = fake.date_this_year() end_date = ( fake.date_between_dates(date_start=start_date, date_end=datetime.today() + timedelta(days=180)) @@ -53,12 +123,12 @@ def generate_exhibits(location_ids): end_date, random.choice(location_ids), random.choice([True, False]), - fake.text(max_nb_chars=100) + fake.text(max_nb_chars=100), ] def generate_items(acquisition_type_ids, collection_ids, exhibit_ids): for i in range(1, NUM_ITEMS + 1): - name = f"{random.choice(adjectives)} {random.choice(nouns)}" + name = generate_item_name() acquisition_date = fake.date_this_year() exhibit_id = random.choice(exhibit_ids) if random.random() < 0.5 else None yield [ @@ -67,36 +137,37 @@ def generate_items(acquisition_type_ids, collection_ids, exhibit_ids): fake.unique.ean13(), acquisition_date, random.choice(acquisition_type_ids), - random.choice(collection_ids), - exhibit_id + exhibit_id, ] -# Generate Data -location_ids = list(range(1, NUM_LOCATIONS + 1)) -collection_ids = list(range(1, NUM_COLLECTIONS + 1)) -acquisition_type_ids = list(range(1, NUM_ACQUISITION_TYPES + 1)) -exhibit_ids = list(range(1, NUM_EXHIBITS + 1)) - -tables = { - "Locations": generate_locations(), - "Collections": generate_collections(), - "Acquisition Types": generate_acquisition_types(), - "Exhibits": generate_exhibits(location_ids), - "Items": generate_items(acquisition_type_ids, collection_ids, exhibit_ids), -} +def generate_item_collections(collection_ids, item_ids): + """Generate relationships between items and multiple collections.""" + for item_id in item_ids: + num_collections = random.randint(1, 3) # Each item belongs to 1-3 collections + assigned_collections = random.sample(collection_ids, num_collections) + for collection_id in assigned_collections: + yield [item_id, collection_id] -# Write to SQL file -sql_file = os.path.join(os.getcwd(), "generated_data.sql") +if __name__ == "__main__": + # Generate IDs + location_ids = list(range(1, NUM_LOCATIONS + 1)) + collection_ids = list(range(1, NUM_COLLECTIONS + 1)) + acquisition_type_ids = list(range(1, NUM_ACQUISITION_TYPES + 1)) + exhibit_ids = list(range(1, NUM_EXHIBITS + 1)) + item_ids = list(range(1, NUM_ITEMS + 1)) -with open(sql_file, "w") as f: - f.write('SET search_path="Museum Exhibits";\n\n') + # Generate tables + tables = { + "Locations": generate_locations(), + "Collections": generate_collections(), + "Acquisition Types": generate_acquisition_types(), + "Exhibits": generate_exhibits(location_ids), + "Items": generate_items(acquisition_type_ids, collection_ids, exhibit_ids), # Generate Items first + } - for table_name, generator in tables.items(): - # Add quotes around table name since it contains spaces - f.write(f'COPY "{table_name}" FROM stdin;\n') - for row in generator: - cleaned_row = "\t".join(map(clean_value, row)) - f.write(f"{cleaned_row}\n") - f.write("\\.\n\n") + # Generate Item_Collections after Items + tables["Item_Collections"] = generate_item_collections(collection_ids, item_ids) -print(f"SQL file generated: {sql_file}") + # Write to SQL file + sql_file = get_output_file_path("generated_data.sql") + write_to_sql_file(sql_file, "Museum Exhibits", tables) diff --git a/beta_use_cases/museum_exhibits/generated_data.sql b/beta_use_cases/museum_exhibits/generated_data.sql index b0bcadc..f1b4bbe 100644 --- a/beta_use_cases/museum_exhibits/generated_data.sql +++ b/beta_use_cases/museum_exhibits/generated_data.sql @@ -1,102 +1,201 @@ SET search_path="Museum Exhibits"; COPY "Locations" FROM stdin; -1 Museum Location 1 3342 Thomas Walk Suite 970 Mitchellville, NM 52630 -2 Museum Location 2 312 Jared Ford Suite 913 South Nicholas, OH 72577 -3 Museum Location 3 59689 Brianna Flats Schmidtfort, MP 28349 -4 Museum Location 4 97612 Janice Isle Suite 251 Smithtown, AS 12945 -5 Museum Location 5 3120 Scott Rapid Apt. 766 North Rebeccaburgh, TN 32185 +1 Museum Location 1 96178 Shaw Station New Charles, SC 93243 +2 Museum Location 2 20618 Krystal Park Suite 943 North Cherylmouth, WI 08589 +3 Museum Location 3 9471 Cheryl Station Suite 488 Ramseyton, OR 12788 +4 Museum Location 4 705 Butler Causeway Suite 166 Port Anthony, MO 73573 +5 Museum Location 5 PSC 4330, Box 7699 APO AA 73204 \. COPY "Collections" FROM stdin; -1 Modern Paintings Collection Thousand whole last certainly leader dog. -2 Ancient Sculptures Collection Sign student would evidence detail short. -3 Historic Sculptures Collection World mother while across big feel. -4 Exquisite Paintings Collection More city difficult Republican ask play. -5 Rare Artifacts Collection Wife yes thing ball long camera that. -6 Exquisite Artifacts Collection Its road them significant serious. -7 Historic Artifacts Collection Affect own vote article really above fast. -8 Exquisite Relics Collection Strong next water involve perform. -9 Ancient Art Collection Ground apply feeling wrong benefit sell. -10 Historic Vases Collection Radio south study goal much. +1 20Th Century Cubist Collection Raise real yet Mrs. Decision thus shake least. +2 Renaissance Baroque Collection Several travel determine decade son. +3 20Th Century Impressionist Collection Yet national nice. +4 20Th Century Abstract Collection Get continue television. From result late likely. +5 Renaissance Baroque Collection College least apply put direction poor hospital. +6 19Th Century Modernist Collection Country myself bit start anyone. Bed head edge. +7 20Th Century Cubist Collection Wife administration leg garden glass Congress. +8 Ancient Cubist Collection Fall hundred candidate peace sea record pattern. +9 19Th Century Impressionist Collection Unit single interesting than. +10 Renaissance Baroque Collection Green phone fish reality. \. COPY "Acquisition Types" FROM stdin; -1 Donation However market few citizen deep measure senior. -2 Purchase Near consider police. -3 Bequest Even then sing continue machine. -4 Loan Lawyer both boy body water pick itself despite. -5 Exchange Contain ten stay analysis word military attorney. +1 Donation Upon candidate center baby. +2 Purchase Individual feel the particular. +3 Bequest Great lawyer main heavy pick. +4 Loan Task father attorney. +5 Exchange Take check teacher talk again. \. COPY "Exhibits" FROM stdin; -1 Exquisite Sculptures Exhibit 2025-01-01 2025-02-02 2 True Such give grow drive their character. Mouth scene measure modern deep ability free. -2 Historic Manuscripts Exhibit 2025-01-07 2025-05-17 5 True Threat eye up line front worker red response. Fight option study fear director role show. -3 Modern Relics Exhibit 2025-01-03 2025-04-25 2 True Thank while need half. Score he thank southern community whole. Small art knowledge crime. -4 Exquisite Vases Exhibit 2025-01-07 \N 1 True Fire occur garden ago feel wide. Stage them instead lose college discuss reveal. -5 Modern Art Exhibit 2025-01-01 2025-04-16 4 True Large huge could research happy responsibility car. -6 Ancient Paintings Exhibit 2025-01-02 2025-01-07 2 True Study human view sometimes. School card what. -7 Historic Art Exhibit 2025-01-08 2025-01-30 4 True Idea prevent my indeed this yet. Big audience today popular media. -8 Rare Vases Exhibit 2025-01-02 2025-03-05 2 True Fall require activity science ability. Music term red. -9 Rare Artifacts Exhibit 2025-01-05 2025-04-21 2 True For doctor point. Tough serve grow water avoid determine. Increase near glass southern fly. -10 Historic Paintings Exhibit 2025-01-08 2025-06-02 4 False Simply skin billion clearly option table determine. Detail whole to later type short. -11 Historic Manuscripts Exhibit 2025-01-06 2025-06-22 4 False Return help deep me family. Plan response send contain subject. -12 Rare Paintings Exhibit 2025-01-08 2025-01-31 2 True Cultural even run yes with. -13 Exquisite Relics Exhibit 2025-01-03 2025-01-19 4 False Loss any represent window now argue. Training into majority rise person respond thought. -14 Historic Artifacts Exhibit 2025-01-01 \N 4 True Reveal member which without everyone green fear. Beyond me view quality seem. -15 Ancient Art Exhibit 2025-01-04 2025-04-27 3 True Child best business conference system. Face us work. Detail note body during sure feel often bar. +1 The Evolution Of Butterfly Sketches 2025-01-11 2025-05-15 3 False Everything majority less. South crime open start. +2 Perspectives On The Home Manuscript 2025-01-11 2025-03-08 5 False Save for prepare group human some wind. Claim future base she. +3 The Evolution Of The Home Sculpture 2025-01-12 \N 1 False Professor from certain enter sound he suggest. +4 Liminal Retreat In Urban Landscapes Sculpture 2025-01-04 2025-07-04 2 True Color project world three public. +5 Reclaiming The Landscape Sculpture 2025-01-10 \N 3 True Approach including modern success whole. Unit market least yard life inside agreement. +6 Silence: Exploring Landscape Portrait Series 2025-01-02 2025-03-08 1 True Understand remain perhaps people hit since. Simple dog sister somebody. +7 Silence: Exploring Butterfly Vase 2025-01-04 2025-07-09 3 True From another talk quality nor minute option. East officer sort rock significant bank network. +8 Decontextualizing Isolation Sketches 2025-01-15 2025-05-22 1 False Able soon necessary upon color chair run. Which concern believe necessary. +9 Perspectives On The Home Artifact 2025-01-02 2025-05-02 5 False His bank human never newspaper discussion. Use itself wish. +10 Silence: Exploring The Home Pot 2025-01-06 2025-07-09 4 False Avoid meet ball study. +11 Perspectives On Bird Sculpture 2025-01-15 2025-07-06 2 True Open strong go. +12 Reclaiming The Urban Landscapes Portrait Series 2025-01-09 2025-04-19 5 False Though save cover indeed case hear write. Approach get a audience forward his wonder. +13 Perspectives On Urban Landscapes Vase 2025-01-01 2025-03-12 3 False Heart cause outside argue sort her sing. However board employee. +14 Reclaiming The Butterfly Portrait Series 2025-01-02 2025-05-05 1 True Information treat story food relationship rule. Ball recognize cold investment mind. +15 Perspectives On Butterfly Sculpture 2025-01-01 \N 4 True Of mean choice staff you. Cost price course series your. Expert size allow create. \. COPY "Items" FROM stdin; -1 Ancient Artifacts 7861969220297 2025-01-05 3 2 15 -2 Ancient Vases 8820074493636 2025-01-01 5 9 4 -3 Ancient Artifacts 2625026956919 2025-01-01 5 10 12 -4 Historic Bowls 8695494022694 2025-01-08 1 1 \N -5 Ancient Sculptures 8518672551345 2025-01-07 1 10 13 -6 Historic Paintings 3630925335627 2025-01-03 2 2 \N -7 Historic Paintings 8069955695053 2025-01-06 5 6 11 -8 Ancient Bowls 3505984304054 2025-01-03 4 7 \N -9 Exquisite Manuscripts 7526113518926 2025-01-06 2 4 10 -10 Historic Paintings 2125602538121 2025-01-02 1 1 2 -11 Modern Relics 4707548142853 2025-01-01 2 7 8 -12 Ancient Artifacts 1532720226921 2025-01-05 4 1 \N -13 Rare Bowls 8058055853272 2025-01-06 4 1 9 -14 Rare Relics 1999697582984 2025-01-04 5 10 \N -15 Modern Artifacts 0695161049687 2025-01-06 1 3 11 -16 Historic Sculptures 1846493212600 2025-01-04 2 5 \N -17 Exquisite Paintings 4074847383493 2025-01-05 5 2 8 -18 Rare Relics 8540721566970 2025-01-02 4 6 12 -19 Ancient Vases 0963776290450 2025-01-01 5 2 9 -20 Rare Sculptures 3833419958528 2025-01-05 5 8 1 -21 Rare Sculptures 0950298840362 2025-01-08 5 9 13 -22 Ancient Art 0818100697606 2025-01-03 1 2 \N -23 Exquisite Paintings 6690314200704 2025-01-07 3 2 10 -24 Exquisite Bowls 5606147743654 2025-01-06 2 5 11 -25 Modern Paintings 3813534602704 2025-01-06 2 9 \N -26 Rare Paintings 6721744958290 2025-01-07 5 10 2 -27 Historic Art 9485048537991 2025-01-07 3 8 11 -28 Historic Artifacts 9735752321639 2025-01-01 3 6 \N -29 Rare Manuscripts 0327009188684 2025-01-07 5 1 8 -30 Rare Paintings 3876964658185 2025-01-07 3 4 \N -31 Historic Paintings 3572359527899 2025-01-03 5 1 15 -32 Ancient Relics 6759492550756 2025-01-04 3 9 \N -33 Rare Relics 1140458904167 2025-01-07 5 10 3 -34 Exquisite Art 1465923516349 2025-01-05 5 1 14 -35 Historic Vases 3287812094565 2025-01-04 1 3 \N -36 Exquisite Sculptures 1662516081011 2025-01-05 4 7 \N -37 Exquisite Vases 7475174373947 2025-01-06 4 5 12 -38 Rare Paintings 2599395429414 2025-01-02 4 4 13 -39 Historic Art 3098942732209 2025-01-03 5 8 \N -40 Historic Art 6889724096872 2025-01-05 1 10 14 -41 Exquisite Artifacts 4031573040526 2025-01-08 3 10 9 -42 Rare Artifacts 5495800649989 2025-01-03 4 8 \N -43 Modern Manuscripts 5107602652059 2025-01-07 4 10 9 -44 Ancient Artifacts 3348770363312 2025-01-03 5 10 5 -45 Rare Art 9743628810767 2025-01-08 3 4 \N -46 Modern Artifacts 7318515963097 2025-01-07 4 6 \N -47 Exquisite Paintings 8794344557673 2025-01-06 5 5 \N -48 Historic Sculptures 7775970437400 2025-01-06 2 10 \N -49 Ancient Artifacts 2825435332964 2025-01-04 1 7 \N -50 Modern Artifacts 4624956992552 2025-01-04 3 8 \N +1 Bronze vase 1862193314406 2025-01-05 3 \N +2 Landscape weathered baroque portrait series 9535874743896 2025-01-05 3 6 +3 Baroque asian bronze vase 4800379049251 2025-01-06 1 \N +4 Philosophical medieval egyptian manuscript 6379059931400 2025-01-09 3 7 +5 Baroque asian vase 9933923670481 2025-01-08 5 5 +6 Polished asian painting 0144935136233 2025-01-06 3 2 +7 Weathered greek modernist portrait series 7268749722130 2025-01-09 5 7 +8 Baroque medieval european bowl and plate 2275542342859 2025-01-02 5 \N +9 Isolation weathered modernist butterfly drawing 5388368939145 2025-01-11 2 \N +10 Baroque roman pen bowl and plate 6414803832043 2025-01-01 4 14 +11 Impressionist portrait series 0029966751542 2025-01-03 1 \N +12 Philosophical 20th century manuscript 6586913183244 2025-01-10 4 11 +13 The home weathered egyptian landscape drawing 1516486210936 2025-01-04 3 \N +14 The home cubist medieval european clay bowl and plate 3938295034066 2025-01-13 4 \N +15 Ancient artifact 7984733976401 2025-01-14 3 \N +16 Rough painting 9480842383768 2025-01-13 2 \N +17 Literary 20th century roman manuscript 8075779896132 2025-01-09 4 12 +18 Rough greek impressionist portrait series 0003882794715 2025-01-07 3 13 +19 Polished egyptian baroque portrait series 1933437898119 2025-01-11 4 14 +20 Landscape roman modernist landscape drawing 4354869487967 2025-01-07 3 15 +21 Clay sculpture 6001343163166 2025-01-15 4 12 +22 Baroque sculpture 8196716726333 2025-01-12 5 11 +23 Landscape medieval european sculpture 9074954082819 2025-01-14 5 \N +24 Rough medieval european cubist portrait series 4886834291786 2025-01-05 5 \N +25 Urban landscapes roman pot 2692004359925 2025-01-02 2 13 +26 Landscape charcoal artifact 2383142547670 2025-01-01 4 12 +27 Weathered cubist portrait series 9540678654064 2025-01-14 3 15 +28 The home egyptian pot 7154717552949 2025-01-12 1 \N +29 The home cubist pot 2218589397260 2025-01-13 3 3 +30 Isolation smooth medieval european modernist painting 3499690477112 2025-01-14 1 12 +31 Landscape medieval european impressionist painting 5221016819300 2025-01-12 1 \N +32 Abstract roman vase 9188117485055 2025-01-15 2 5 +33 Urban landscapes scientific polished asian baroque sketches 2375255060412 2025-01-04 4 \N +34 The home 19th century artifact 9567257514625 2025-01-02 4 12 +35 Medieval european painting 0424561107495 2025-01-09 1 7 +36 Bird abstract asian charcoal vase 1020427654432 2025-01-02 1 6 +37 Bird cubist roman marble bowl and plate 5633822694994 2025-01-11 1 \N +38 Baroque pot 6399700317773 2025-01-10 3 \N +39 Cubist egyptian pot 1939386479751 2025-01-01 3 \N +40 Scientific 19th century asian manuscript 5012449018725 2025-01-02 2 12 +41 Egyptian baroque wood sculpture 3923255267295 2025-01-05 2 \N +42 Asian impressionist portrait series 1364389777274 2025-01-15 1 13 +43 Ancient greek artifact 6729603764594 2025-01-14 3 \N +44 Religious ancient roman manuscript 7533693134972 2025-01-06 2 \N +45 Bird weathered greek baroque painting 6604095702333 2025-01-02 1 10 +46 Bird cubist pot 1439136675278 2025-01-02 3 2 +47 Medieval european vase 8903998365378 2025-01-14 5 \N +48 Bird roman bowl and plate 6659922405512 2025-01-08 4 \N +49 19th century asian artifact 1103195281151 2025-01-09 1 7 +50 Landscape marble artifact 5726845015164 2025-01-03 1 13 +\. + +COPY "Item_Collections" FROM stdin; +1 6 +1 4 +1 3 +2 10 +2 6 +3 4 +4 5 +5 9 +5 10 +5 5 +6 9 +6 1 +6 3 +7 6 +7 8 +7 2 +8 6 +9 1 +10 7 +10 2 +11 2 +11 7 +11 10 +12 1 +12 5 +13 5 +13 4 +14 1 +14 7 +15 1 +15 4 +16 4 +16 6 +16 7 +17 6 +17 4 +17 8 +18 5 +19 5 +20 2 +20 10 +20 7 +21 9 +22 10 +23 1 +23 9 +24 7 +25 10 +25 2 +25 6 +26 4 +27 9 +28 9 +29 8 +29 3 +29 1 +30 7 +31 2 +31 7 +32 7 +32 1 +33 2 +33 4 +34 7 +34 9 +35 1 +35 3 +36 5 +37 7 +38 3 +39 8 +40 1 +41 2 +41 4 +41 6 +42 8 +42 10 +43 4 +43 6 +44 3 +44 5 +44 6 +45 5 +45 2 +45 3 +46 6 +46 4 +47 2 +47 1 +47 10 +48 7 +48 2 +49 5 +50 4 +50 9 +50 7 \. diff --git a/beta_use_cases/museum_exhibits/requirements.txt b/beta_use_cases/museum_exhibits/requirements.txt deleted file mode 100644 index 62e3730..0000000 --- a/beta_use_cases/museum_exhibits/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -faker -faker-commerce diff --git a/beta_use_cases/museum_exhibits/schema.sql b/beta_use_cases/museum_exhibits/schema.sql index 879809b..739e4f1 100644 --- a/beta_use_cases/museum_exhibits/schema.sql +++ b/beta_use_cases/museum_exhibits/schema.sql @@ -2,41 +2,45 @@ DROP SCHEMA IF EXISTS "Museum Exhibits" CASCADE; CREATE SCHEMA "Museum Exhibits"; SET search_path = "Museum Exhibits"; +CREATE TABLE "Acquisition Types" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + type_name TEXT NOT NULL UNIQUE, + description TEXT +); -create table "Acquisition Types" ( - id bigint primary key generated always as identity, - type_name text not null unique, - description text +CREATE TABLE "Collections" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + description TEXT ); -create table "Collections" ( - id bigint primary key generated always as identity, - name text not null, - description text +CREATE TABLE "Locations" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + address TEXT ); -create table "Locations" ( - id bigint primary key generated always as identity, - name text not null, - address text +CREATE TABLE "Exhibits" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + start_date DATE NOT NULL, + end_date DATE, + location_id BIGINT NOT NULL REFERENCES "Locations" (id), + featured BOOLEAN DEFAULT FALSE, + description TEXT ); -create table "Exhibits" ( - id bigint primary key generated always as identity, - name text not null, - start_date date not null, - end_date date, - location_id bigint not null references "Locations" (id), - featured boolean default false, - description text +CREATE TABLE "Items" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + serial_number TEXT NOT NULL UNIQUE, + acquisition_date DATE NOT NULL, + acquisition_type_id BIGINT NOT NULL REFERENCES "Acquisition Types" (id), + exhibit_id BIGINT REFERENCES "Exhibits" (id) ); -create table "Items" ( - id bigint primary key generated always as identity, - name text not null, - serial_number text not null unique, - acquisition_date date not null, - acquisition_type_id bigint not null references "Acquisition Types" (id), - collection_id bigint not null references "Collections" (id), - exhibit_id bigint references "Exhibits" (id) +CREATE TABLE "Item_Collections" ( + item_id BIGINT NOT NULL REFERENCES "Items" (id) ON DELETE CASCADE, + collection_id BIGINT NOT NULL REFERENCES "Collections" (id) ON DELETE CASCADE, + PRIMARY KEY (item_id, collection_id) ); diff --git a/beta_use_cases/nonprofit_grant_tracking/README.md b/beta_use_cases/nonprofit_grant_tracking/README.md new file mode 100644 index 0000000..a276cb7 --- /dev/null +++ b/beta_use_cases/nonprofit_grant_tracking/README.md @@ -0,0 +1,56 @@ +# Nonprofit Grant Tracking Sample Data + +This sample dataset represents a nonprofit organization tracking grants, their lifecycle stages, staff involvement, and fund allocations. + +```mermaid +%% https://mermaid.js.org/syntax/entityRelationshipDiagram.html + +erDiagram + "Grants" { + BIGINT id PK + string name + string description + NUMERIC amount + DATE start_date + DATE end_date + string status + } + + "Staff" { + BIGINT id PK + string name + string position + } + + "Lifecycle Stages" { + BIGINT id PK + string name + string description + } + + "Grant Lifecycle" { + BIGINT id PK + BIGINT grant_id FK + BIGINT stage_id FK + BIGINT staff_id FK + TIMESTAMP signed_off_at + int stage_order + string notes + string role + } + + "Grant Allocations" { + BIGINT id PK + BIGINT grant_id FK + string category + NUMERIC allocated_amount + NUMERIC spent_amount + } + +%% Relationships +%% See: https://mermaid.js.org/syntax/entityRelationshipDiagram.html#relationship-syntax + "Grant Lifecycle" ||--|| "Grants" : "grant_id" + "Grant Lifecycle" ||--|| "Lifecycle Stages" : "stage_id" + "Grant Lifecycle" ||--|| "Staff" : "staff_id" + "Grant Allocations" ||--|| "Grants" : "grant_id" +``` diff --git a/beta_use_cases/nonprofit_grant_tracking/generate_data.py b/beta_use_cases/nonprofit_grant_tracking/generate_data.py new file mode 100644 index 0000000..17c112d --- /dev/null +++ b/beta_use_cases/nonprofit_grant_tracking/generate_data.py @@ -0,0 +1,118 @@ +import os +import random +from datetime import datetime +from faker import Faker + +fake = Faker() + +def clean_value(value): + """Clean a value for SQL COPY operations.""" + if value is None: + return r"\N" + if isinstance(value, str): + return value.replace("\t", " ").replace("\n", " ") + return str(value) + +def write_to_sql_file(output_path, search_path, tables): + """Write the generated data to an SQL file.""" + with open(output_path, "w") as f: + f.write(f'SET search_path="{search_path}";\n\n') + for table_name, generator in tables.items(): + f.write(f'COPY "{table_name}" FROM stdin;\n') + for row in generator: + cleaned_row = "\t".join(map(clean_value, row)) + f.write(f"{cleaned_row}\n") + f.write("\\.\n\n") + print(f"SQL file generated: {output_path}") + +def get_output_file_path(filename): + """Get the output file path relative to the current script's directory.""" + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_file_dir, filename) + +# Constants +NUM_GRANTS = 20 +NUM_STAFF = 15 +NUM_LIFECYCLE_STAGES = 6 +NUM_GRANT_LIFECYCLE_ENTRIES = 50 +NUM_ALLOCATIONS = 40 + +LIFECYCLE_STAGES = [ + "Application Submitted", + "Review Process", + "Approved", + "Fund Disbursed", + "Implementation", + "Final Report", +] + +# Table Data Generation +def generate_grants(): + stock_phrases = [ + "Literacy Program", + "Time to Read", + "Building Readers", + "Read and Succeed", + "Community Reading Initiative", + ] + for i in range(1, NUM_GRANTS + 1): + name = f"{fake.city()} {random.choice(stock_phrases)}" + yield [ + i, + name, + fake.paragraph(), + round(random.uniform(1000, 50000), 2), + fake.date_this_decade(), + fake.date_this_decade(), + ] + +def generate_staff(): + for i in range(1, NUM_STAFF + 1): + yield [ + i, + fake.name(), + random.choice(["Program Manager", "Coordinator", "Administrator", "Finance Officer"]), + ] + +def generate_lifecycle_stages(): + for i, stage in enumerate(LIFECYCLE_STAGES, start=1): + yield [i, stage, fake.paragraph()] + +def generate_grant_lifecycle(): + lifecycle_entries = set() + for i in range(1, NUM_GRANT_LIFECYCLE_ENTRIES + 1): + grant_id = random.randint(1, NUM_GRANTS) + stage_id = random.randint(1, NUM_LIFECYCLE_STAGES) + staff_id = random.randint(1, NUM_STAFF) + if (grant_id, stage_id) not in lifecycle_entries: + lifecycle_entries.add((grant_id, stage_id)) + yield [ + i, + grant_id, + stage_id, + staff_id, + fake.date_time_this_year(), + ] + + +def generate_allocations(): + for i in range(1, NUM_ALLOCATIONS + 1): + yield [ + i, + random.randint(1, NUM_GRANTS), + round(random.uniform(500, 5000), 2), # Allocated amount + round(random.uniform(0, 5000), 2), # Spent amount + ] + +# Main Script +if __name__ == "__main__": + tables = { + "Grants": generate_grants(), + "Staff": generate_staff(), + "Lifecycle Stages": generate_lifecycle_stages(), + "Grant Lifecycle": generate_grant_lifecycle(), + "Grant Allocations": generate_allocations(), + } + + sql_file = get_output_file_path("generated_data.sql") + write_to_sql_file(sql_file, "Nonprofit Grant Tracking", tables) diff --git a/beta_use_cases/nonprofit_grant_tracking/generated_data.sql b/beta_use_cases/nonprofit_grant_tracking/generated_data.sql new file mode 100644 index 0000000..9682111 --- /dev/null +++ b/beta_use_cases/nonprofit_grant_tracking/generated_data.sql @@ -0,0 +1,141 @@ +SET search_path="Nonprofit Grant Tracking"; + +COPY "Grants" FROM stdin; +1 Mirandafurt Time to Read Ever discover lay success. Popular yard site physical. Media push able foot us tend. 20560.71 2020-06-16 2024-09-22 +2 South Paigefort Literacy Program Rich lot determine worry story nor prove. Defense into these themselves improve base senior true. 38957.42 2022-05-19 2022-07-29 +3 Calvinmouth Literacy Program Century difference help seat. 36293.85 2023-02-23 2024-06-08 +4 East Shawn Time to Read International ability until friend interest. Ahead detail nation girl. 40919.9 2023-08-02 2022-07-09 +5 Emilyburgh Building Readers Such white movie instead happy why. Knowledge base leave trial. Paper director style might value development. 35555.2 2020-07-20 2024-09-18 +6 Moniquefurt Building Readers Wait community every least. Sit audience leave against center Republican. 7439.65 2021-07-20 2020-07-26 +7 North Madisonside Building Readers Station article stop throughout pass section. Military huge state seek serve opportunity modern. 27374.36 2024-02-05 2022-12-04 +8 Ericland Building Readers Wish discover happy your seem ago. Word rest federal network. 4744.98 2022-07-13 2024-01-17 +9 South Kristen Community Reading Initiative Join candidate site near none shake join. Data never air. Up management bag trip yeah. 8185.23 2024-08-10 2022-03-09 +10 Barrymouth Time to Read Ever challenge production challenge difficult same dinner. Close face form. Several window top discussion. 47852.93 2021-09-24 2022-02-20 +11 West Shawnstad Time to Read Science worry responsibility wide. System nor care once south meet. 45127.51 2021-10-02 2021-08-31 +12 Kingland Read and Succeed Worker kitchen evidence carry author. 20604.19 2021-05-08 2022-01-22 +13 Moorebury Literacy Program Old course born particularly either future. Short attorney direction point. 12723.6 2022-06-08 2020-03-10 +14 Kimberlyport Literacy Program Scientist few have much dinner church. Professor realize into. State because difference no gun health. 7436.28 2022-01-14 2024-08-28 +15 Yoderton Building Readers Mrs happy show could floor. Away term check. 15972.76 2022-07-09 2022-09-09 +16 West James Time to Read American money even with. Yes mother deep mission whom business. Increase property gun free field door. 25983.05 2020-03-31 2024-08-02 +17 Fowlerfurt Read and Succeed Place know dinner war become whom check. Article forward focus audience. Already man still music free cold. 24207.67 2021-06-24 2021-12-15 +18 Garzabury Read and Succeed Loss team main figure hit can. Begin senior rich about ten yet your. Just store arm herself both mention bed. Wish argue job sister. 21446.09 2024-03-07 2022-11-15 +19 Holtview Community Reading Initiative Station old place determine race. Responsibility history eat some two. Former pass glass adult social foreign myself. 31677.06 2021-02-25 2021-12-25 +20 Lake Rebeccafurt Community Reading Initiative Wait enough enjoy seem war tonight least. Discover water resource against everything region. Throughout detail listen generation do capital single. 24176.07 2020-10-25 2020-02-15 +\. + +COPY "Staff" FROM stdin; +1 Sarah Branch Coordinator +2 James Cuevas Administrator +3 Victoria Costa Coordinator +4 Brian Clark Administrator +5 David Peters Finance Officer +6 Christina Price Coordinator +7 Beth Shannon Administrator +8 Marcus Cox Program Manager +9 Mrs. Janet Mays Administrator +10 Sarah Odom Program Manager +11 Ellen Murphy Program Manager +12 Kelly Russell Administrator +13 Emma Shaw Finance Officer +14 Renee Fox Administrator +15 Timothy Martinez Finance Officer +\. + +COPY "Lifecycle Stages" FROM stdin; +1 Application Submitted Career check better news artist power official. Art southern drop probably fast catch. Power agency red successful down give free teach. +2 Review Process Natural last campaign call assume wind law. Everything office recently without law issue while. +3 Approved Series commercial American money company. Catch success city laugh sister else. +4 Fund Disbursed Ahead white treat small hundred drop after. Attack back fight tend shake. Lose many inside tree. +5 Implementation Walk wide serious major wish yeah should. Compare approach read from drive. +6 Final Report Often choice admit but. Likely south girl minute. +\. + +COPY "Grant Lifecycle" FROM stdin; +1 17 3 8 2025-01-01 23:01:09.302967 +2 2 2 4 2025-01-05 04:37:25.748302 +3 9 1 9 2025-01-08 02:20:05.010619 +4 15 2 3 2025-01-03 08:51:58.382561 +5 13 2 3 2025-01-05 21:03:30.384342 +6 8 2 12 2025-01-16 01:02:23.357373 +7 19 4 4 2025-01-05 04:32:06.337236 +8 19 3 11 2025-01-13 04:25:54.491900 +9 3 3 1 2025-01-12 01:28:36.476066 +10 18 2 3 2025-01-11 00:03:34.332647 +13 12 6 10 2025-01-01 16:04:40.429566 +14 18 4 12 2025-01-04 04:44:02.778167 +15 10 3 6 2025-01-12 08:43:00.378012 +16 2 6 4 2025-01-05 19:51:32.403716 +17 18 3 10 2025-01-16 00:22:19.465580 +18 16 2 10 2025-01-09 07:27:56.594906 +19 3 6 14 2025-01-03 14:04:21.093857 +20 4 6 6 2025-01-12 17:24:48.507572 +21 16 5 3 2025-01-02 08:08:19.705695 +22 15 5 1 2025-01-02 01:01:36.816840 +23 12 2 1 2025-01-03 01:56:27.607091 +25 1 1 4 2025-01-03 08:31:36.603785 +27 9 5 4 2025-01-03 15:44:15.496038 +28 8 4 4 2025-01-05 20:58:22.970851 +29 1 2 12 2025-01-02 15:59:56.479601 +30 17 5 14 2025-01-12 23:18:34.977835 +31 3 5 1 2025-01-15 23:15:01.088765 +32 14 6 3 2025-01-14 18:55:42.485923 +33 10 1 4 2025-01-12 14:40:14.505989 +34 19 1 3 2025-01-04 15:02:11.476928 +35 1 5 12 2025-01-11 12:47:16.679809 +36 10 6 13 2025-01-13 19:31:04.269175 +37 20 6 13 2025-01-12 04:42:14.324461 +39 8 6 7 2025-01-08 08:21:55.603816 +40 3 2 15 2025-01-05 10:33:43.935986 +42 6 2 13 2025-01-09 12:34:03.728249 +43 13 5 4 2025-01-13 10:35:36.171886 +44 1 4 1 2025-01-02 21:44:01.380399 +46 13 4 10 2025-01-14 10:08:24.603706 +47 10 5 15 2025-01-09 23:51:24.157053 +48 11 6 4 2025-01-01 13:20:44.106455 +49 17 2 15 2025-01-03 04:59:09.806400 +50 6 1 15 2025-01-04 15:10:18.177152 +\. + +COPY "Grant Allocations" FROM stdin; +1 14 1924.67 3641.29 +2 6 2473.16 104.72 +3 13 3817.23 2404.98 +4 20 1361.09 3137.25 +5 20 1096.94 1493.9 +6 3 2521.53 3811.58 +7 13 3104.41 2965.79 +8 10 2525.86 4488.7 +9 13 3048.99 3507.54 +10 7 4479.25 41.89 +11 9 1823.97 806.5 +12 8 2355.36 3418.43 +13 15 1362.66 331.03 +14 11 4592.34 1387.3 +15 19 2734.38 2303.21 +16 14 805.47 3123.89 +17 6 1263.36 4151.05 +18 16 2396.67 22.43 +19 9 1387.45 2833.53 +20 11 3392.78 3781.49 +21 12 2459.42 756.17 +22 7 1207.52 4768.76 +23 2 3735.55 2242.26 +24 5 2963.03 1080.31 +25 13 1053.23 306.83 +26 2 944.89 2397.22 +27 12 1262.85 1476.51 +28 9 3881.34 3624.98 +29 11 2766.7 4755.4 +30 1 4031.01 968.46 +31 10 4752.34 3674.41 +32 9 3542.73 4667.71 +33 9 4985.84 2229.88 +34 5 1108.8 1637.5 +35 4 811.14 1024.91 +36 14 1038.06 4343.46 +37 9 4817.11 4700.2 +38 19 2031.59 4446.95 +39 12 1753.27 1965.34 +40 5 3326.81 1000.13 +\. + diff --git a/beta_use_cases/nonprofit_grant_tracking/schema.sql b/beta_use_cases/nonprofit_grant_tracking/schema.sql new file mode 100644 index 0000000..d8f416c --- /dev/null +++ b/beta_use_cases/nonprofit_grant_tracking/schema.sql @@ -0,0 +1,39 @@ +DROP SCHEMA IF EXISTS "Nonprofit Grant Tracking" CASCADE; +CREATE SCHEMA "Nonprofit Grant Tracking"; +SET search_path = "Nonprofit Grant Tracking"; + +CREATE TABLE "Grants" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + description TEXT, + amount NUMERIC(12, 2) NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL +); + +CREATE TABLE "Staff" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + position TEXT +); + +CREATE TABLE "Lifecycle Stages" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + name TEXT NOT NULL, + description TEXT +); + +CREATE TABLE "Grant Lifecycle" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + grant_id BIGINT NOT NULL REFERENCES "Grants" (id) ON DELETE CASCADE, + stage_id BIGINT NOT NULL REFERENCES "Lifecycle Stages" (id) ON DELETE CASCADE, + staff_id BIGINT NOT NULL REFERENCES "Staff" (id) ON DELETE SET NULL, + signed_off_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() +); + +CREATE TABLE "Grant Allocations" ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + grant_id BIGINT NOT NULL REFERENCES "Grants" (id) ON DELETE CASCADE, + allocated_amount NUMERIC(12, 2) NOT NULL, + spent_amount NUMERIC(12, 2) DEFAULT 0 +); diff --git a/beta_use_cases/hardware_store/requirements.txt b/beta_use_cases/requirements.txt similarity index 100% rename from beta_use_cases/hardware_store/requirements.txt rename to beta_use_cases/requirements.txt