-
Notifications
You must be signed in to change notification settings - Fork 8
Hannah #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Hannah #5
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| transaction_id,product_name,category,price,quantity,customer_email,date,revenue,vat | ||
| 1,Laptop Pro,Electronics,999.99,2,alice@example.com,2024-03-15,1999.98,420.0 | ||
| 2,Wireless Mouse,Electronics,29.99,5,bob@company.com,2024-03-15,149.95,31.49 | ||
| 3,Usb Cable,Electronics,4.99,10,,2024-03-16,49.9,10.48 | ||
| 4,Office Chair,Furniture,349.5,1,charlie@work.org,2024-03-16,349.5,73.39 | ||
| 5,Standing Desk,Furniture,599.0,1,charlie@work.org,not_a_date,599.0,125.79 | ||
| 9,Webcam Hd,Electronics,54.99,1,,2024-03-18,54.99,11.55 | ||
| 10,Desk Lamp,Furniture,34.99,4,grace@university.edu,2024-03-19,139.96,29.39 | ||
| 11,Noise Cancelling Headphones,Electronics,199.99,1,alice@example.com,2024-03-19,199.99,42.0 | ||
| 12,Cable Management Kit,Furniture,15.99,6,henry@business.com,2024-03-20,95.94,20.15 | ||
| 13,Ergonomic Mouse Pad,Furniture,24.99,3,ivan@email.com,2024-03-20,74.97,15.74 | ||
| 14,Laptop Stand,Furniture,45.99,2,jenny@work.org,2024-03-21,91.98,19.32 | ||
| 15,Bluetooth Speaker,Unknown,39.99,1,karl@startup.io,2024-03-21,39.99,8.4 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,4 +30,19 @@ | |
| # Replace this stub with your dataclass: | ||
| @dataclass | ||
| class Transaction: | ||
| transaction_id: int # TODO: replace this stub with the full field list above | ||
| transaction_id: int | ||
| product_name: str # TODO: replace this stub with the full field list above | ||
| category: str | ||
| price: float | ||
| quantity: int | ||
| customer_email: str | ||
| date: str | ||
| revenue: float = 0.0 | ||
| vat: float = 0.0 | ||
|
|
||
| def __post_init__(self): | ||
| if (self.price <= 0): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the assignment says reject negative prices, kindly change to <0 |
||
| raise ValueError(f"{self.price} must be greater than zero ") | ||
|
|
||
| if (not self.product_name.strip()): | ||
| raise ValueError(f"{self.product_name} cannot be empty") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,13 +32,20 @@ | |
| def read_csv(path: str) -> list[dict]: | ||
| """Read a CSV file into a list of dicts. I/O only — no business rules.""" | ||
| # TODO: implement using csv.DictReader. | ||
| with open(path, mode='r', encoding='utf-8') as file: | ||
| reader = csv.DictReader(file) | ||
| return list(reader) | ||
| raise NotImplementedError | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove |
||
|
|
||
|
|
||
| def write_csv(rows: list[dict], path: str) -> None: | ||
| """Write a list of dicts to CSV. I/O only — no business rules.""" | ||
| # TODO: implement using csv.DictWriter. | ||
| raise NotImplementedError | ||
| with open(path, mode='w', encoding='utf-8', newline='') as file: | ||
| if rows: | ||
| writer = csv.DictWriter(file, fieldnames=rows[0].keys()) | ||
| writer.writeheader() | ||
| writer.writerows(rows) | ||
|
|
||
|
|
||
| def run() -> None: | ||
|
|
@@ -50,10 +57,16 @@ def run() -> None: | |
|
|
||
| # Materialise as Transaction instances so the dataclass __post_init__ | ||
| # acts as a final guard before serialisation. | ||
| # TODO: cast price / quantity / revenue / vat to the right types here | ||
| # if your transforms left them as strings, then iterate over `data` | ||
| # to build Transaction(**row) for each cleaned row. | ||
| transactions = [Transaction(**row) for row in data] | ||
| # The transforms layer returns cleaned dicts; iterate over them to build | ||
| # Transaction instances, which validates all fields in __post_init__. | ||
| transactions = [] | ||
| for row in data: | ||
| # Cast string fields to correct types before creating Transaction | ||
| row['price'] = float(row['price']) | ||
| row['quantity'] = int(row['quantity']) | ||
| row['revenue'] = float(row['revenue']) | ||
| row['vat'] = float(row['vat']) | ||
| transactions.append(Transaction(**row)) | ||
|
|
||
| # Output dir must exist. Use pathlib for cross-platform safety. | ||
| Path(OUTPUT_PATH).parent.mkdir(parents=True, exist_ok=True) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,8 +19,12 @@ def remove_invalid(rows: list[dict]) -> list[dict]: | |
| Empty here means missing, "", or whitespace-only. | ||
| """ | ||
| # TODO: implement. Return a new list, do not mutate `rows`. | ||
| raise NotImplementedError | ||
|
|
||
|
|
||
| return [ | ||
| row for row in rows | ||
| if row.get("product_name", "").strip() | ||
| and float(row.get("price", 0)) >= 0 | ||
| ] | ||
|
|
||
| def clean_fields(rows: list[dict]) -> list[dict]: | ||
| """Clean string fields: | ||
|
|
@@ -32,7 +36,15 @@ def clean_fields(rows: list[dict]) -> list[dict]: | |
| Return a new list. Do not mutate the input rows. | ||
| """ | ||
| # TODO: implement. | ||
| raise NotImplementedError | ||
| return [ | ||
| { | ||
| **row, # ✅ spread pattern - copies all existing fields | ||
|
Comment on lines
+40
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put |
||
| "product_name": row.get("product_name", "").strip().title(), | ||
| "customer_email": row.get("customer_email", "").strip().lower(), | ||
| "category": row.get("category", "").strip() or "Unknown", | ||
| } | ||
| for row in rows | ||
| ] | ||
|
|
||
|
|
||
| def calculate_revenue(rows: list[dict], vat_rate: float = 0.21) -> list[dict]: | ||
|
|
@@ -41,10 +53,20 @@ def calculate_revenue(rows: list[dict], vat_rate: float = 0.21) -> list[dict]: | |
| Round both to 2 decimal places. Coerce price/quantity from string if needed. | ||
| """ | ||
| # TODO: implement. | ||
| raise NotImplementedError | ||
|
|
||
| return [ | ||
| { | ||
| **row, | ||
| "revenue": round(float(row.get("price", 0)) * int(row.get("quantity", 0)), 2), | ||
| "vat": round(float(row.get("price", 0)) * int(row.get("quantity", 0)) * vat_rate, 2), | ||
| } | ||
| for row in rows | ||
| ] | ||
|
|
||
|
|
||
| def filter_zero_quantity(rows: list[dict]) -> list[dict]: | ||
| """Remove rows where quantity is 0.""" | ||
| # TODO: implement. | ||
| raise NotImplementedError | ||
| return [ | ||
| row for row in rows | ||
| if int(row.get("quantity", 0)) != 0 | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| https://sthyfstudentsdemo.blob.core.windows.net/week2-hannahwn/clean_sales.csv |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two lines load env vars before
_required()runs, and they useos.environ["INPUT_PATH"]which raisesKeyError(notValueError) if missing.You already have the correct solution on lines 43–44. Please delete lines 20–21 and 33–36, and keep only: