from datetime import datetime from execnb.nbio import read_nb from fastcore.utils import * import google.generativeai as genai from pathlib import Path
audrey.feldroy.com
The experimental notebooks of Audrey M. Roy Greenfeld. This website and all its notebooks are open-source at github.com/audreyfeldroy/audrey.feldroy.com
# Auto-Renaming My Untitled.ipynb Files With Gemini 1.5 Flash
by Audrey M. Roy Greenfeld | Sat, Feb 1, 2025
I'm starting to accumulate many UntitledX.ipynb files. Here I use the Gemini 1.5 Flash language model from Google to rename each one based on its contents.
Desired File Format
YYYY-MM-DD-Title-of-Notebook-in-TitleCase-With-Hyphens.ipynb
Get the Untitled Notebooks
nbs = L(Path().glob("Untitled*.ipynb")) nbs
nb = nbs[0]
Get the Last Modified Date
To get each file's prefix, I get the file modified stats:
Path(nb).stat().st_mtime
This returns a Unix timestamp. To get a more readable datetime:
last_modified = datetime.fromtimestamp(Path(nb).stat().st_mtime) last_modified
last_modified.strftime('%Y-%m-%d')
Check for an Existing Title
It would be in the first cell:
cells = L(read_nb(nb).cells) cells[0]
{ 'cell_type': 'markdown',
'id': 'c68e6923',
'idx_': 0,
'metadata': {},
'source': 'git-nbdiffdriver diff: git-nbdiffdriver: command not found'}
{'cell_type': 'markdown',
'id': 'c68e6923',
'metadata': {},
'source': 'git-nbdiffdriver diff: git-nbdiffdriver: command not found',
'idx_': 0}Ask Gemini to Title the Notebook
model = genai.GenerativeModel('gemini-1.5-flash-latest') model
with open(x) as f: nb = f.read()
def generate_title_part(nb): prompt = f"""Given this Jupyter notebook, create a filename following these EXACT steps: 1. Extract the title from the first cell if it starts with '#'. In this case it's: "FastHTML By Example, Part 2" 2. Convert to the format: Words-In-Title-Case-With-Hyphens.ipynb 3. Remove any special characters (like commas) 4. If the filename sounds repetitive, simplify it. 5. If the first cell does not contain a title, create one based on the entire notebook's contents. <notebook> {nb} </notebook> Return ONLY the filename, nothing else.""" safety_settings = [ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE",}, {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE",}, {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE",}, {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE",}, ] response = model.generate_content(prompt, safety_settings=safety_settings, request_options = {"timeout": 1000}) try: return response.text except Exception as ex: raise ex
result = generate_title_part(nb) print(result)
Prefix the Title With the Date
Putting the full title together:
full_title = f"{last_modified.strftime('%Y-%m-%d')}-{result.strip()}" print(full_title)
Rename the File
x
new_path = Path(full_title)
if new_path.exists(): print(f"Warning: {new_path} already exists") else: x.rename(new_path) print(f"Renamed {x} to {new_path}")
Make This All a Function
Let's put this all together into a function that we can call on several files.
def rename_notebook(nb_path): """Rename an untitled notebook based on its contents and modification date""" date = datetime.fromtimestamp(Path(nb_path).stat().st_mtime).strftime('%Y-%m-%d') with open(nb_path) as f: nb = f.read() title_part = generate_title_part(nb) new_name = f"{date}-{title_part.strip()}" new_path = Path(new_name) if new_path.exists(): print(f"Warning: {new_path} already exists") return nb_path else: nb_path.rename(new_path) print(f"Renamed {nb_path} to {new_path}") return new_path
nbs = L(Path().glob("Untitled*.ipynb")) nbs
new_paths = nbs.map(rename_notebook)
Several of my notebooks were renamed successfully! I ran out of quota, though. I was probably hitting the Gemini API too fast. Let's see where we are and try again.
nbs = L(Path().glob("Untitled*.ipynb")) nbs
new_paths = nbs.map(rename_notebook)
Mostly done! The 2 warnings sound like I have duplicates.
nbs = L(Path().glob("Untitled*.ipynb")) nbs
Finally, I'm checking those remaining notebooks. It appears those are variations on the ones that exist. I can manually merge those.
© 2024-2025 Audrey M. Roy Greenfeld