Fix names and phone numbers, drop duplicates and blanks.
/build/project-form-sheet-cleaner with the starter and the rubric already in it.Read a Google Form export with messy names, five ways of writing a phone number, duplicates and missing amounts, and print a clean table with a total.
Every office job has a messy sheet. Cleaning one teaches csv, regular expressions, None for missing data and de-duplication with a set: the core of real data work.
You can take real-world messy data and make it trustworthy with a few small functions.
Each step is one function in clean_sheet.py. In the workspace, press Check next to a step: it runs your file and tells you exactly what is still wrong. Step 1 is free; the rest are part of Pro or your 7-day free trial.
Build against this exact list — it is the spec your published project is judged on.
Copy this into a new file — or open it in a workspace as clean_sheet.py. The TODO parts are yours to fill.
"""
Form sheet cleaner.
Reads a messy Google Form export, fixes names and phone numbers, drops
duplicates and incomplete rows, and prints a clean table. Fill in one
function per step, press Run, then press "Check" on that step.
"""
import csv
import io
import re
RAW = """Name, Phone ,City,Amount
rahul SHARMA ,+91 98200 12345,mumbai,"1,200"
Priya Nair,9820011111,Mumbai ,850
rahul sharma,98200-12345,MUMBAI,"1,200"
ANJALI rao, 080 4123 9876,bengaluru,
Vikram Singh,+91-99300-55555,Delhi,2300
priya nair,09820011111,mumbai,850
Sneha Iyer,12345,Pune,500
"""
def clean_name(name: str) -> str:
# TODO: " rahul SHARMA " -> "Rahul Sharma": remove extra spaces
# (" ".join(name.split()) does it) and capitalise each word (.title()).
raise NotImplementedError("step 1, clean_name(): tidy spaces and capitals")
def clean_phone(phone: str) -> str | None:
# TODO: keep only the digits (re.sub(r"\D", "", phone)), then the last 10.
# If there are fewer than 10 digits it is not a phone number: return None.
raise NotImplementedError("step 2, clean_phone(): last 10 digits, or None")
def parse_amount(text: str) -> int | None:
# TODO: "1,200" -> 1200, " 850 " -> 850, "" -> None.
raise NotImplementedError("step 3, parse_amount(): text to a number, or None")
def read_rows(raw: str) -> list[dict]:
# TODO: read RAW with csv.DictReader(io.StringIO(raw)). The header has
# stray spaces (" Phone "), so strip every key. Return one dict per row
# with keys Name, Phone, City, Amount, cleaned with your three functions
# (City uses clean_name too).
raise NotImplementedError("step 4, read_rows(): read and clean every row")
def keep_good(rows: list[dict]) -> list[dict]:
# TODO: drop rows whose Phone or Amount is None, and any row whose phone
# you have already seen (keep the first one). Keep the original order.
raise NotImplementedError("step 5, keep_good(): no duplicates, no blanks")
def main() -> None:
print(clean_name(" rahul SHARMA "), clean_phone("+91 98200 12345"), parse_amount('1,200'))
rows = read_rows(RAW)
good = keep_good(rows)
# TODO: print a table of the good rows (Name, Phone, City, Amount), then
# "3 clean rows, 4 removed, total ₹4,350".
raise NotImplementedError("step 6, main(): print the clean table")
if __name__ == "__main__":
try:
main()
except NotImplementedError as todo:
# A fresh starter is SUPPOSED to stop here. Say which step is next
# instead of printing a traceback that looks like a bug.
print(f"Not built yet: {todo}")
print("Write that function, then press Run again. Each step you finish moves this message forward.")
clean_sheet.py and a README.md holding all 5 rubric items as a checklist.pyrun.in/u/<handle>/w/project-form-sheet-cleaner.pyrun.in/u/<handle> portfolio page.Your workspace saves to this browser only. Sign in to keep it across devices and to publish it.
Flip the workspace to Public and paste its URL below, or push the file to a GitHub gist or repo.
A published PyRun workspace URL (pyrun.in/u/<handle>/w/project-form-sheet-cleaner) works as the public URL too.