Skip to main content

SQLite database for tracking SRS learning state with FSRS and Ebisu algorithms

Project description

srsdb

Canary Build and Test Release to PyPI PyPI version Python 3.8+ Type Checked with mypy License: MIT

A Python library for managing Spaced Repetition System (SRS) learning data using SQLite. Track the state of individual learning items with sophisticated scheduling algorithms.

Features

  • 🧠 Multiple SRS Algorithms: FSRS (deterministic) and Ebisu (Bayesian)
  • 💾 SQLite Backend: Persistent storage with flexible schema
  • 📊 Detailed Tracking: Monitor difficulty, stability, review history, and more
  • 🔄 Flexible Correctness: Rate answers on a 0-100 scale
  • 🧪 Well Tested: Comprehensive test suite with 35+ unit tests
  • 🐍 Python 3.8+: Compatible with modern Python versions
  • 🔌 Optional Dependencies: Use FsrsDatabase without any dependencies, add Ebisu when needed

Installation

From PyPI (Recommended)

Install the latest stable release from PyPI:

# Basic installation (FSRS only, no dependencies)
pip install srsdb

# With Ebisu support
pip install srsdb[ebisu]

From Source

For development or to use the latest code:

# Clone the repository
git clone https://github.com/jomof/srsdb.git
cd srsdb

# Install in development mode
pip install -e .

# Or with development tools
pip install -e ".[dev]"

Example Databases

Explore pre-built example databases in the example/ directory:

  • example/fsrs.db - Spanish vocabulary learning with FSRS (10 cards, 25 reviews)
  • example/ebisu.db - Python programming concepts with Ebisu (10 cards, 44 reviews)
# View statistics
python example/view_stats.py

# Regenerate examples
python example/generate_examples.py

# Explore with SQLite
sqlite3 example/fsrs.db "SELECT * FROM fsrs_cards;"

See example/README.md for more details.

Quick Start

from datetime import datetime
from fsrs_database import FsrsDatabase

# Create a database instance
db = FsrsDatabase("my_learning.db")

# Record answering a question
now = datetime.now()
db.answer(now, "question_id_1", correctness=85)  # 85% correct

# Get cards that are due for review
due_cards = db.next(now)
print(f"Cards to review: {due_cards}")

# Check when the next review is scheduled
next_review = db.next_due_date()
print(f"Next review: {next_review}")

Interface: SrsDatabase

The SrsDatabase abstract base class defines the interface for all SRS implementations:

Constructor

SrsDatabase(database_file: str)

Initializes the database with a file path. The file is created if it doesn't exist.

Methods

answer(now: datetime, question_key: str, correct: int) -> None

Records the result of answering a question.

