Files in, files out
Right now your notes are typed into a cell. A tool reads a folder and writes a file, and that step — three functions, none of them AI — is what makes it something you can hand to somebody.
Getting files into Colab
Upload directly, easiest for a handful:
from google.colab import files
uploaded = files.upload()
print(list(uploaded.keys()))A file picker appears; what you choose lands in the runtime.
Mount Drive, better for anything recurring:
from google.colab import drive
drive.mount('/content/drive')Your Drive appears at /content/drive/MyDrive/. Now a folder of notes lives somewhere permanent and the tool reads it each run.
Reading a folder
from pathlib import Path
folder = Path("/content/drive/MyDrive/meeting-notes")
paths = sorted(folder.glob("*.txt"))
print(f"found {len(paths)} files")
for p in paths[:3]:
print(" ", p.name)glob("*.txt") finds every text file; sorted makes the order predictable, which matters when you are comparing two runs. Reading one:
text = paths[0].read_text(encoding="utf-8")Specify the encoding. Text files from Windows, from email, or containing any non-English characters will otherwise throw UnicodeDecodeError on some machines and not others, which is a genuinely annoying way to lose an hour. utf-8 is the right default, and encoding="utf-8", errors="replace" will get you through a folder containing one badly-encoded file rather than stopping on it.
The whole pipeline
import json
from pathlib import Path
rows = []
for i, p in enumerate(paths):
print(f"{i+1}/{len(paths)}", end=" ", flush=True)
try:
text = p.read_text(encoding="utf-8", errors="replace")
if not text.strip():
print(f"\n{p.name}: empty, skipped")
continue
items = salvage(extract(text))
for item in items or []:
rows.append({
"meeting": p.stem,
"owner": item.get("owner", "UNKNOWN"),
"action": item.get("action", ""),
"due": item.get("due", "UNKNOWN"),
})
except Exception as e:
print(f"\n{p.name} failed: {type(e).__name__}: {e}")
print(f"\n{len(rows)} action items from {len(paths)} meetings")Every part of that has appeared already. The loop from lesson 6, the parse and salvage from lesson 7, the try/except, the progress print. p.stem is the filename without its extension, which becomes the meeting name.
item.get("owner", "UNKNOWN") is worth noting: .get returns a default if the key is missing, rather than raising an error. It is the small habit that stops one malformed record killing a 400-file run.
Writing the CSV
import csv
with open("actions.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["meeting", "owner", "action", "due"])
writer.writeheader()
writer.writerows(rows)
files.download("actions.csv")csv.DictWriter handles the escaping — commas inside an action, quotes, newlines — which is exactly the sort of thing you should never write yourself. newline="" prevents blank lines between rows on some platforms.
That downloads a spreadsheet. Open it, and the tool is real.
Two checks before you trust the output
Count. Does the number of rows make sense? Twelve meetings producing three action items is suspicious; producing four hundred is more suspicious.
Read ten at random. Not the first ten — the first ten are usually your test files.
import random
for r in random.sample(rows, min(10, len(rows))):
print(r)Random sampling catches the systematic error that ordered inspection misses, because problems cluster in the files you did not think about.
Where this becomes a real tool
Point it at a Drive folder, run it weekly, and you have something genuinely useful. The natural next steps — a scheduled run, writing straight to a Sheet, posting to a channel — are all plumbing rather than AI, and every one is a well-documented library call away.
Which is worth pausing on. You have now built the five-part shape from lesson 1: input from a folder, prompt assembly in a template, the model call, parsing with validation, output to a file. The remaining lessons make it reliable, give it a face, and add retrieval — but the architecture is complete, and it is the same architecture underneath products people pay for.
Do this today: put three real text files in a Drive folder and run the whole pipeline end to end. Downloading a CSV your own code produced is a different feeling from watching a model reply in a cell, and it is the moment this stops being an exercise.