When APIs Ghost You: Building a Python Company Industry API Fallback

Day 98 of 100 Days Coding Challenge

Today’s mission: teach Business Rader 3000 how to fetch industry information for a company. Sounds simple, right? Type “Ford Motor Corporation,” press enter, and voilà—get back “automotive industry.” Except, of course, nothing in programming is ever that straightforward.

My first plan was to use Clearbit. Plot twist: Clearbit slammed the door on free accounts. Then I looked at OpenCorporates—yep, that one comes with a price tag too. Fair enough; servers don’t run on fairy dust. But since I’m a hobbyist coder, dropping money on every shiny API is not my jam.

So I went with the Big Three:

  • Yahoo Finance (yfinance) for public companies.
  • Wikidata API for international and quirky cases.
  • Google Knowledge Graph is the “last resort uncle” who knows a little about everything.

Google KG is tricky—you get some queries free, but if you forget to actually activate your account (like I did), you’ll spend hours wondering why nothing works. I had déjà vu from a year ago when I took a Coursera class on Google Cloud and burned through my free credits, then was charged $6.5. It’s possible I may have just expired a trial. To be fair, Google Cloud gave you a way to maintain your costs if you are serious about using their service. That is how I know I spent money. The bright side? Google still gives a generous allowance of daily free queries. It’s like IT giants saying, “Sure, kid, go play in our sandbox—just don’t dig a swimming pool.”

Today’s Motivation / Challenge

Why does this matter? Because knowing what industry a company belongs to is like knowing the genre of a book before you read it. You don’t want to bring a box of tissues to a slapstick comedy, and you don’t want to pitch software to a tire manufacturer. Business Rader 3000 needs this context to do anything useful, and today was the day it learned how to ask smart questions.

Purpose of the Code (Object)

The code takes the company name you provide and then checks three different sources (Yahoo Finance, Wikidata, Google KG) to guess its industry and sector. If one fails, the next one steps up. Think of it as having three friends with wildly different interests—if one doesn’t know the answer, another probably does.

AI Prompt

For your Business Rader 3000 project, I’d suggest:

  • Use Yahoo Finance / yfinance for public companies.
  • Use Wikidata API for broader coverage (private + international).
  • Fall back to the Google Knowledge Graph API for missing entries.

Please add exactly the same functions, but change the source of information.

Functions & Features

  • Prompt the user for a company name.
  • Query Yahoo Finance for industry and sector info.
  • Query the Wikidata API as a second source.
  • Use Google Knowledge Graph as a fallback.
  • Display results in a simple dictionary format.

Requirements / Setup

You’ll need:

  • Python 3.11+
  • Install libraries:

pip install yfinance requests

(Plus a Google API key if you’re using Google KG.)

Minimal Code Sample

import yfinance as yf

company = “F”

info = yf.Ticker(company).info

print({“industry”: info.get(“industry”), “sector”: info.get(“sector”)})

Here we ask Yahoo Finance about Ford, and it politely answers with industry and sector details.

business-rader-3000

Notes / Lessons Learned

When I first ran the program, the response was… silence. No data, no errors, just tumbleweeds. Debugging to the rescue. I added two levels:

  • BR_DEBUG=1 to show which provider answered (or didn’t).
  • BR_DEBUG=2 to print detailed HTTP errors.

That’s when I remembered my weather app fiasco: API keys sometimes need 45–50 minutes to wake up after being created. Turns out, I hadn’t properly activated Google KG at all. Once I fixed that, it worked like a charm. The funny part? By the time I figured this out, I couldn’t tell whether it was my script improving or the API finally letting me in. Either way, I learned something priceless—how to debug like a grown-up.

Optional Ideas for Expansion

  • Save industry results into a local file so you don’t keep hitting the APIs for the same company.
  • Add a fallback “guess” list based on keywords in the company name.
  • Display results in a friendly table instead of a plain dictionary.

        info = yf.Ticker(sym).get_info()

        industry = _normalize(info.get(“industry”))

        sector = _normalize(info.get(“sector”))

        if industry or sector:

            return {“industry”: industry, “sector”: sector or _guess_sector_from_industry(industry)}

