-
Notifications
You must be signed in to change notification settings - Fork 8
Mareh A. #6
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?
Mareh A. #6
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 |
|---|---|---|
|
|
@@ -2,23 +2,57 @@ | |
| import io | ||
| import logging | ||
| from pathlib import Path | ||
|
|
||
| import os | ||
| import shutil | ||
| import pandas as pd | ||
|
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. unused import, can remove |
||
| from dotenv import load_dotenv | ||
| from azure.identity import DefaultAzureCredential | ||
| from azure.storage.blob import BlobServiceClient | ||
|
|
||
| ACCOUNT_URL = "https://sthyfstudentsdemo.blob.core.windows.net" | ||
| SOURCE_CONTAINER = "week4-inputs" | ||
|
|
||
| PROJECT_ROOT = Path(__file__).resolve().parents[1] | ||
| load_dotenv(PROJECT_ROOT / ".env") | ||
| load_dotenv() | ||
|
|
||
| ACCOUNT_URL = os.getenv("ACCOUNT_URL") | ||
| SOURCE_CONTAINER = os.getenv("SOURCE_CONTAINER") | ||
| if not ACCOUNT_URL or not SOURCE_CONTAINER: | ||
| raise RuntimeError( | ||
| "Please set ACCOUNT_URL and SOURCE_CONTAINER environment variables before running." | ||
| ) | ||
|
Comment on lines
+17
to
+22
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. This stops the pipeline from running at all. These env-var lines (+ the raise) sit at the top level of ingest.py, so they run the moment the file is imported, and main.py imports ingest on line 1. So even with the download/upload calls commented out, python main.py crashes here before Tasks 2–6 run. Fix: move the check inside download_inputs() so it only runs when that function is called. Then importing the file is harmless and the rest of the pipeline runs without Azure set up. |
||
| FILES = ["messy_sales.csv", "messy_customers.csv"] | ||
|
|
||
|
|
||
| def download_inputs(data_dir: Path) -> None: | ||
| """Task 1: Download input CSV files from Azure Blob Storage.""" | ||
| # TODO: Create a BlobServiceClient using DefaultAzureCredential and ACCOUNT_URL. | ||
| # TODO: Get a container client for SOURCE_CONTAINER. | ||
| # TODO: For each filename in FILES, download the blob and write it to data_dir/<filename>. | ||
| # TODO: Log a message for each downloaded file. | ||
| raise NotImplementedError("Task 1: implement download_inputs") | ||
| credential = DefaultAzureCredential() | ||
| service = BlobServiceClient(account_url=ACCOUNT_URL, credential=credential) | ||
| container = service.get_container_client(SOURCE_CONTAINER) | ||
| if not container.exists(): | ||
| logging.info( | ||
| f"Container '{SOURCE_CONTAINER}' not found." | ||
| ) | ||
| data_dir.mkdir(parents=True, exist_ok=True) | ||
| for name in FILES: | ||
| blob = container.get_blob_client(name) | ||
| with open(data_dir / name, "wb") as f: | ||
| f.write(blob.download_blob().readall()) | ||
| logging.info("Downloaded %s", name) | ||
|
|
||
|
|
||
| def load_inputs_local(data_dir: Path) -> None: | ||
| """Because of the Fallback i implented this function to load data locally""" | ||
| data_dir.mkdir(exist_ok=True) | ||
| sample_data_dir = Path(__file__).resolve().parent.parent / "sample_data" | ||
|
|
||
| for name in FILES: | ||
| source_file = sample_data_dir / name | ||
| destination_file = data_dir / name | ||
| if source_file.exists(): | ||
| shutil.copy(source_file, destination_file) | ||
| logging.info("Successfully loaded %s from local sample_data", name) | ||
| else: | ||
| logging.error("File %s not found in sample_data!", name) | ||
|
|
||
|
|
||
| def upload_outputs(output_dir: Path, github_username: str) -> None: | ||
|
|
@@ -31,4 +65,4 @@ def upload_outputs(output_dir: Path, github_username: str) -> None: | |
| # TODO: Upload every .parquet file in output_dir to the container. | ||
| # TODO: Download customer_summary.parquet back and assert its row count matches the local file. | ||
| # TODO: Log the container name and number of files uploaded. | ||
| raise NotImplementedError("Task 7: implement upload_outputs") | ||
| pass | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,59 @@ | ||
| """Tasks 5 and 6: Build report tables and write outputs.""" | ||
| import matplotlib | ||
| matplotlib.use("Agg") | ||
| import matplotlib.pyplot as plt | ||
| import logging | ||
| from pathlib import Path | ||
|
|
||
| import pandas as pd | ||
|
|
||
|
|
||
| def build_reports(enriched: pd.DataFrame) -> dict[str, pd.DataFrame]: | ||
| """Task 5: Build four summary tables using groupby and named aggregations.""" | ||
| # TODO: Add a week column using .dt.isocalendar().week. | ||
| # TODO: Build weekly_revenue: group by week and region, columns week/region/total_revenue/order_count. | ||
| # TODO: Build customer_summary: group by customer_email, columns customer_email/customer_name/ | ||
| # region/loyalty_tier/total_spent/avg_order/order_count. | ||
| # Use ("customer_name", "first") to pick the constant-per-group string columns. | ||
| # TODO: Build category_performance: group by category, columns category/total_revenue/order_count. | ||
| # TODO: Build loyalty_analysis: group by loyalty_tier, columns loyalty_tier/avg_spent/customer_count. | ||
| raise NotImplementedError("Task 5: implement build_reports") | ||
| week = enriched["date"].dt.isocalendar().week | ||
| enriched["week"] = week | ||
| weekly_revenue = enriched.groupby(["week", "region"], as_index=False).agg( | ||
| total_revenue=("revenue", "sum"), order_count=("transaction_id", "count") | ||
| ) | ||
| customer_summary = enriched.groupby("customer_email", as_index=False).agg( | ||
| customer_name=("customer_name", "first"), | ||
| region=("region", "first"), | ||
| loyalty_tier=("loyalty_tier", "first"), | ||
| total_spent=("revenue", "sum"), | ||
| avg_order=("revenue", "mean"), | ||
| order_count=("transaction_id", "count"), | ||
| ) | ||
| category_performance = enriched.groupby("category", as_index=False).agg( | ||
| total_revenue=("revenue", "sum"), order_count=("transaction_id", "count") | ||
| ) | ||
| loyalty_analysis = enriched.groupby("loyalty_tier", as_index=False).agg( | ||
| avg_spent=("revenue", "mean"), customer_count=("customer_email", "nunique") | ||
| ) | ||
| return { | ||
| "weekly_revenue": weekly_revenue, | ||
| "customer_summary": customer_summary, | ||
| "category_performance": category_performance, | ||
| "loyalty_analysis": loyalty_analysis, | ||
| } | ||
|
|
||
|
|
||
| def write_outputs(reports: dict[str, pd.DataFrame], output_dir: Path) -> None: | ||
| """Task 6: Write report tables to CSV/Parquet and save a bar chart.""" | ||
| output_dir.mkdir(exist_ok=True) | ||
|
|
||
| # TODO: Write reports["weekly_revenue"] to weekly_revenue.csv with index=False. | ||
| # TODO: Write reports["customer_summary"] to customer_summary.parquet with index=False. | ||
| # TODO: Write reports["category_performance"] to category_performance.csv with index=False. | ||
| # TODO: Sort category_performance by total_revenue descending. | ||
| # TODO: Plot a bar chart (x="category", y="total_revenue") and save to category_revenue.png | ||
| # using plt.savefig(output_dir / "category_revenue.png", bbox_inches="tight"). | ||
| # Use matplotlib.use("Agg") before importing pyplot for headless environments. | ||
| raise NotImplementedError("Task 6: implement write_outputs") | ||
| reports["weekly_revenue"].to_csv(output_dir / "weekly_revenue.csv", index=False) | ||
| reports["customer_summary"].to_parquet( | ||
| output_dir / "customer_summary.parquet", index=False | ||
| ) | ||
| cat_df = reports["category_performance"].sort_values( | ||
| by="total_revenue", ascending=False | ||
| ) | ||
| cat_df.to_csv(output_dir / "category_performance.csv", index=False) | ||
| plt.figure(figsize=(10, 6)) | ||
| plt.bar( | ||
| cat_df["category"], cat_df["total_revenue"], color="skyblue", edgecolor="black" | ||
| ) | ||
| plt.title("Total Revenue by Category") | ||
| plt.xlabel("Category") | ||
| plt.ylabel("Total Revenue") | ||
| plt.xticks(rotation=45) | ||
| plt.savefig(output_dir / "category_revenue.png", bbox_inches="tight") | ||
| plt.close() |
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.
unused import, can remove