Python API
The imo_vmdb package provides a Python API that can be used independently
of any web framework or HTTP connection. It covers importing, normalising,
cleaning up, exporting, and querying meteor observation data.
Database connection
- class imo_vmdb.DBAdapter(config: Mapping[str, Any])
Lightweight database connection adapter supporting SQLite, PostgreSQL, and MySQL.
A dedicated adapter is used instead of a heavier abstraction (e.g. SQLAlchemy) because the SQL in this project is straightforward and the only cross-backend difference is placeholder syntax. The adapter handles three concerns:
Dynamic driver selection — the DB-API 2.0 module (
sqlite3,psycopg2,mysql.connector, …) is imported at runtime from config, so no specific driver is a hard dependency of the package.SQLite initialisation — enables foreign-key enforcement via
PRAGMA foreign_keys = ON, which SQLite disables by default.Placeholder normalisation — internally,
%(name)s-style named placeholders are rewritten to the active backend’s dialect.
The default backend is
sqlite3; a different module can be selected via themodulekey in config.- Parameters:
config –
Connection parameters. The optional
modulekey names the DB-API 2.0 module to load (default:"sqlite3"); all remaining keys are forwarded verbatim to that module’sconnect()call.Typical keys per backend:
SQLite —
database: path to the.dbfile.PostgreSQL (
module = psycopg2) —database,user, optionallyhost,password.MySQL (
module = pymysql) —database,user, optionallyhost,password, plus any driver-specific keys such assql_modeorinit_command.
These correspond directly to the
[database]section of the imo-vmdb configuration file.
- close() None
Close the database connection.
- commit() None
Commit the current transaction.
- cursor() Any
Return a new database cursor.
- Returns:
A new cursor object from the underlying DB-API 2.0 connection.
- ping() None
Verify that the database accepts a trivial query.
Issues a
SELECT 1and discards the result. Intended for liveness/readiness checks (e.g./healthendpoints).- Raises:
DBException – If the underlying driver raises any error.
- rollback() None
Roll back the current transaction.
The keys of the config dict correspond directly to the [database]
section of the configuration file (see Setup). Examples:
import imo_vmdb
# SQLite
db = imo_vmdb.DBAdapter({"database": "/path/to/vmdb.db"})
# PostgreSQL
db = imo_vmdb.DBAdapter({
"module": "psycopg2",
"database": "vmdb",
"user": "vmdb",
"host": "localhost",
})
# MySQL
db = imo_vmdb.DBAdapter({
"module": "pymysql",
"database": "vmdb",
"user": "vmdb",
})
- class imo_vmdb.DBException
Raised when a database operation fails.
Operations
- imo_vmdb.cleanup(db_conn: DBAdapter, logger: Logger) int
Remove all previously imported data, if any, while preserving normalized data in the database.
This function takes an existing database connection and a logger object as parameters. It removes all previously imported data from the database, leaving normalized data intact.
- Parameters:
db_conn – An open database connection implementing DB-API 2.0.
logger (logging.Logger) – A logger object used to log errors, warnings, and additional information.
- Returns:
An integer indicating the result of the operation. 0 for success, other values for errors.
- Return type:
int
- imo_vmdb.export_db(src_db_conn: DBAdapter, dst_conn: Connection) None
Export normalized observations and reference data into a SQLite database.
Creates the full imo-vmdb schema on dst_conn (dropping any pre-existing tables) and copies all rows from the normalized and reference tables of src_db_conn into it. The raw
imported_*tables are intentionally skipped — an exported database is a clean snapshot of normalized data that can be shared and used as the input of a fresh imo-vmdb install.The destination connection is committed but not closed; its lifecycle is owned by the caller. This allows in-memory destinations (e.g.
sqlite3.connect(":memory:")) to be inspected after export.- Parameters:
src_db_conn – Source database connection. May be SQLite, PostgreSQL, or MySQL.
dst_conn – Empty (or to-be-overwritten) SQLite destination connection. Must be a real
sqlite3.Connection; other DB-API drivers are not supported as the destination.
- imo_vmdb.export_table(db_conn: DBAdapter, table: str, reimport: bool = False) tuple[list[str], list[tuple]]
Export all rows from a database table.
When reimport is
Trueand table is'shower', the result uses column names and date formats that are compatible withCSVImporter, so the exported CSV can be imported again without modification. For all other tables reimport has no effect.- Parameters:
db_conn – An open database connection implementing DB-API 2.0.
table – Name of the table to export. Must be one of the exportable normalized tables:
shower,radiant,obs_session,magnitude,rate,magnitude_detail. Rawimported_*tables are intentionally not exportable.reimport – If
True, export in re-import-compatible format where applicable.
- Returns:
Tuple of
(column_names, rows).- Raises:
ValueError – If table is not an exportable table name.
- imo_vmdb.initdb(db_conn: DBAdapter, logger: Logger) int
Initialize an empty database, removing all data if the database already exists.
This function takes an existing database connection and a logger object as parameters. It initializes an empty database, removing all data if the database already exists.
- Parameters:
db_conn – An open database connection implementing DB-API 2.0.
logger (logging.Logger) – A logger object used to log errors, warnings, and additional information.
- Returns:
An integer indicating the result of the operation. 0 for success, 1 for errors.
- Return type:
int
- imo_vmdb.normalize(db_conn: DBAdapter, logger: Logger) int
Establish relationships between imported records and enrich observations with additional information.
This function takes an existing database connection and a logger object as parameters. It establishes relationships between the imported records in the database, enriching observations with additional information.
- Parameters:
db_conn – An open database connection implementing DB-API 2.0.
logger (logging.Logger) – A logger object used to log errors, warnings, and additional information.
- Returns:
An integer indicating the result of the operation. 0 for success, 1 for errors.
- Return type:
int
Exporting a whole database
export_db() writes the normalized observations and
reference data of a source database into an empty SQLite destination,
using the same schema as a regular imo-vmdb database. The resulting
file can be shared and re-used as the input of another imo-vmdb
installation. The raw imported_* tables are intentionally
excluded.
import sqlite3
import imo_vmdb
src = imo_vmdb.DBAdapter({"database": "/path/to/vmdb.db"})
# Export to a portable SQLite file:
dst = sqlite3.connect("/tmp/snapshot.sqlite")
imo_vmdb.export_db(src, dst)
dst.close()
- class imo_vmdb.CSVImporter(db_conn: DBAdapter, logger: Logger, do_delete: bool = False, try_repair: bool = False, is_permissive: bool = False)
A class for importing CSV files of various types into a database.
The CSVImporter class allows to import CSV files into a database. You can specify whether to delete existing data, attempt data repair, or be permissive about non-critical data errors during the import.
- Parameters:
db_conn – An existing database connection implementing DB-API 2.0.
logger (logging.Logger) – A logger object used to log errors, warnings, and additional information.
do_delete (bool) – If True, delete existing data before importing. Default is False.
try_repair (bool) – If True, attempt data repair during import. Default is False.
is_permissive (bool) – If True, be permissive about non-critical data errors. Default is False.
- run(file_list: Sequence[str]) None
Import CSV files specified in the files_list into the database.
This method imports CSV files into the database, with options to delete existing data, attempt data repair, and be permissive about non-critical data errors. After running this method, you can check the has_errors, counter_read, and counter_write properties of this object to determine the import result.
- Parameters:
file_list (list of str) – A list of file paths to CSV files for import.
Importing data
SessionImporter is a programmatic alternative to CSV files
when you need to persist a complete observation session (with its rates and
magnitudes) directly from Python. The caller controls the transaction:
from datetime import datetime
import imo_vmdb
db = imo_vmdb.DBAdapter({"database": "/path/to/vmdb.db"})
session = imo_vmdb.SessionImport(
id=12345,
latitude=52.0,
longitude=13.4,
country="DE",
location_name="Berlin",
rates=(
imo_vmdb.RateImport(
id=700001,
session_id=12345,
shower="PER",
period_start=datetime(2025, 8, 12, 22, 0, 0),
period_end=datetime(2025, 8, 12, 23, 0, 0),
t_eff=1.0,
f=1.0,
lim_magn=6.2,
method="C",
freq=17,
),
),
)
cur = db.cursor()
imp = imo_vmdb.SessionImporter(cur)
try:
imp.upload(session) # raises DuplicateSessionError if session already exists
db.commit()
except Exception:
db.rollback()
raise
Use upload(..., replace=True) to overwrite an existing session, or
delete() to remove one. Call
normalize() afterwards to make the data visible in the
normalized tables (obs_session, rate, magnitude). Log verbosity
can be adjusted via logging.getLogger("imo_vmdb").setLevel(...).
- class imo_vmdb.SessionImporter(cur)
Issues DB statements for
SessionImport-based operations.The caller owns both the connection (transaction) and the cursor. This class neither commits, rolls back, nor opens a cursor of its own — all statements are issued on the cursor passed at construction time. Combine multiple operations into one transaction by issuing them on the same cursor between a single
db.commit()/db.rollback()pair.- Parameters:
cur – An open DB-API 2.0 cursor.
- delete(session_id: int) bool
Issue DELETE statements for session_id and all its rates and magnitudes.
- Parameters:
session_id – ID of the session to remove.
- Returns:
Trueif the session existed (statements were issued),Falseif no row with that id exists (no statement issued).- Raises:
DBException – On database error.
- upload(session: SessionImport, replace: bool = False) None
Issue INSERT statements for session and its children.
- Parameters:
session – Session to persist.
replace – When
Trueand a session with the same id already exists, DELETE statements for that session and its rates and magnitudes are issued first. WhenFalse(the default), a pre-existing session raisesDuplicateSessionErrorbefore any statement is issued.
- Raises:
DuplicateSessionError – If
session.idalready exists andreplaceisFalse.DBException – On database error.
- class imo_vmdb.SessionImport(id: int, latitude: float, longitude: float, country: str, location_name: str, observer_id: int | None = None, observer_name: str | None = None, elevation: float | None = None, rates: tuple[RateImport, ...] = (), magnitudes: tuple[MagnitudeImport, ...] = ())
An observation session plus its rates and magnitudes, ready to upload.
Pure container. The caller is responsible for providing sensible values.
- class imo_vmdb.RateImport(id: int, session_id: int, period_start: datetime, period_end: datetime, t_eff: float, f: float, lim_magn: float, method: str, freq: int, observer_id: int | None = None, shower: str | None = None, ra: float | None = None, dec: float | None = None)
A single rate observation to be uploaded together with its parent session.
Pure container. The caller is responsible for providing sensible values.
- class imo_vmdb.MagnitudeImport(id: int, session_id: int, period_start: datetime, period_end: datetime, magn: dict[int, float], observer_id: int | None = None, shower: str | None = None)
A single magnitude observation to be uploaded together with its parent session.
Pure container. The caller is responsible for providing sensible values.
- exception imo_vmdb.DuplicateSessionError
Raised when an upload targets a session id that already exists.
Service classes
Each service class wraps a DBAdapter connection and
exposes the queries available for one entity. The query method
returns the matching list (with optional pagination/total), by_id /
by_code return a single record (or None).
- class imo_vmdb.RateService(db_conn: DBAdapter)
Service for rate observation queries.
- Parameters:
db_conn – An open
DBAdapterconnection.
- query(f: RateFilter) Rates
Return rate observations matching f.
- Parameters:
f – A
RateFilterspecifying filter criteria and includes.- Returns:
A
Ratesinstance.sessions,magnitudesandmagnitude_detailsare only set when the corresponding flags on f areTrue.totalis set whenwith_totalisTrueor when pagination is in use.
- class imo_vmdb.MagnitudeService(db_conn: DBAdapter)
Service for magnitude observation queries.
- Parameters:
db_conn – An open
DBAdapterconnection.
- by_id(magn_id: int) Magnitude | None
Return a single magnitude observation by ID, or
Noneif not found.
- query(f: MagnitudeFilter) Magnitudes
Return magnitude observations matching f.
- Parameters:
f – A
MagnitudeFilterspecifying filter criteria and includes.- Returns:
A
Magnitudesinstance.sessionsandmagnitude_detailsare only set when the corresponding flags on f areTrue.totalis set whenwith_totalisTrueor when pagination is in use.
- class imo_vmdb.SessionService(db_conn: DBAdapter)
Service for observation session queries.
- Parameters:
db_conn – An open
DBAdapterconnection.
- query(f: SessionFilter) Sessions
Return sessions matching f.
- class imo_vmdb.ShowerService(db_conn: DBAdapter)
Service for meteor shower reference data and radiants.
- Parameters:
db_conn – An open
DBAdapterconnection.
- active(on_date: date) list[Shower]
Return all showers whose activity period covers on_date.
Handles year-wrapping showers (e.g. start in December, end in January) by treating the period as inclusive on both ends.
- Parameters:
on_date – Calendar date to test against each shower’s
start_*/end_*fields. Year is ignored.- Returns:
List of matching
Showerinstances.
- class imo_vmdb.StatsService(db_conn: DBAdapter)
Aggregate statistics over the observation database.
All
by_*methods accept an optionalperiod_start/period_endfilter (ISO date strings) that restricts the rate and magnitude tables before aggregation. Sessions are not period-filtered formetaandby_countryto keep the semantics simple (“how many sessions are in the database”).- Parameters:
db_conn – An open
DBAdapterconnection.
- by_country(period_start: datetime | None = None, period_end: datetime | None = None) list[CountryStat]
Return per-country counts of sessions, rates and magnitudes.
- by_shower(period_start: datetime | None = None, period_end: datetime | None = None) list[ShowerStat]
Return per-shower counts of rates and magnitudes.
Filter types
- class imo_vmdb.RateFilter(showers: list[str] = <factory>, period_start: ~datetime.datetime | None = None, period_end: ~datetime.datetime | None = None, sl_min: float | None = None, sl_max: float | None = None, lim_magn_min: float | None = None, lim_magn_max: float | None = None, sun_alt_max: float | None = None, moon_alt_max: float | None = None, session_ids: list[int] = <factory>, rate_ids: list[int] = <factory>, include_sessions: bool = False, include_magnitudes: bool = False, include_magnitude_details: bool = False, limit: int | None = None, offset: int | None = None, order_by: str | None = None, order: str | None = None, with_total: bool = False)
Filter criteria for rate observation queries.
- Parameters:
showers – IAU shower codes to include; use
'SPO'for sporadics.period_start – Include only observations starting at or after this
datetime.datetime(UTC).period_end – Include only observations ending at or before this
datetime.datetime(UTC).sl_min – Minimum solar longitude (start of period).
sl_max – Maximum solar longitude (end of period).
lim_magn_min – Minimum limiting magnitude.
lim_magn_max – Maximum limiting magnitude.
sun_alt_max – Maximum sun altitude in degrees.
moon_alt_max – Maximum moon altitude in degrees.
session_ids – Restrict to specific session IDs.
rate_ids – Restrict to specific rate record IDs.
include_sessions – If
True, include asessionslist in the result.include_magnitudes – If
True, include amagnitudeslist with the fullMagnitudeobservations referenced by the returned rates via theirmagn_id.include_magnitude_details – If
True, include amagnitude_detailslist with the per-magnitude-class frequencies (frommagnitude_detail) for the magnitude observations referenced by the returned rates.
- class imo_vmdb.MagnitudeFilter(showers: list[str] = <factory>, period_start: ~datetime.datetime | None = None, period_end: ~datetime.datetime | None = None, sl_min: float | None = None, sl_max: float | None = None, lim_magn_min: float | None = None, lim_magn_max: float | None = None, session_ids: list[int] = <factory>, magn_ids: list[int] = <factory>, include_sessions: bool = False, include_magnitude_details: bool = False, limit: int | None = None, offset: int | None = None, order_by: str | None = None, order: str | None = None, with_total: bool = False)
Filter criteria for magnitude observation queries.
- Parameters:
showers – IAU shower codes to include; use
'SPO'for sporadics.period_start – Include only observations starting at or after this
datetime.datetime(UTC).period_end – Include only observations ending at or before this
datetime.datetime(UTC).sl_min – Minimum solar longitude (start of period).
sl_max – Maximum solar longitude (end of period).
lim_magn_min – Minimum limiting magnitude.
lim_magn_max – Maximum limiting magnitude.
session_ids – Restrict to specific session IDs.
magn_ids – Restrict to specific magnitude record IDs.
include_sessions – If
True, include asessionslist in the result.include_magnitude_details – If
True, include amagnitude_detailslist with the per-magnitude-class frequencies (frommagnitude_detail) for each magnitude observation.
- class imo_vmdb.SessionFilter(observer_ids: list[int] = <factory>, showers: list[str] = <factory>, period_start: ~datetime.datetime | None = None, period_end: ~datetime.datetime | None = None, sl_min: float | None = None, sl_max: float | None = None, lim_magn_min: float | None = None, lim_magn_max: float | None = None, include_rates: bool = False, include_magnitudes: bool = False, limit: int | None = None, offset: int | None = None, order_by: str | None = None, order: str | None = None, with_total: bool = False)
Filter criteria for session queries.
The observation-level filters (
showers,period_*,sl_*,lim_magn_*) match sessions that have at least one rate or magnitude observation satisfying the criteria. When an include is requested (include_rates/include_magnitudes), the returned observations are filtered by the same criteria, restricted to the session IDs in the result page.- Parameters:
observer_ids – Restrict to specific observer IDs.
showers – IAU shower codes to include; use
'SPO'for sporadics.period_start – Include only sessions with at least one rate or magnitude observation starting at or after this
datetime.datetime(UTC).period_end – Include only sessions with at least one rate or magnitude observation ending at or before this
datetime.datetime(UTC).sl_min – Minimum solar longitude (start of period) across rate/magnitude.
sl_max – Maximum solar longitude (end of period) across rate/magnitude.
lim_magn_min – Minimum limiting magnitude across rate/magnitude.
lim_magn_max – Maximum limiting magnitude across rate/magnitude.
include_rates – If
True, include arateslist of fullRateobservations in the result.include_magnitudes – If
True, include amagnitudeslist of fullMagnitudeobservations in the result.limit – Maximum number of sessions to return.
offset – Number of leading sessions to skip.
order_by – Sort column (whitelist:
id,country,observer_id).order –
"asc"or"desc".with_total – If
True, populateSessions.total.
Result types
- class imo_vmdb.Shower(iau_code: str, name: str, start_month: int, start_day: int, end_month: int, end_day: int, peak_month: int | None, peak_day: int | None, ra: float | None, dec: float | None, v: float | None, r: float | None, zhr: float | None)
Single meteor shower from the reference catalogue.
See Field Reference for field descriptions.
- class imo_vmdb.Session(id: int, longitude: float, latitude: float, elevation: float, country: str, location_name: str, observer_id: int | None, observer_name: str | None)
Observation session linked to rate or magnitude observations.
See Field Reference for field descriptions.
- class imo_vmdb.Rate(id: int, shower: str | None, period_start: datetime, period_end: datetime, sl_start: float, sl_end: float, session_id: int, freq: int, lim_magn: float, t_eff: float, f: float, sidereal_time: float, sun_alt: float, sun_az: float, moon_alt: float, moon_az: float, moon_illum: float, field_alt: float | None, field_az: float | None, rad_alt: float | None, rad_az: float | None, magn_id: int | None, magn_solo: bool | None)
Single normalised rate observation returned by
RateService.query().See Field Reference for field descriptions.
magn_idlinks to the associatedMagnitudeobservation, or isNonewhen no matching magnitude observation exists.magn_soloisTruewhen this rate is the only contributor to its linked magnitude (their periods match exactly),Falsewhen the magnitude observation aggregates this rate together with others over a longer period, andNonewhenmagn_idisNone.
- class imo_vmdb.Magnitude(id: int, shower: str | None, period_start: datetime, period_end: datetime, sl_start: float, sl_end: float, session_id: int, freq: int, mean: float, lim_magn: float | None)
Single normalised magnitude observation returned by
MagnitudeService.query().See Field Reference for field descriptions.
- class imo_vmdb.MagnitudeDetail(id: int, magn: int, freq: float)
Per-class frequency entry for a magnitude observation.
See Field Reference for field descriptions.
- class imo_vmdb.Radiant(shower: str, month: int, day: int, ra: float, dec: float)
Radiant position of a meteor shower at a given calendar day.
- class imo_vmdb.Rates(observations: list[Rate], sessions: list[Session] | None = None, magnitudes: list[Magnitude] | None = None, magnitude_details: list[MagnitudeDetail] | None = None, total: int | None = None)
Return value of
RateService.query().
- class imo_vmdb.Magnitudes(observations: list[Magnitude], sessions: list[Session] | None = None, magnitude_details: list[MagnitudeDetail] | None = None, total: int | None = None)
Return value of
MagnitudeService.query().
- class imo_vmdb.Sessions(sessions: list[Session], rates: list[Rate] | None = None, magnitudes: list[Magnitude] | None = None, total: int | None = None)
Return value of
SessionService.query().
- class imo_vmdb.StatsMeta(sessions: int, rates: int, magnitudes: int, period_start: datetime | None, period_end: datetime | None, rate_meteors: int = 0, magnitude_meteors: int = 0, rate_period_start: datetime | None = None, rate_period_end: datetime | None = None, magnitude_period_start: datetime | None = None, magnitude_period_end: datetime | None = None, imported_sessions: int = 0, imported_rates: int = 0, imported_magnitudes: int = 0, imported_rate_meteors: int = 0, imported_magnitude_meteors: int = 0)
Database scope summary returned by
StatsService.meta().sessions/rates/magnitudesand*_meteorscover the normalized tables; theimported_*fields cover the raw imported tables that feed normalization.*_meteorsare the sum of thefreqcolumn on the rate/magnitude rows.period_start/period_endis the union ofrate_period_*andmagnitude_period_*(kept for backward compatibility).
- class imo_vmdb.ShowerStat(shower: str | None, rates: int, magnitudes: int)
Per-shower aggregate counts.
showerisNonefor sporadics.
- class imo_vmdb.CountryStat(country: str, sessions: int, rates: int, magnitudes: int)
Per-country aggregate counts.
- class imo_vmdb.YearStat(year: int, rates: int, magnitudes: int)
Per-year aggregate counts.
WSGI deployment
The web UI and REST API can be hosted under any WSGI server using the
public app factory imo_vmdb.httpd.wsgi_app. Configuration is read
from the IMO_VMDB_CONFIG environment variable (path to an INI file)
or directly from IMO_VMDB_* variables (see Setup).
- imo_vmdb.httpd.wsgi_app()
WSGI app factory for Gunicorn
--factorymode.Configuration is read from the
IMO_VMDB_CONFIGenvironment variable (path to an INI file) or fromIMO_VMDB_DATABASE_*etc. directly. Host and port are controlled by Gunicorn’s--bindflag.The Web UI is opt-in: set
IMO_VMDB_WEBSERVER_ENABLE_WEBUI=trueto enable it. By default only the REST API is served.Example:
gunicorn --workers 1 --threads 4 "imo_vmdb.httpd:wsgi_app()"
Warning
Always use
--workers 1. TheJobManagerstores job state in-process; multiple workers would make jobs invisible across processes, breaking status polling and log streaming.
Example using Gunicorn (install with pip install "imo-vmdb[web]"):
# With a config file:
IMO_VMDB_CONFIG=config.ini \
gunicorn --workers 1 --threads 4 \
--bind 127.0.0.1:8000 "imo_vmdb.httpd:wsgi_app()"
# Without a config file:
IMO_VMDB_DATABASE_DATABASE=./vmdb.db \
gunicorn --workers 1 --threads 4 \
--bind 127.0.0.1:8000 "imo_vmdb.httpd:wsgi_app()"
Warning
Always use --workers 1. The job manager stores job state in-process;
multiple workers would make jobs invisible across processes, breaking status
polling and log streaming.