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.

Warm Winter Essentials: Switching to Insulated Pants After a Cold Season

Brian’s fitness journal after a brain stroke

Over the years, after my brain stroke, I came to realize it’s important to know the warm winter essentials. Today, I officially retired my old sweatpants and upgraded to a new pair my wife ordered for me. The decision was long overdue. She had noticed a hole in the knee—courtesy of our cat—and gently declared that the pants had reached the end of their honorable service life.

To be fair, I tend to use clothing for a very long time. If something still functions, I keep it. However, the hole had grown well beyond its original “claw-sized” stage, and the fabric itself had become noticeably thin. At that point, even I had to admit the insulation had quietly retired years ago.

My wife specifically searched for something very warm because I am almost always cold. She explained that some pants have better insulation, and my old pair once did too—before age gradually wore it away. Fabric, much like people, loses resilience over time.

Since today was laundry day, it felt like the perfect moment to make the swap. The difference was immediate. The new pants are significantly warmer, have no mysterious knee ventilation, and include a soft insulating inner layer. The warmth was almost surprising.

We also keep the house relatively cool in winter because my wife prefers a moderate indoor temperature. She usually sets it around 65°F (18°C), believing that overly hot houses in winter—and overly cold ones in summer—are not ideal for health. As a result, I normally rely on hoodies and extra layers to stay comfortable.

Since my brain stroke, my temperature regulation has not been the same. Without layered clothing, I often feel cold even on warmer days. At times, I can feel hot and cold simultaneously, which is as confusing as it sounds. It is as if my internal thermostat occasionally sends mixed signals.

One amusing detail: the inside of the new pants is so well insulated that the outside fabric can feel cool to the touch while the inside stays very warm. Apparently, my body heat now creates a cozy zone—because our kitten has started choosing my chest or belly as her preferred resting spot. Clearly, she has conducted her own thermal research and approved the results.

My wife even suggested buying an extra pair as a spare, but I declined. I still have another pair for laundry rotation, and buying too many would feel unnecessary. Warmth is important, but so is practicality.

That said, I must admit: I genuinely like these new pants. Sometimes, a small upgrade in daily comfort makes a noticeable difference—especially during a cold season where warmth quietly becomes a daily priority.

Building a Sources & Citations System in SQLModel

Day 91 of 100 Days Coding Challenge: Python

Today was a reminder that computers are ruthless sticklers for details. I decided to add sources linked to the detailed card—because just like in academic papers, you can’t just “borrow” information floating around the internet without tipping your hat to whoever wrote it first. Seems fair, right?

But of course, the minute I touched the database, I triggered the classic relationship drama. My code started yelling at me: “source not defined.” Over and over. It was like being haunted by an ex who just wouldn’t leave the room. I spent five solid hours untangling imports, relationships, and annotations, trying everything from brute force to delicate tweaks. Eventually, the stars aligned, and the errors gave up. Victory never felt so earned.

Today’s Motivation / Challenge

Why spend a day on sources? Because information without sources is gossip, and gossip doesn’t belong in a timeline app. Adding citations isn’t just academic—it’s about trust. If you’re building a history tool, you want to know where the facts came from. Think of it like putting a bibliography on your app so it won’t get expelled for plagiarism.

Purpose of the Code (Object)


This code connects events and civilizations in the database to their sources, so each detail card can show where the information came from. Instead of a bare timeline with floating facts, now you get credibility built in. It’s like footnotes, but without the foot pain.

AI Prompt (for reference)

Day 16 — Sources & citations

  • sources table; link events/civs to sources; show bibliography per page.

Accept: civ detail lists sources with links.

Functions & Features

  • Add and store sources in a dedicated table.
  • Link sources to both events and civilizations.
  • Display a neat bibliography with clickable references on detail pages.

Requirements / Setup

  • Python 3.11+
  • Install SQLModel and SQLAlchemy 2.0+

pip install sqlmodel sqlalchemy

Minimal Code Sample

from typing import TYPE_CHECKING

from sqlmodel import SQLModel, Field, Relationship

class Source(SQLModel, table=True):

    id: int = Field(primary_key=True)

    title: str

    # Source linked to events

    events: list[“Event”] = Relationship(back_populates=”sources”)

# Ensures circular imports don’t trip us up

if TYPE_CHECKING:

    from .models import Event

This shows how a Source model can safely connect to events without circular import chaos.

The Civilization Timeline Builder

Notes / Lessons Learned

Those “source not defined” errors? Almost always circular-import headaches. The fix: postpone annotation evaluation, only import for type checking, and pass actual class objects instead of string guesses. SQLAlchemy 2.0 makes you play by the rules, and if you don’t, it punishes you with cryptic error messages.

After five hours of trial and error, I realized programming is like fixing plumbing: leaks pop up where you least expect them, and you just have to stay calm, systematic, and maybe a little stubborn. My biggest takeaway? Debugging isn’t a sprint—it’s therapy with a keyboard.

Optional Ideas for Expansion

  • Add clickable links so users can jump straight to the source online.
  • Allow filtering events by source—for example, “show me all events documented by Herodotus.”
  • Add a “source credibility” rating, so you can separate legends from solid history.