Case Study 1: CSV Sales Report Automation
Scenario
You receive a raw sales export every day or week in CSV format. The data contains transaction details for various products sold by the business. Your task is to automate the reporting process by loading the CSV file, calculating sales metrics, and generating a visual summary of business performance.
Suggested Libraries
pandasmatplotlib
Sample Files You May Create
sales_data.csv— the raw exportsales_report.py— your solution scriptsales_chart.png— the generated chart
Requirements
- Load sales data from a CSV file.
- Validate that required columns exist.
- Calculate total sales revenue.
- Summarize sales by product.
- Summarize sales by time period.
- Generate a chart showing business performance.
- Output useful summary information for management review.
Have a genuine attempt at this before revealing the solution below. Think about what should happen if the CSV is missing a column, or if it's empty — should your script crash, or fail gracefully?
💡 Show Answer
import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path
def load_sales_data(csv_path):
df = pd.read_csv(csv_path, parse_dates=["Date"])
# Basic sanity checks - fail loudly rather than silently computing garbage
required_cols = {"Date", "Product", "Quantity", "UnitPrice"}
missing = required_cols - set(df.columns)
if missing:
raise ValueError(f"CSV is missing required columns: {missing}")
df["TotalSale"] = df["Quantity"] * df["UnitPrice"]
return df
def summarize_sales(df):
total_sales = df["TotalSale"].sum()
sales_by_product = df.groupby("Product")["TotalSale"].sum().sort_values(ascending=False)
sales_by_month = (
df.set_index("Date")["TotalSale"]
.resample("ME")
.sum()
)
return {
"total_sales": total_sales,
"sales_by_product": sales_by_product,
"sales_by_month": sales_by_month,
}
def generate_chart(summary, output_path="sales_chart.png"):
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
summary["sales_by_product"].plot(kind="bar", ax=axes[0], color="steelblue")
axes[0].set_title("Total Sales by Product")
axes[0].set_ylabel("Sales")
axes[0].tick_params(axis="x", rotation=45)
summary["sales_by_month"].plot(kind="line", marker="o", ax=axes[1], color="darkorange")
axes[1].set_title("Monthly Sales Trend")
axes[1].set_ylabel("Sales")
fig.suptitle(f"Total Sales: {summary['total_sales']:,.2f}", fontsize=14, fontweight="bold")
fig.tight_layout()
fig.savefig(output_path, dpi=150)
plt.close(fig)
return output_path
def run(csv_path):
print("Loading CSV...")
df = load_sales_data(csv_path)
print("Summarizing data...")
summary = summarize_sales(df)
print("Generating chart...")
chart_path = generate_chart(summary)
print(f"Total sales: {summary['total_sales']:,.2f}")
print(f"Chart saved to: {chart_path}")
print("\nTop products:\n", summary["sales_by_product"].head())
if __name__ == "__main__":
run("./sales_data.csv")
Notes:
resample("ME")(month-end) needs aDatetimeIndex, which is whyDateis set as the index before resampling.- Wrap
load_sales_datain a try/except at the call site in production so a malformed CSV doesn't crash a scheduled job silently — log the error and alert instead. - If the source is genuinely an
.xlsxfile rather than.csv, swappd.read_csvforpd.read_excel(path, engine="openpyxl")— everything downstream is identical.