Oversleeping and Still Winning the Morning: A Runner’s Small Victory

Brian’s fitness journal after a brain stroke

So this is how I got away with oversleeping and still winning the morning.

This morning began with what looked like a promising start—and then quietly derailed.

I actually woke up before my alarm. After a quick trip to the bathroom, I returned to bed for what I assumed would be a brief rest. Unfortunately, my brain interpreted that as permission for a second sleep session.

The next thing I remember was hearing my wife get up and leave for her morning exercise. Shortly after the front door closed, my alarm went off. I turned it off and thought, very logically, that I would get up after she returned so we would not both compete for the bathroom.

In theory, this sounded like a perfectly organized plan.

In reality, it made absolutely no sense.

My wife usually leaves before 6:30 a.m., while my alarm rings at 7:00. Looking back, the most likely explanation is that I simply fell asleep again and missed everything—including her return from exercise, her getting ready, and her leaving for work.

My wife operates on a far stricter schedule than I do. She arrives at work earlier than most people because she likes to clear her emails before colleagues and bosses begin their day. Meanwhile, my morning apparently turned into a quiet demonstration of the dangers of comfortable pillows.

I had intended to start my day at 7:00 a.m.

Instead, I woke up when my calendar reminder sounded at 8:00.

One hour behind schedule.

Normally, that might derail the entire morning, because my routine includes a long chain of small tasks. If one falls behind, the rest tend to domino into chaos. Today, however, I decided to move quickly and avoid lingering over anything.

Efficiency replaced elegance.

Surprisingly, it worked.

I caught up with my morning tasks and still managed to leave for my run at roughly the time I had planned the night before when I checked the weather forecast.

Even better, the run itself went well. My legs felt a little sore at the start, but I still managed to beat my target pace for the first time this week.

So while the day technically began with oversleeping, it ended with something close to success.

Not perfect—but proof that sometimes a late start does not ruin the day if you simply keep moving forward.

Building a Python Business Research Tool from Scratch

Day 97 of 100 Days Coding Challenge: Python

Business Radar 3000

The Civilization Timeline project took longer than I wanted. Over the past few days, I had to do a major cleanup because my data suddenly stopped pulling correctly. I spent hours tracking down the cause—chasing bugs like a detective with too much coffee and not enough clues.

That’s when I learned a hard but valuable lesson: always back up your app, no matter how busy you are. If you’re working with environments, GitHub is not optional—it’s survival gear. Unfortunately, this is one of those lessons best learned once… the hard way.

With only four days left in the 100 Days challenge, it’s clear I won’t finish everything neatly by Day 100. Still, instead of panicking, I did what any reasonable person would do: I started a new project.

Our sister company recently introduced an AI-based market analysis app for their business. I wasn’t entirely sure what kind of AI they were using, but it sparked an idea: Maybe I can build something similar myself. The upside? I get to choose exactly which AI to use—and how.

This project also has long-term potential. I can see myself using it as an external analysis tool for my own business in the future, which makes the effort feel far less theoretical.

Knowing this project will likely push me about 7–8 days past the official 100-day mark, I went ahead and launched it anyway.

Thus, Business Radar 3000 was born.

Experience

Today kicked off a brand-new adventure: Business Rader 3000. If that sounds like a sci-fi gadget, well… it kind of is—except instead of spotting UFOs, it’s designed to find businesses. Starting a new project always feels like opening a crisp, blank notebook. You know the one: smooth pages, faint inky smell, the promise that this time, you’ll keep your handwriting neat.

I know this one will run longer than my 100-day countdown—14 days at least. But here’s the twist: I don’t actually care if it spills over. I started this challenge to see if coding would stick. Ninety-seven days in, I already know the answer. Spoiler: it did. I’m not stopping when the counter hits 100.

For setup, I leaned on Poetry again. Last time, I thought I’d forget everything about environments and dependencies, but muscle memory kicked in. Fourteen days may have passed, but the commands still rolled out smoother than I expected. Sure, I peeked at my notes for reassurance, but compared to my earlier flailing, today felt almost professional.

Today’s Motivation / Challenge

