-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdb.py
More file actions
319 lines (284 loc) · 12.1 KB
/
db.py
File metadata and controls
319 lines (284 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
import ast
import time
from datetime import datetime
import numpy as np
import pandas as pd
from sqlalchemy import Engine, create_engine
from sqlalchemy.sql import text
from constants import DATA_FOLDER
# Create an SQLite database
DB_ENGINE = create_engine("sqlite:///munder_difflin.db")
# list containing the different kinds of papers
paper_supplies = [
# Paper Types (priced per sheet unless specified)
{"item_name": "A4 paper", "category": "paper", "unit_price": 0.05},
{"item_name": "Letter-sized paper", "category": "paper", "unit_price": 0.06},
{"item_name": "Cardstock", "category": "paper", "unit_price": 0.15},
{"item_name": "Colored paper", "category": "paper", "unit_price": 0.10},
{"item_name": "Glossy paper", "category": "paper", "unit_price": 0.20},
{"item_name": "Matte paper", "category": "paper", "unit_price": 0.18},
{"item_name": "Recycled paper", "category": "paper", "unit_price": 0.08},
{"item_name": "Eco-friendly paper", "category": "paper", "unit_price": 0.12},
{"item_name": "Poster paper", "category": "paper", "unit_price": 0.25},
{"item_name": "Banner paper", "category": "paper", "unit_price": 0.30},
{"item_name": "Kraft paper", "category": "paper", "unit_price": 0.10},
{"item_name": "Construction paper", "category": "paper", "unit_price": 0.07},
{"item_name": "Wrapping paper", "category": "paper", "unit_price": 0.15},
{"item_name": "Glitter paper", "category": "paper", "unit_price": 0.22},
{"item_name": "Decorative paper", "category": "paper", "unit_price": 0.18},
{"item_name": "Letterhead paper", "category": "paper", "unit_price": 0.12},
{"item_name": "Legal-size paper", "category": "paper", "unit_price": 0.08},
{"item_name": "Crepe paper", "category": "paper", "unit_price": 0.05},
{"item_name": "Photo paper", "category": "paper", "unit_price": 0.25},
{"item_name": "Uncoated paper", "category": "paper", "unit_price": 0.06},
{"item_name": "Butcher paper", "category": "paper", "unit_price": 0.10},
{"item_name": "Heavyweight paper", "category": "paper", "unit_price": 0.20},
{"item_name": "Standard copy paper", "category": "paper", "unit_price": 0.04},
{"item_name": "Bright-colored paper", "category": "paper", "unit_price": 0.12},
{"item_name": "Patterned paper", "category": "paper", "unit_price": 0.15},
# Product Types (priced per unit)
{
"item_name": "Paper plates",
"category": "product",
"unit_price": 0.10,
}, # per plate
{"item_name": "Paper cups", "category": "product", "unit_price": 0.08}, # per cup
{
"item_name": "Paper napkins",
"category": "product",
"unit_price": 0.02,
}, # per napkin
{
"item_name": "Disposable cups",
"category": "product",
"unit_price": 0.10,
}, # per cup
{
"item_name": "Table covers",
"category": "product",
"unit_price": 1.50,
}, # per cover
{
"item_name": "Envelopes",
"category": "product",
"unit_price": 0.05,
}, # per envelope
{
"item_name": "Sticky notes",
"category": "product",
"unit_price": 0.03,
}, # per sheet
{"item_name": "Notepads", "category": "product", "unit_price": 2.00}, # per pad
{
"item_name": "Invitation cards",
"category": "product",
"unit_price": 0.50,
}, # per card
{"item_name": "Flyers", "category": "product", "unit_price": 0.15}, # per flyer
{
"item_name": "Party streamers",
"category": "product",
"unit_price": 0.05,
}, # per roll
{
"item_name": "Decorative adhesive tape (washi tape)",
"category": "product",
"unit_price": 0.20,
}, # per roll
{
"item_name": "Paper party bags",
"category": "product",
"unit_price": 0.25,
}, # per bag
{
"item_name": "Name tags with lanyards",
"category": "product",
"unit_price": 0.75,
}, # per tag
{
"item_name": "Presentation folders",
"category": "product",
"unit_price": 0.50,
}, # per folder
# Large-format items (priced per unit)
{
"item_name": "Large poster paper (24x36 inches)",
"category": "large_format",
"unit_price": 1.00,
},
{
"item_name": "Rolls of banner paper (36-inch width)",
"category": "large_format",
"unit_price": 2.50,
},
# Specialty papers
{"item_name": "100 lb cover stock", "category": "specialty", "unit_price": 0.50},
{"item_name": "80 lb text paper", "category": "specialty", "unit_price": 0.40},
{"item_name": "250 gsm cardstock", "category": "specialty", "unit_price": 0.30},
{"item_name": "220 gsm poster paper", "category": "specialty", "unit_price": 0.35},
]
# Given below are some utility functions you can use to implement your multi-agent system
def generate_sample_inventory(
paper_supplies: list, coverage: float = 0.4, seed: int = 137
) -> pd.DataFrame:
"""
Generate inventory for exactly a specified percentage of items from the full paper supply list.
This function randomly selects exactly `coverage` × N items from the `paper_supplies` list,
and assigns each selected item:
- a random stock quantity between 200 and 800,
- a minimum stock level between 50 and 150.
The random seed ensures reproducibility of selection and stock levels.
Args:
paper_supplies (list): A list of dictionaries, each representing a paper item with
keys 'item_name', 'category', and 'unit_price'.
coverage (float, optional): Fraction of items to include in the inventory (default is 0.4, or 40%).
seed (int, optional): Random seed for reproducibility (default is 137).
Returns:
pd.DataFrame: A DataFrame with the selected items and assigned inventory values, including:
- item_name
- category
- unit_price
- current_stock
- min_stock_level
"""
# Ensure reproducible random output
np.random.seed(seed)
# Calculate number of items to include based on coverage
num_items = int(len(paper_supplies) * coverage)
# Randomly select item indices without replacement
selected_indices = np.random.choice(
range(len(paper_supplies)), size=num_items, replace=False
)
# Extract selected items from paper_supplies list
selected_items = [paper_supplies[i] for i in selected_indices]
# Construct inventory records
inventory = []
for item in selected_items:
inventory.append(
{
"item_name": item["item_name"],
"category": item["category"],
"unit_price": item["unit_price"],
"current_stock": np.random.randint(200, 800), # Realistic stock range
"min_stock_level": np.random.randint(
50, 150
), # Reasonable threshold for reordering
}
)
# Return inventory as a pandas DataFrame
return pd.DataFrame(inventory)
def init_database(DB_ENGINE: Engine, seed: int = 137) -> Engine:
"""
Set up the Munder Difflin database with all required tables and initial records.
This function performs the following tasks:
- Creates the 'transactions' table for logging stock orders and sales
- Loads customer inquiries from 'quote_requests.csv' into a 'quote_requests' table
- Loads previous quotes from 'quotes.csv' into a 'quotes' table, extracting useful metadata
- Generates a random subset of paper inventory using `generate_sample_inventory`
- Inserts initial financial records including available cash and starting stock levels
Args:
DB_ENGINE (Engine): A SQLAlchemy engine connected to the SQLite database.
seed (int, optional): A random seed used to control reproducibility of inventory stock levels.
Default is 137.
Returns:
Engine: The same SQLAlchemy engine, after initializing all necessary tables and records.
Raises:
Exception: If an error occurs during setup, the exception is printed and raised.
"""
try:
# ----------------------------
# 1. Create an empty 'transactions' table schema
# ----------------------------
transactions_schema = pd.DataFrame(
{
"id": [],
"item_name": [],
"transaction_type": [], # 'stock_orders' or 'sales'
"units": [], # Quantity involved
"price": [], # Total price for the transaction
"transaction_date": [], # ISO-formatted date
}
)
transactions_schema.to_sql(
"transactions", DB_ENGINE, if_exists="replace", index=False
)
# Set a consistent starting date
initial_date = datetime(2025, 1, 1).isoformat()
# ----------------------------
# 2. Load and initialize 'quote_requests' table
# ----------------------------
quote_requests_df = pd.read_csv(f"{DATA_FOLDER}/quote_requests.csv")
quote_requests_df["id"] = range(1, len(quote_requests_df) + 1)
quote_requests_df.to_sql(
"quote_requests", DB_ENGINE, if_exists="replace", index=False
)
# ----------------------------
# 3. Load and transform 'quotes' table
# ----------------------------
quotes_df = pd.read_csv(f"{DATA_FOLDER}/quotes.csv")
quotes_df["request_id"] = range(1, len(quotes_df) + 1)
quotes_df["order_date"] = initial_date
# Unpack metadata fields (job_type, order_size, event_type) if present
if "request_metadata" in quotes_df.columns:
quotes_df["request_metadata"] = quotes_df["request_metadata"].apply(
lambda x: ast.literal_eval(x) if isinstance(x, str) else x
)
quotes_df["job_type"] = quotes_df["request_metadata"].apply(
lambda x: x.get("job_type", "")
)
quotes_df["order_size"] = quotes_df["request_metadata"].apply(
lambda x: x.get("order_size", "")
)
quotes_df["event_type"] = quotes_df["request_metadata"].apply(
lambda x: x.get("event_type", "")
)
# Retain only relevant columns
quotes_df = quotes_df[
[
"request_id",
"total_amount",
"quote_explanation",
"order_date",
"job_type",
"order_size",
"event_type",
]
]
quotes_df.to_sql("quotes", DB_ENGINE, if_exists="replace", index=False)
# ----------------------------
# 4. Generate inventory and seed stock
# ----------------------------
inventory_df = generate_sample_inventory(paper_supplies, seed=seed)
# Seed initial transactions
initial_transactions = []
# Add a starting cash balance via a dummy sales transaction
initial_transactions.append(
{
"item_name": None,
"transaction_type": "sales",
"units": None,
"price": 50000.0,
"transaction_date": initial_date,
}
)
# Add one stock order transaction per inventory item
for _, item in inventory_df.iterrows():
initial_transactions.append(
{
"item_name": item["item_name"],
"transaction_type": "stock_orders",
"units": item["current_stock"],
"price": item["current_stock"] * item["unit_price"],
"transaction_date": initial_date,
}
)
# Commit transactions to database
pd.DataFrame(initial_transactions).to_sql(
"transactions", DB_ENGINE, if_exists="append", index=False
)
# Save the inventory reference table
inventory_df.to_sql("inventory", DB_ENGINE, if_exists="replace", index=False)
return DB_ENGINE
except Exception as e:
print(f"Error initializing database: {e}")
raise