Bug Fix Report
1. Diagnosis in one line
Python's CSV reader gives you every value as text, so order['amount'] is a string like "42.50", or "" for a cancelled order. Adding text to the number total fails.
Confidence: High. The error message names the exact types involved, a whole number and a piece of text. That's how csv.DictReader always behaves, and the blank amounts you mentioned are a second problem on the same line.
2. What went wrong (plain English)
Your script reads orders.csv, adds up the amount column and divides the total by the number of orders to get the average.
The problem is on line 14: total += order['amount']. csv.DictReader doesn't guess what kind of data is in each column. Everything comes back as text. So the first time through the loop, Python is asked to do 0 + "42.50". It won't add a number to text, so it stops with unsupported operand type(s) for +=: 'int' and 'str'. In that message, int is your starting total = 0 and str is the text from the CSV.
Converting the text to a number fixes that part. But your cancelled orders have a blank amount, and turning a blank into a number causes its own crash (a ValueError). So the fix also has to skip blank rows.
My assumption: cancelled orders shouldn't count toward the average at all. They're left out of the total and out of the number you divide by. If you divided by every row, including cancelled ones, the average would come out too low.
3. Fixed code
import csv
def load_orders(path):
orders = []
with open(path, newline='') as f:
reader = csv.DictReader(f)
for row in reader:
orders.append(row)
return orders
def average_order_value(orders):
total = 0
count = 0 # FIXED: count only orders that actually have an amount
for order in orders:
amount = (order['amount'] or '').strip() # FIXED: get the text value (or '' if missing) and trim spaces
if amount == '': # FIXED: skip cancelled orders with a blank amount
continue # FIXED
total += float(amount) # FIXED: convert text like "42.50" into a number before adding
count += 1 # FIXED: this row counts toward the average
return total / count # FIXED: divide by real (non-blank) orders, not every row
if __name__ == '__main__':
orders = load_orders('orders.csv')
print('Average order value:', round(average_order_value(orders), 2))
4. What changed
| Location (line/function) | Before | After | Why |
|---|---|---|---|
average_order_value, new line after total = 0 |
(none) | count = 0 |
Keeps track of how many orders have a real amount. |
average_order_value, start of loop |
(none) | amount = (order['amount'] or '').strip() |
Gets the amount as clean text. or '' covers a row so short that the field is missing entirely. |
average_order_value, in loop |
(none) | if amount == '': continue |
Skips cancelled orders with blank amounts, so they don't crash the conversion or distort the average. |
average_order_value, line 14 |
total += order['amount'] |
total += float(amount) |
Turns the text into a decimal number so it can be added. This was the line that crashed. |
average_order_value, in loop |
(none) | count += 1 |
Counts this order toward the average. |
average_order_value, return |
return total / len(orders) |
return total / count |
Divides by real orders only, so cancelled rows don't pull the average down. |
5. How to test it
- Save a copy of your script, put the fixed
average_order_valuefunction in it, and runpython3 report.pyin Terminal from yourshopfolder. - Quick check with known numbers: make a file
test.csvcontaining the three linesamount,40,45, then a blank row for a cancelled order (,if there are other columns, or just an empty line ifamountis the only one). Point the script attest.csvand you should seeAverage order value: 42.5, because (40 + 45) ÷ 2. - Run it on your real
orders.csvand compare the result with Shopify's own average order value for the same date range. It should be close, though Shopify may treat refunds or taxes differently. - Edge case: put a space in one blank amount cell (
" "). It should still be skipped without crashing. - Edge case: try a CSV where every amount is blank. You'll get a
ZeroDivisionError. That's expected for now; see Section 7.
6. If it still doesn't work
- Amounts contain symbols or commas (
$42.50or1,250.00) and the script crashes withValueError: could not convert string to float. To check, addprint(repr(amount))just above thefloat(amount)line and look at the last value printed before the crash. If you see symbols, remove them first, for exampleamount.replace('$', '').replace(',', ''). KeyError: 'amount'means the column header doesn't exactly matchamount. Real Shopify exports often call itTotalorSubtotal, and sometimes the file starts with a hidden character (a "BOM") stuck to the first header. To check, addprint(list(orders[0].keys()))afterload_orders. If the first name starts with'\ufeff', change theopen(...)line toopen(path, newline='', encoding='utf-8-sig').- The number looks wrong but there's no crash. You might be averaging the wrong column, or your export might list one row per item, not per order. Shopify order exports can repeat an order across several rows, and the extra rows often have a blank total. To check, open the CSV in Numbers or Excel and see whether the same order number appears on more than one row.
To pin it down, it would help to have the first 3–5 lines of orders.csv (with customer details removed) and the full error text if a new one appears.
7. Other things I noticed (optional)
- If the file is empty or every order is cancelled,
countstays at 0 and dividing by it crashes. You could addif count == 0: return 0just before the retu