Why does this project matter? Imagine a magical Rolodex (remember those?) that not only stores company names but also tells you their industry, who runs them, and what opportunities might be on the horizon. Business Rader 3000 is my attempt at building just that. It’s equal parts detective tool and personal assistant—except this one never complains about coffee breaks.

Purpose of the Code (Object)

The code starts small: it asks for a company name, sets up a safe working environment, and makes sure the right tools are installed. Think of it as laying the foundation for a house—you won’t see fancy wallpaper yet, but without a good base, the whole thing topples.

AI Prompt

Please add the following Tasks.

Day 1 of Business Rader 3000

  1. Prompt the user for a company name.
  2. Set up a virtual environment.
  3. Install necessary libraries.

Functions & Features

  • Ask the user for a company name.
  • Create a clean, isolated Python environment.
  • Install core libraries automatically.

Requirements / Setup

You’ll need:

  • Python 3.11+
  • Poetry (recommended) or pip

If you’re going the pip route:

pip install requests

Minimal Code Sample

# main.py

company = input(“Enter a company name: “)

print(f”Great! We’ll research {company}.”)

One line to ask, one line to confirm. The coding equivalent of saying “hello” before the real conversation starts.

business-radar-3000

Notes / Lessons Learned

Ninety-seven days in, and I’m still tripping over where files should live. Here’s the cheat sheet that finally stuck:

business-rader-3000/ <—- Yes, I have a spelling error, I’ve noticed after I uploaded my app.

├─ .gitignore

├─ .env                 # (later, don’t commit this!)

├─ pyproject.toml       # if using Poetry

├─ requirements.txt     # if using pip

└─ src/

   └─ main.py

Put .gitignore at the root, not buried in some sub-folder. And yes, double-check before pushing to GitHub—nobody wants their API keys doing a world tour. Eventually, this becomes second nature, but while learning, never be shy about asking, “Where does this file go?” It saves hours of detective work later.

Optional Ideas for Expansion

  • Add a greeting that remembers the last company you entered.
  • Store the company name in a text file or database for future sessions.
  • Build a tiny CLI so you don’t have to retype commands every time.

Lessons from a Python CSV Seed Import Disaster

Day 96 of 100 Days Coding Challenge: Python

Today was less about writing shiny new features and more about wrestling with files until the app behaved like it did back on Day 19. After nearly four hours of tinkering yesterday, I had to step away—full-time jobs and real life have a way of enforcing time limits. But I was annoyed at myself for one thing: forgetting to commit to GitHub. Again.

This project has grown into a filing labyrinth, the kind where one wrong turn in data/ leaves you staring at errors that make no sense. The safest way to preserve it is to both recreate the file structure and push to GitHub. Of course, I did neither at first. Nineteen days of work, sometimes 4–5 hours at a stretch, left hanging by a thread. Thankfully, I managed to recover everything, lock it with a password, and—finally—commit it to GitHub. Lesson learned.

Today’s Motivation / Challenge

Why does this matter? Because all the code in the world won’t help if your files don’t point to the right place. Data seeds are the backbone of this project: civilizations, events, and sources all live in CSVs. If your app can’t find them, it’s like trying to read a book without the pages.

Purpose of the Code (Object)

The code reconnects the app to its seed files—civilizations.csv, events.csv, and sources.csv. These files feed the database with initial data, ensuring the app has something to display. Without them, the app just spins its wheels and looks confused. By linking everything properly, the app can reload cleanly whenever you restart it.

AI Prompt

We will work on the Import.
Creating the link to the correct data seeds:

  • data/seeds/civilizations.csv
  • data/seeds/events.csv
  • data/seeds/sources.csv

Functions & Features

  • Import civilizations, events, and sources from CSV seed files.
  • Ensure the app database initializes with correct data.
  • Reconnect app logic to seed files if paths are broken.

Requirements / Setup

You’ll need:

  • Python 3.11

Installs:

pip install sqlmodel

Minimal Code Sample

import pandas as pd

civs = pd.read_csv(“data/seeds/civilizations.csv”)

events = pd.read_csv(“data/seeds/events.csv”)

sources = pd.read_csv(“data/seeds/sources.csv”)

Reads the seed CSVs into dataframes for use in the app.