Parameters:

  • now: The time the question was answered (doesn't have to be real-time, useful for testing)
  • question_key: Unique identifier for the question
  • correct: Correctness value from 0-100
    • 0 = completely wrong
    • 100 = completely correct
    • Values in between = degrees of correctness

The correctness value is internally converted to an appropriate value for the underlying SRS algorithm.

next(now: datetime) -> List[str]

Returns questions that are due for review as of the given time.

Parameters:

  • now: The current time to check against

Returns:

  • List of question keys in chronological order of due date

next_due_date() -> Optional[datetime]

Returns the date/time when the next question becomes due.

Returns:

  • The next due date, or None if no questions are scheduled

Implementation: FsrsDatabase

The FsrsDatabase class implements the SrsDatabase interface using the FSRS algorithm, which tracks:

  • Difficulty: How hard the card is (0-10 scale)
  • Stability: Memory stability in days
  • State: Card state (new, learning, review, relearning)
  • Review History: Complete history of all reviews
  • Lapses: Number of times the card was forgotten

Example Usage

from datetime import datetime, timedelta
from fsrs_database import FsrsDatabase

# Initialize database
db = FsrsDatabase("flashcards.db")

# Day 1: Learn new cards
day1 = datetime(2024, 1, 1, 10, 0, 0)

db.answer(day1, "spanish_hello", 90)      # Got it right!
db.answer(day1, "spanish_goodbye", 60)    # Mostly correct
db.answer(day1, "spanish_please", 20)     # Need more practice

# Day 2: Review due cards
day2 = day1 + timedelta(days=1)
due = db.next(day2)
print(f"Cards due: {due}")  # ['spanish_please']

for card in due:
    # Review the card and record the result
    db.answer(day2, card, 75)

# Day 7: Check what needs review
day7 = day1 + timedelta(days=7)
due = db.next(day7)
print(f"Cards due after a week: {due}")

# Check next scheduled review
next_review = db.next_due_date()
print(f"Next review scheduled: {next_review}")

Customizing FSRS Parameters

You can customize the FSRS algorithm behavior using FsrsKnobs:

from srsdb import FsrsDatabase, FsrsKnobs

# Create custom configuration
knobs = FsrsKnobs(
    rating_thresholds=(30, 60, 90),  # Stricter thresholds
    # w=[...] # You can also customize the 17 FSRS weight parameters
)

# Use custom configuration
db = FsrsDatabase("strict.db", knobs)

Configuration Options:

  • rating_thresholds: Tuple of (hard, good, easy) thresholds for converting correctness to ratings
    • Default: (25, 50, 85) means < 25 = Again, 25-49 = Hard, 50-84 = Good, ≥ 85 = Easy
  • w: List of 17 FSRS weight parameters controlling difficulty and stability calculations
    • Default: Research-based values providing good general-purpose scheduling

Advanced Example: Learning Session

from datetime import datetime, timedelta
from fsrs_database import FsrsDatabase

def study_session(db, current_time, responses):
    """Simulate a study session with multiple card reviews."""

    # Get cards due for review
    due_cards = db.next(current_time)
    print(f"\n📚 Study session at {current_time}")
    print(f"Cards to review: {len(due_cards)}")

    # Review each card
    for card_id in due_cards:
        correctness = responses.get(card_id, 50)
        db.answer(current_time, card_id, correctness)

        emoji = "✅" if correctness >= 70 else "⚠️" if correctness >= 40 else "❌"
        print(f"  {emoji} {card_id}: {correctness}% correct")

    # Show next review time
    next_due = db.next_due_date()
    if next_due:
        days_until = (next_due - current_time).days
        print(f"📅 Next review in {days_until} day(s)")

# Initialize
db = FsrsDatabase("study.db")
start = datetime(2024, 1, 1, 9, 0, 0)

# Day 1: Initial learning
study_session(db, start, {
    "capitals_france": 100,
    "capitals_germany": 85,
    "capitals_italy": 60,
    "capitals_spain": 40,
})

# Day 3: First review
study_session(db, start + timedelta(days=3), {
    "capitals_spain": 80,
    "capitals_italy": 90,
})

# Day 10: Follow-up review
study_session(db, start + timedelta(days=10), {
    "capitals_france": 95,
    "capitals_germany": 100,
    "capitals_italy": 85,
    "capitals_spain": 70,
})

Implementation: EbisuDatabase

The EbisuDatabase class implements the SrsDatabase interface using the Ebisu Bayesian algorithm, which tracks:

  • Alpha & Beta: Parameters of the Beta distribution on recall probability
  • Half-life (t): Expected time until recall probability reaches 0.5
  • Bayesian Updates: Uses quiz results to update beliefs about memory strength
  • Review History: Complete history with recall probabilities

Note: Requires the ebisu package: pip install ebisu

Example Usage

from datetime import datetime, timedelta
from ebisu_database import EbisuDatabase

# Initialize database (requires ebisu package)
db = EbisuDatabase("learning.db")

# Day 1: Learn new vocabulary
day1 = datetime(2024, 1, 1, 9, 0, 0)

db.answer(day1, "vocab_apple", 95)       # Nearly perfect
db.answer(day1, "vocab_banana", 70)      # Pretty good
db.answer(day1, "vocab_cherry", 40)      # Struggling

# Day 3: Review cards that are due
day3 = day1 + timedelta(days=3)
due = db.next(day3)
print(f"Cards needing review: {due}")

# Cards are ordered by recall probability (lowest first)
for card in due:
    db.answer(day3, card, 80)  # Review with 80% correctness

# Check when next review is needed
next_review = db.next_due_date()
print(f"Next review: {next_review}")

Customizing Ebisu Parameters

You can customize the Ebisu algorithm behavior using EbisuKnobs:

from srsdb import EbisuDatabase, EbisuKnobs

# Create custom configuration
knobs = EbisuKnobs(
    default_half_life_hours=12.0,  # Faster reviews (default: 24.0)
    recall_threshold=0.7,           # More conservative (default: 0.5)
    target_recall=0.7               # Higher target (default: 0.5)
)

# Use custom configuration
db = EbisuDatabase("custom.db", knobs)

Configuration Options:

  • default_half_life_hours: Initial half-life for new cards in hours
    • Default: 24.0 (1 day)
    • Lower values = more frequent initial reviews
  • recall_threshold: Recall probability threshold for considering cards due
    • Default: 0.5 (cards with < 50% recall probability are due)
    • Higher values = more conservative, cards reviewed earlier
  • target_recall: Target recall probability for scheduling
    • Default: 0.5 (schedule at half-life point)

Key Differences from FSRS

  • Bayesian: Uses probabilistic modeling vs deterministic scheduling
  • Adaptive: Continuously updates beliefs about memory strength
  • Flexible: Supports soft-binary quiz results (0.0-1.0 success values)
  • Half-life Based: Schedules reviews based on predicted forgetting curve

Database Schema

FsrsDatabase Tables

The FsrsDatabase creates two tables:

fsrs_cards

Stores the current state of each card:

  • question_key (PRIMARY KEY): Unique card identifier
  • difficulty: Card difficulty (0-10)
  • stability: Memory stability in days
  • state: Card state (0=new, 1=learning, 2=review, 3=relearning)
  • due_date: When the card is next due
  • reps: Total number of reviews
  • lapses: Number of times forgotten

fsrs_reviews

Historical record of all reviews:

  • id (PRIMARY KEY): Auto-incrementing review ID
  • question_key: Card being reviewed
  • review_time: When the review occurred
  • rating: FSRS rating (1-4)
  • state: Card state after review

EbisuDatabase Tables

The EbisuDatabase creates two tables:

ebisu_cards

Stores the current Bayesian model for each card:

  • question_key (PRIMARY KEY): Unique card identifier
  • alpha: Alpha parameter of Beta distribution
  • beta: Beta parameter of Beta distribution
  • t: Half-life in hours
  • last_review: Timestamp of last review
  • total_reviews: Total number of reviews

ebisu_reviews

Historical record of all reviews:

  • id (PRIMARY KEY): Auto-incrementing review ID
  • question_key: Card being reviewed
  • review_time: When the review occurred
  • correctness: Correctness value (0-100)
  • recall_probability: Predicted recall before this review

Testing

Run the test suite:

# Using unittest
python -m unittest discover -v

# Using pytest (if installed)
pytest -v

Implementation Status

  • SrsDatabase Interface: Complete
  • FsrsDatabase: Fully implemented with comprehensive tests (17+ tests)
  • EbisuDatabase: Fully implemented with comprehensive tests (18+ tests)

Future Enhancements

  • Export/import functionality
  • Statistics and analytics
  • Card tagging and filtering
  • Multiple deck support

Contributing

Contributions are welcome! Please ensure:

  • All tests pass (python -m unittest discover)
  • Code follows Python best practices
  • New features include tests

License

MIT License - See LICENSE file for details

See Also

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

srsdb-0.12.0.tar.gz (18.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

srsdb-0.12.0-py3-none-any.whl (17.3 kB view details)

Uploaded Python 3

File details

Details for the file srsdb-0.12.0.tar.gz.

File metadata

  • Download URL: srsdb-0.12.0.tar.gz
  • Upload date:
  • Size: 18.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for srsdb-0.12.0.tar.gz
Algorithm Hash digest
SHA256 55685aad940ccaa47c4b217e8e96f902e3f85059a5727b4aafc7cbd3eabf5562
MD5 bdf4d54648b61bae6f43579285e4621b
BLAKE2b-256 b82c3035f6d8d9cbe2df565308107356673fc06c970904266060760a5d55d888

See more details on using hashes here.

Provenance

The following attestation bundles were made for srsdb-0.12.0.tar.gz:

Publisher: release.yml on jomof/srsdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file srsdb-0.12.0-py3-none-any.whl.

File metadata

  • Download URL: srsdb-0.12.0-py3-none-any.whl
  • Upload date:
  • Size: 17.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for srsdb-0.12.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd98b11d3116349d803b582f403298bbc9467d257031f5a5d78e637a079e0b98
MD5 8791d0f4a430ff1f82f81bfc754073da
BLAKE2b-256 e9c521171df93d7b026a16b9dbc83876cbccc966d39cd2330c8278adf76ce672

See more details on using hashes here.

Provenance

The following attestation bundles were made for srsdb-0.12.0-py3-none-any.whl:

Publisher: release.yml on jomof/srsdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page