Python Tutorial
Python Project: CSV Report
Parse rows of sales data, compute totals per product, and print a sorted report.
What you are building
You parse a small CSV of product, quantity, and unit price. The program multiplies quantity by price for each row, sums revenue per product, and prints a report sorted by revenue, highest first.
Stay in the Python editor at /try. This track is not HTML, C, or C++. The CSV lives in a multiline string. You do not read a file from disk, and you do not install packages. The standardcsv module is enough.
Skills used
csv.DictReaderon a string viaio.StringIOintandfloatconversions on cells- A dict of running revenue per product
sortedwith a key andreverse=True- A printed table of product, units, and revenue
Parse a CSV string
In this editor there is no sales.csv on disk. Put the same text in a string. Wrap it withStringIO so csv can read it like a file. DictReader uses the header row as keys.
Example
import csv
from io import StringIO
raw = """product,qty,price
Mug,3,4.50
Notebook,2,8.00
Pen,10,1.25
"""
reader = csv.DictReader(StringIO(raw))
for row in reader:
print(row["product"], row["qty"], row["price"])Cells arrive as strings. "3" is not 3 until you call int. "4.50"needs float. Convert once when you walk the rows.
Revenue per product
Line revenue is quantity times unit price. Two Mug rows must add, not overwrite. Store a pair per product: units sold and money earned. A dict keyed by product name holds that pair.
Example
import csv
from io import StringIO
raw = """product,qty,price
Mug,3,4.50
Notebook,2,8.00
Mug,1,4.50
"""
totals = {}
reader = csv.DictReader(StringIO(raw))
for row in reader:
name = row["product"]
qty = int(row["qty"])
price = float(row["price"])
units, revenue = totals.get(name, (0, 0.0))
totals[name] = (units + qty, revenue + qty * price)
print(totals)csv.reader would give lists. DictReader gives names, so a later column swap in the header is easier to see. Skip blank lines. Do not write a parser that splits on every comma if a future cell might contain a comma; the csv module already handles quotes.
Complete program
Five sales rows, three products. The report sorts by revenue. Mug earns 18.00, Notebook 24.00, Pen 12.50. Notebook should print first. Run the whole script at /try.
Example
import csv
from io import StringIO
RAW = """product,qty,price
Mug,3,4.50
Notebook,2,8.00
Mug,1,4.50
Pen,10,1.25
Notebook,1,8.00
"""
totals = {}
reader = csv.DictReader(StringIO(RAW))
row_count = 0
for row in reader:
name = row["product"].strip()
qty = int(row["qty"])
price = float(row["price"])
units, revenue = totals.get(name, (0, 0.0))
totals[name] = (units + qty, revenue + qty * price)
row_count += 1
ranked = sorted(totals.items(), key=lambda item: item[1][1], reverse=True)
grand_units = sum(units for units, _ in totals.values())
grand_rev = sum(revenue for _, revenue in totals.values())
print("CSV sales report")
print("Rows parsed:", row_count)
print()
print(f"{'product':<12} {'units':>6} {'revenue':>10}")
print("-" * 30)
for name, (units, revenue) in ranked:
print(f"{name:<12} {units:>6} {revenue:>10.2f}")
print("-" * 30)
print(f"{'TOTAL':<12} {grand_units:>6} {grand_rev:>10.2f}")| Product | Units | Revenue | How |
|---|---|---|---|
| Notebook | 3 | 24.00 | 2 + 1 at 8.00 |
| Mug | 4 | 18.00 | 3 + 1 at 4.50 |
| Pen | 10 | 12.50 | 10 at 1.25 |
| TOTAL | 17 | 54.50 | sum of the three |
Common mistakes
- Opening a real path such as
open("sales.csv")in this editor. There is no project folder file. Use the multiline string. - Sorting by product name when you wanted money. The key is
item[1][1]: the revenue inside the tuple. - Multiplying strings:
"10" * 3repeats text. Convert types first. - Forgetting the header. If you use
csv.readerand do not skip row 1,int("qty")crashes. - Installing a spreadsheet library. This report is stdlib only:
csv,io, dicts.
How to extend / Practice tasks
Leave the five demo rows in place until 54.50 matches.
- Add a line
Mug,2,4.50and confirm Mug units become 6 and revenue 27.00, with a new grand total of 63.50. - Sort by units instead of revenue and print which product moves to the top (Pen).
- Skip any row whose quantity is 0 or whose price is negative. Test by inserting
Sticker,0,1.00andLamp,-1,20.00and checking that TOTAL units stay 17.
📘 Real-World Deep Dive
Turning a CSV into a summary report is the single most common "first real task" a Python developer is handed at work. It ties together file reading, per-group aggregation, and clean formatted output.
What to build
Read a sales CSV, total revenue per region, and print a tidy report — streaming rows so it works whether the file has 10 lines or 10 million.
Real-Life Example
import csv, io
from collections import defaultdict
SAMPLE = """region,product,amount
north,widget,120
south,widget,80
north,gadget,200
south,gadget,50
"""
def report(f) -> dict:
totals = defaultdict(float)
for row in csv.DictReader(f): # streams one row at a time
totals[row["region"]] += float(row["amount"])
return dict(totals)
for region, total in sorted(report(io.StringIO(SAMPLE)).items()):
print(f"{region:6} {total:8.2f}")csv.DictReader streams rows lazily, so memory stays flat even on huge files — no list(reader) needed.
Expected Output
north 320.00
south 130.00Common mistakes
line.split(",")breaks on quoted fields that contain commas — always use thecsvmodule, never manual splitting.list(csv.reader(f))pulls the whole file into RAM; iterate the reader directly to stream.- Every CSV value is a string — forgetting
float(row["amount"])concatenates text instead of adding numbers.
🚀 Performance & Best Practices
- Streaming with
DictReaderkeeps memory constant regardless of file size. - For heavy numeric crunching or joins across files, pandas'
read_csv+groupbyis far faster and shorter — a natural next step. - Write the report with
csv.writer(not string formatting) if it feeds another program.
🧪 Try It Yourself
- Read a real file path from
sys.argvinstead of the embedded sample. - Add a second breakdown (per product) and print both tables.
- Re-implement the aggregation with pandas and compare the line count.