The Civilization Timeline Builder

Notes / Lessons Learned

When you hit a problem importing data, the best thing you can do is stay calm and check filenames line by line. My biggest mistake came from an earlier definition error—I had renamed a file, recreated seeds, and left a trail of confusion. AI didn’t make it worse, but long chats with shifting memory meant old names and new names occasionally got tangled. Programming is like that: fragile, literal, unforgiving.

At one point, I even had the wrong database URL: sqlite:///./data/app.db instead of the correct sqlite:///./data/civ.db. One tiny typo, and the whole app refused to recognize my seeds. Reloading Streamlit without the right database path? A guaranteed disaster.

In the end, a fresh brain helped more than any clever trick. I reconnected the seed files, corrected the database path, and the app sprang back to life. Programming, I’ve realized, is 50% code, 50% precision—and 100% patience.

Optional Ideas for Expansion

  • Add validation scripts to confirm all seed files exist before the app runs.
  • Build a “seed reloader” button in the UI for quick fixes.
  • Let users upload their own seed CSVs to extend the dataset.

Lessons from an Imperfect Oven from Baking

Brian’s fitness journal after a brain stroke

You can learn anything from many things. I learned lessons from an imperfect oven.

Today I set out to bake a lemon pound cake to snack on during the week. I’ve tried this recipe several times before—with mixed results. Sometimes it turns out beautifully, and other times… well, it becomes a learning experience.

I chose lemon pound cake partly because I enjoy it and partly because I have been trying to maintain my weight. Having a homemade snack on hand helps me avoid random snacking.

I started baking in the afternoon, and the results remain uncertain for now. The cake is still cooling after spending about 50 minutes longer in the oven than the recipe suggested.

Here are some lessons from an imperfect Oven.

The main culprit is our oven. It is very old and wildly inaccurate when it comes to temperature. We have discussed replacing it someday, but until that day arrives, every baking project becomes a small scientific experiment.

The second factor may be the silicone loaf pan I used this time. Silicone insulates differently from metal, so the cake may have needed more time to bake through. Once the initial baking time passed, I began checking the cake every five to ten minutes, waiting for the tester to come out clean—or at least mostly clean.

Even after the extra baking time, I still worried the center might be slightly undercooked. However, the top had already turned a beautiful golden brown, and I did not want to risk burning it. At some point, baking becomes a negotiation between undercooked and overcooked.

So I made the executive decision: take it out.

Now the cake sits on the counter cooling quietly, holding the answer to whether this attempt succeeded. I will only know the truth once I slice into it later.

Last time I baked one, I offered it to my wife. She politely ate a slice—maybe half a slice—but sweets are not really her weakness. She is also trying to lose weight, so giving her an entire pound cake might not qualify as thoughtful support.

Which means, realistically, I will probably end up eating most of it myself.

If the cake turns out well, that will be a reward.
If not, it will still count as valuable research in the ongoing science of baking with a temperamental oven.

I Learned How Important it is to Back Up Your files

Day 95 of 100 Days Coding Challenge: Python

Today was supposed to be the grand finale. The plan: add a simple authorization system, set up daily backups, and polish everything with a shiny test suite. Instead, one tiny testing error snowballed into a catastrophic app crash. And the punchline? I hadn’t pushed to GitHub in four days. Yes, four.

So there I was, watching my carefully crafted app crumble, muttering at myself for breaking the golden rule: always back up. The irony stung—after weeks of building a timeline of civilizations that fell for ignoring simple rules, I managed to repeat history myself. Turns out, hubris gets you whether you’re Rome or just a tired coder.

Today’s Motivation / Challenge

Why does this matter? Because by the end of any project, you want to know your work isn’t just duct-taped together. Governance, performance, backups—these are the boring but essential pieces that keep your app from toppling like a badly built tower. Think of it like brushing your teeth: not exciting, but skip it, and you’ll regret it.

Purpose of the Code (Object)

The code here ties up loose ends: adding simple password-based access, creating a daily backup script, optimizing database performance with indexes, and ensuring tests cover at least 80% of the data and API. The goal is to make sure the app runs fast, stays secure, and—most importantly—doesn’t collapse when someone sneezes.

