This is Flask-based collection manager for Finnish Science Fiction, Fantasy and Horror books. Contents and database schema is based on "The Bibliography of of Finnish Sf-, Fantasy- and Horror Literature". This is why interface is also only in Finnish. There are errors in the data as it was impossible to import everything correctly.
There is an importer (bib_import.py) which can fill the database with the data from the bibliography. Because it will be possible to add and edit everything in the database it could also be possible to add books by hand in the future.
Application uses Flask framework and SQLAlchemy ORM. Bootstrap css files are missing from this package at the moment.
The database has been designed in such a way it can store different kinds of books but this might also make the structure less obvious. As the import data includes Finnish books only (original and translations) I will use those as examples on how to deal with some situations. This chapter talks mainly about the trinity of Work, Edition and Part.
Work is the original work and does not really represent anything concrete. It's the work done by one or several authors and covers every version printed.
Edition is a single edition of the book in one language. So for instance, every edition in English has their own row in this table.
Part is whatever parts an edition and/or work contains. Short stories can be listed as parts allowing collections where some edition is not based strictly on some work but could be either a combination or just part of one. Sometimes works are also split into several volumes and in some cases multiple works are combined into one book. This table allows expressing these cases. Title is always the name in the edition.
There must always be at least one row in each of these three tables for any book.
- The simple case: First edition of a book: There should be one row in each of Work, Edition and Part and nothing else.
- Another edition of a book: There should be another row in Edition and Part but only the original row in Work.
- A work split into multiple parts. For example Frank Herbert's Dune was split into three parts in Finland. This would be represented as one row in Work and three rows in Edition and Part.
- Two works combined into one edition, e.g. a compendum of Shakespeare's plays. This should have one Work and Part row for each play and one row in Edition for the compendum.
- A collection translated into another language. Edition and Work both have one row while every short story has a row in Part and also the ShortStory table. Title in the Part row has the translated name while Title in ShortStory holds the original name of the story.
This is not implemented yet. Also very WIP anyways and lacks descriptions for return values.
Strings used as search parameters are always used with ilike operator. So to search using a part of a string, you can use the percentage sign, e.g. using "Asimov%" as the person's full name will find all the names starting with Asimov (case independent).
All replies are in JSON.
POST /api/1.0/auth
Parameters:
- username
- password
GET /api/works
Parameters:
- workid
- title
- authorid
- language
Returns works that match the search criteria which can be any of the parameters.
GET /api/editions
Parameters_
- workid
- editionid
- title
- authorid
- language
GET /api/work/{workid}
GET /api/edition/{editionid}
GET /api/persons
Parameters:
- fullname
- last_name
- first_name
- dob
- dod
- birthplace
- type (A = author, T = translator, E = editor)
GET /api/person/{personid}
Requires authentication.
GET /api/collection
The project includes a comprehensive API test suite with 238 tests covering 158 endpoints.
Install development dependencies:
pdm install -dCreate a dedicated test database with cloned data from the main database:
pdm run python tests/scripts/setup_test_db.py --cloneOptions:
--clone- Clone data from main database (recommended for first setup)--users-only- Only create test users (skip database creation)
This script:
- Fixes PostgreSQL collation version mismatches
- Creates the
suomisf_testdatabase - Clones data from the main database
- Sets up the correct schema search path
- Creates test users (regular and admin)
Run all tests:
pdm run pytest tests/Run tests with verbose output:
pdm run pytest tests/ -vRun specific test file:
pdm run pytest tests/api/test_stats.py -vSnapshot tests compare API responses against stored "golden" responses from the database. This ensures API behavior remains consistent after code changes.
Generating/Updating Snapshots:
After database schema or data changes, regenerate the snapshots:
pdm run python tests/scripts/update_snapshots.pyThis captures responses from all configured endpoints and stores them in tests/fixtures/snapshots/.
Listing Endpoints:
To see which endpoints are configured for snapshot testing:
pdm run python tests/scripts/update_snapshots.py --listThe test suite clones the suomisf database live into suomisf_test
(pg_dump/psql, via clone_test_database() in conftest.py) at the start
of every full run, then applies the numbered migration SQL files and creates
the test users. This happens automatically - no separate dump/restore step is
needed. Use --skip-db-setup to skip re-cloning when iterating on a single
test file against an already-set-up database.
Run API performance benchmarks:
pdm run python -m tests.benchmark.benchmark_runnerBenchmark results are stored in tests/benchmark/results/ with git hash tracking.
The test coverage status is tracked in tests/API_COVERAGE.md, which includes:
- Status of all 158 API endpoints
- Test history with git hashes
- Pass/fail statistics
tests/
├── api/ # API endpoint tests
│ ├── base_test.py # Base test class with helpers
│ ├── test_auth.py # Authentication & authorization (33 tests)
│ ├── test_entities.py # Entity CRUD endpoints
│ ├── test_filters.py # Filter and search endpoints
│ ├── test_related.py # Nested/related endpoints
│ ├── test_stats.py # Statistics endpoints (32 tests)
│ └── test_misc.py # Miscellaneous endpoints (36 tests)
├── fixtures/
│ ├── snapshots/ # API response snapshots (~35 files)
│ └── test_parameters.json # Parameterized test data
├── scripts/
│ ├── setup_test_db.py # Test database setup (create, clone, users)
│ └── update_snapshots.py # Snapshot generator
├── benchmark/
│ └── benchmark_runner.py # Performance benchmarks
├── results/ # Test run results
├── conftest.py # Pytest fixtures
└── API_COVERAGE.md # Coverage tracking (238 tests, 158 endpoints)