Your First AI Project

Lesson 8 of 14

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:

python
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:

python
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

python
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:

python
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

python
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

python
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.

python
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.

← Previous