AI Prompt

Please do the following:
Governance & perf

  • Simple auth (env-guarded)
  • Daily backup script
  • DB indexes
  • Test suite

Accept: tests ≥80% coverage on data + API; timeline loads <250ms on sample set.

Functions & Features

  • Add environment-based simple authentication.
  • Run a daily script to back up the database.
  • Add indexes for faster queries.
  • Build a test suite with at least 80% coverage.

Requirements / Setup

You’ll need:

  • Python 3.11

Installs:

pip install pytest

Minimal Code Sample

# Simple env-based auth

import os

PASSWORD = os.getenv(“APP_PASSWORD”)

def check_auth(pw):

    return pw == PASSWORD

A tiny helper to check passwords against environment variables.

The Civilization Timeline Builder

Notes / Lessons Learned

Creating authorization wasn’t the problem—it was forgetting that I’d hardcoded a password in an .env file ages ago and then promptly lost it. Nothing like digging through old notes trying to remember whether you typed password123 or password_123.

Then came the imports. Every time I messed with data/seeds, my app broke in spectacular fashion. I’ve realized most of my headaches came from database imports, not from the app itself. Lesson? When your project leans heavily on a database, really know how you’re connecting to it. Otherwise, you’ll spend more time fixing imports than writing actual features.

In the end, I didn’t get the clean finale I wanted, but I walked away with the most important reminder of all: always, always back up. Civilization may fall without warning, but your repo doesn’t have to.

Optional Ideas for Expansion

  • Add role-based permissions (e.g., read-only vs. admin).
  • Automate cloud backups instead of local scripts.
  • Track performance metrics over time to catch slowdowns early.

Packing History in a Zip File

Day 94 of 100 Days Coding Challenge: Python

Today I leveled up my app by teaching it how to bundle everything into one neat little care package: CSV, PNG, quizzes, and all—zipped up and ready to go. Think of it as a take-home kit for history nerds. Instead of juggling screenshots and exports, now you can grab one file, open it later, and relive the glory of your filters and quizzes.

What excites me most is not just the feature itself but the possibilities it unlocks. I suddenly realized this kind of import/export bundle could be handy for any project where you want to ship outputs to others—or just yourself. If I had to re-create the logic from scratch, I’d probably be lost in the weeds. But now? I know the function exists, I know how to call it, and I have the code. That’s enough to carry the trick into future projects. And yes, I’m ridiculously happy about that.

Today’s Motivation / Challenge

Why does this matter? Because nobody wants to lose work. Whether it’s a report, a chart, or a quiz, being able to package and save everything guarantees you can come back to it later. Plus, if you’re sharing your app with others, a zipped curriculum pack makes you look like you thought ahead. It’s like sending someone leftovers in a perfectly labeled container instead of handing them a messy plate.

Purpose of the Code (Object)

The code bundles your selected civilizations, filters, quizzes, and images into a single zip file. Inside, you’ll find JSON, CSV, PNGs, and even a README so you know what’s what. It’s designed to be portable, so you can save a snapshot of your exploration, share it with a friend, or use it as a reference for another project.

AI Prompt

Add the following function:
Curriculum packs

  • Bundle lens + selected civs + quiz + images into a zip.
  • ✅ Accept: zip contains JSON, CSV, PNGs, README.

Functions & Features

  • Collect current filters, data, and quiz results.
  • Export charts and timelines as PNGs.
  • Save everything into a single zip archive.
  • Include a README file for clarity.

Requirements / Setup

You’ll need:

  • Python 3.11

Installs:

pip install plotly kaleido streamlit

Minimal Code Sample

import zipfile, io

with zipfile.ZipFile(“curriculum_pack.zip”, “w”) as zf:

    zf.writestr(“filters.json”, json.dumps(filters))

    zf.writestr(“data.csv”, df.to_csv(index=False))

    zf.writestr(“README.txt”, “Curriculum pack export”)

Writes JSON, CSV, and a README into a zip file in memory or on disk.

The Civilization Timeline Builder

Notes / Lessons Learned

Sometimes it’s the little discoveries that bring the most joy. I used to write imports like:

import io

import csv

But today I learned you can just write:

