Skip to content

Commit

Permalink
Merge pull request #4 from MultiQC/buffer-visits
Browse files Browse the repository at this point in the history
Summarise visits by minute; buffer in memory and in CSV before DB
  • Loading branch information
ewels authored Feb 14, 2024
2 parents e422327 + 50d3d1a commit bcfe0f9
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 97 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,5 @@ dmypy.json
.idea/

.DS_Store

visits.csv
4 changes: 4 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""api.multiqc.info: Providing run-time information about available updates."""

__version__ = "0.1.0.dev0"

from dotenv import load_dotenv

load_dotenv()
47 changes: 23 additions & 24 deletions app/db.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,51 @@
"""Functions to interact with the database."""
import os

import datetime
from os import getenv

from sqlmodel import create_engine, Field, select, Session, SQLModel

sql_url = getenv("DATABASE_URL")
sql_url = os.getenv("DATABASE_URL")
assert sql_url is not None, sql_url
engine = create_engine(sql_url)


class Visit(SQLModel, table=True): # type: ignore # mypy doesn't like this, not sure why
"""Table to record raw individual visits to the version endpoint."""
class Visits(SQLModel, table=True): # type: ignore # mypy doesn't like this, not sure why
"""
Table to record per-minute visit summaries.
Start is a primary key, and start and end are both indexed.
"""

id: int | None = Field(default=None, primary_key=True)
version_multiqc: str | None = None
version_python: str | None = None
operating_system: str | None = None
installation_method: str | None = None
ci_environment: str | None = None
called_at: datetime.datetime | None = Field(default_factory=datetime.datetime.utcnow, nullable=False)
start: datetime.datetime = Field(primary_key=True)
end: datetime.datetime = Field(primary_key=True)
count: int
version_multiqc: str = Field(index=True)
version_python: str = Field(default=None, index=True)
operating_system: str = Field(default=None, index=True)
installation_method: str = Field(default=None, index=True)
ci_environment: str = Field(default=None, index=True)


def create_db_and_tables() -> None:
"""Create the database and tables if they don't exist."""
SQLModel.metadata.create_all(engine)


def add_visit(visit: Visit) -> None:
"""Add a visit to the database."""
with Session(engine) as session:
session.add(visit)
session.commit()


def get_visits(
start: datetime.datetime | None = None,
end: datetime.datetime | None = None,
limit: int | None = None,
) -> list[Visit]:
"""Return list of raw visits from the DB."""
) -> list[Visits]:
"""Return list of per-minute visit summary from the DB."""
with Session(engine) as session:
statement = select(Visit)
statement = select(Visits)
if start:
# Ignore type because Visit.called_at can be None for default value
statement.where(Visit.called_at > start) # type: ignore
statement.where(Visits.start >= start) # type: ignore
if end:
statement.where(Visit.called_at < end) # type: ignore
statement.where(Visits.end <= end) # type: ignore
if limit:
statement.limit(limit)
statement.order_by(Visit.called_at.desc()) # type: ignore
statement.order_by(Visits.start.desc()) # type: ignore
return session.exec(statement).all()
Loading

0 comments on commit bcfe0f9

Please sign in to comment.