import io, csv

It’s small, but it makes me smile—one of those tiny conveniences you only stumble upon while coding.

The helper function itself starts by converting Plotly figures into PNG bytes (thanks to Kaleido, which I’d already added earlier). Then, with the io library, I created in-memory streams for text and files. io.StringIO in particular is handy: it treats strings like files, making them easy to read and write. I’d used it before for downloads, but seeing it in action again for zipping files reminded me just how versatile it is.

The result: a neat little archive of history in my downloads folder. Simple, but powerful.

Optional Ideas for Expansion

  • Add an “unzip and load” feature so packs can be re-imported into the app.
  • Include a summary.txt file with auto-generated narrative recaps.
  • Allow multiple packs to be batched into one archive for classroom use.

Staying Consistent in Unpredictable Weather

Brian’s fitness journal after a brain stroke

The weather in Nashville has been behaving like a rollercoaster lately. Yesterday the temperature jumped up by about 20°F, and today it dropped by nearly the same amount. So winter, after briefly pretending to leave, has returned with enthusiasm.

Since my brain stroke, my body does not regulate temperature very well. My practical solution has been simple: adjust the outfit instead of fighting the weather. Even so, the cold this morning made me check the forecast twice just to confirm we were not facing another surprise snow day. Fortunately, there was no snow—at least not today.

Unless we have lightning, ice storms, or heavy snow, I try to keep my running routine. Consistency matters to me, so I run whenever conditions allow it.

When winter temperatures drop too much, I usually wait until the warmest part of the day before heading out. Nashville winters can feel colder than they appear, especially for someone whose internal thermostat does not cooperate. If I can avoid the worst cold, I will.

Cold weather affects my runs more than I would like. My body spends so much effort trying to stay warm that it leaves less energy for actual running. On Saturdays, I usually run 10 km, and ideally, I prefer conditions that are neither too cold nor too hot.

Today, however, timing worked against me.

I delayed the run longer than usual while waiting for the temperature to improve, which started to disrupt the rest of our Saturday schedule. Eventually, I decided that waiting any longer would only make things worse.

So I went out and ran anyway.

Even in the afternoon, the air remained stubbornly cold. My pace was slower than usual, which felt a bit disappointing. Still, I finished the full 10K despite the strong temptation to cut it short.

In winter running, sometimes the real achievement is not speed—it is simply showing up and finishing.

Adding Quiz Mode to Your Streamlit History App

Day 93 of 100 Days Coding Challenge: Python

Ninety-three days into this Python marathon, it suddenly hit me: I’ve only got a week left. The thought made me a little sad—like reaching the last few pages of a great book and realizing the ending is near. But rather than mope, I decided to channel that energy into something fun.

Today’s feature: quizzes. That’s right, the Civilization Timeline Builder is now handing out pop quizzes like a history teacher who drank too much coffee. The quiz pulls from the same filters (year range, regions, tags) you’ve already selected and generates random questions. “Which came first?” “Match the civ to the years?” It’s like turning my carefully curated data into a trivia night—minus the nachos, unfortunately. When you finish, the program even returns your score. History with instant feedback—cool, right?

Today’s Motivation / Challenge

Why does this matter? Because learning sticks better when you test yourself. You can scroll through timelines all day, but nothing sharpens your memory like a question asking, “Did the Gupta Empire adopt iron later than Rome?” Quizzes make history interactive, playful, and just a little competitive—especially if you’re the type who hates losing, even to yourself.

Purpose of the Code (Object)

The code auto-generates quiz questions based on your current filters. It randomizes both the content and the order, then provides a scoring system when you submit. There’s even a “show answers” toggle, so you can learn from your mistakes without a stern lecture.

AI Prompt

Please add the following function:
Quiz mode

  • Auto-generate 10 questions (“Which came first?”, “Match civ→years”).
  • Accept: scoring & “show answers” toggle.

Functions & Features

  • Generate random quiz questions from filtered civilizations and events.
  • Support multiple-choice or “which came first” style questions.
  • Return a score when the quiz is submitted.
  • Provide reset and show-answer options for replay and review.

Requirements / Setup

You’ll need:

  • Python 3.11

Installs:

pip install streamlit

Minimal Code Sample

import random

questions = random.sample(filtered_events, 10)

for q in questions:

    st.write(f”Which came first: {q[0].name} or {q[1].name}?”)

Picks 10 random event pairs and asks the user to guess which happened earlier.

The Civilization Timeline Builder

Notes / Lessons Learned

As long as I followed the instructions, it was surprisingly straightforward. Since it shares the same sidebar filters, I had to insert the quiz logic right after the filter selections.

To keep things flexible, I used st.number_input to let users decide how many questions they want. With the random library already installed, the program pulls events at random and builds the quiz. I even added two buttons: one to submit answers and another to reset the quiz entirely, giving users the choice to try again.

What struck me is how reusable this structure is. Today it’s history trivia, but tomorrow it could just as easily be a vocabulary quiz, a math drill, or even a fun little self-test on Python syntax. Who knew that adding a quiz mode could make history—and coding—feel a lot like game night?

Optional Ideas for Expansion

  • Add a timer for extra pressure (and bragging rights).
  • Track high scores across sessions.

Let users choose the quiz type: matching, multiple-choice, or true/false.

Exporting Timelines as CSV and PNG in Streamlit

Day 92 of 100 Days Coding Challenge: Python

Today I taught my app how to pack its bags and leave. In other words, I added import and export features. Sure, I could already copy-paste data or grab a screenshot with Greenshot, but sometimes you want your history neat and official—like a CSV for number crunching, a JSON for data nerds, or even a PNG snapshot to show off.

As an accountant, I’m a little obsessed with documentation. If it isn’t written down (preferably in triplicate), it might as well not exist. So this feature speaks to my soul: a one-click way to preserve the work I’ve done without juggling external tools. The first step? Adding Plotly’s Kaleido, which lets charts gracefully turn into PNGs instead of stubbornly staring back at you.

Today’s Motivation / Challenge

Why does this matter? Because sometimes you want to share your findings with others—or just keep a record for yourself—without re-running all the filters. Import/export turns the app into a portable historian. Think of it like saving leftovers: you can pack up today’s insights, reheat them later, and maybe even share with a friend.

Purpose of the Code (Object)

The code adds the ability to import data into the app and export filtered timelines as CSV, JSON, or PNG. That means you can save what you’re looking at, reload it later, or use it in another tool. It makes the app more than just an interactive toy—it becomes a proper record-keeper.

AI Prompt

I would like to add the following function:
Day 17 — Import/Export

  • CSV/JSON importers
  • Export filtered timeline as CSV + PNG snapshot
  • Accept: round-trip a tiny dataset; PNG file downloads

Functions & Features

  • Import datasets in CSV or JSON format.
  • Export filtered results as CSV for spreadsheets.
  • Save a PNG snapshot of the current timeline view.
  • Round-trip: test importing and exporting to confirm nothing gets lost.

Requirements / Setup

You’ll need:

  • Python 3.11

Installs:

pip install plotly kaleido streamlit

Minimal Code Sample

# Export timeline to PNG

fig.write_image(“timeline.png”)

# Export filtered data to CSV

df.to_csv(“filtered_timeline.csv”, index=False)

One saves your visualization as an image, the other exports your filtered data as a spreadsheet.

The Civilization Timeline Builder

Notes / Lessons Learned

Adding the helper functions was straightforward, but the placement of the sidebar UI block was trickier than expected. I initially dropped it in the wrong spot in streamlit_app.py, which triggered a spectacular cascade of errors. Once I moved it to sit neatly after the existing filters (year_range, selected_regions, selected_tags), everything clicked into place.

The PNG export was almost an afterthought—I nearly skipped it, exhausted after five hours of coding. But I pushed through, added it, and suddenly there it was: a crisp timeline image sitting on my desktop. Oddly enough, that little success gave me more motivation than any lecture on perseverance. Sometimes the best reward is seeing history look good in a file you can actually keep.

Optional Ideas for Expansion

  • Add Excel export for people who live in spreadsheets.
  • Bundle multiple outputs (CSV + PNG) into a single downloadable zip file.
  • Create an auto-backup option so every filter session saves itself without asking.