Skip to content

Commit

Permalink
📈 add active users metric
Browse files Browse the repository at this point in the history
  • Loading branch information
anuejn committed May 3, 2024
1 parent f1c16dd commit 5efffe3
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 16 deletions.
4 changes: 3 additions & 1 deletion backend/transcribee_backend/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ def create_user(session: Session, username: str, password: str) -> User:
if existing_user is not None:
raise UserAlreadyExists()
salt, hash = pw_hash(password)
user = User(username=username, password_hash=hash, password_salt=salt)
user = User(
username=username, password_hash=hash, password_salt=salt, last_seen=None
)
session.add(user)
session.commit()
return user
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""add User.last_seen
Revision ID: ef78fa0844f4
Revises: 417eece003cb
Create Date: 2024-05-03 16:08:10.602419
"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "ef78fa0844f4"
down_revision = "417eece003cb"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.add_column(
sa.Column("last_seen", sa.DateTime(timezone=True), nullable=True)
)

# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.drop_column("last_seen")

# ### end Alembic commands ###
67 changes: 52 additions & 15 deletions backend/transcribee_backend/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
from transcribee_backend.helpers.time import now_tz_aware
from transcribee_backend.models.document import Document
from transcribee_backend.models.task import Task, TaskAttempt, TaskState
from transcribee_backend.models.user import User
from transcribee_backend.models.user import User, UserToken
from transcribee_backend.models.worker import Worker


class Metric:
class GaugeMetric:
@abstractmethod
def refresh(self, session: Session):
pass


class TasksInState(Metric):
class TasksInState(GaugeMetric):
def __init__(self):
self.collector = Gauge(
"transcribee_tasks", "Number of tasks", ["state", "task_type"]
Expand All @@ -45,7 +45,7 @@ def refresh(self, session: Session):
)


class Workers(Metric):
class Workers(GaugeMetric):
def __init__(self):
self.collector = Gauge("transcribee_workers", "Workers", ["group"])

Expand All @@ -69,25 +69,56 @@ def refresh(self, session: Session):
self.collector.labels(group="alive").set(result)


class Users(Metric):
class Users(GaugeMetric):
def __init__(self):
self.collector = Gauge("transcribee_users", "Registered users")
self.collector = Gauge(
"transcribee_users", "Users at the Transcribee Instance", ["group"]
)

def refresh(self, session: Session):
(result,) = session.query(func.count(User.id)).one()
self.collector.set(result)
self.collector.labels(group="all").set(result)

now = now_tz_aware()
user_timeout_active = now - datetime.timedelta(hours=1)
(result,) = (
session.query(func.count(User.id))
.where(
col(User.last_seen) >= user_timeout_active,
)
.one()
)
self.collector.labels(group="active").set(result)

now = now_tz_aware()
user_timeout_active = now - datetime.timedelta(hours=1)
(result,) = (
session.query(func.count(User.id))
.where(col(User.last_seen).is_not(None))
.one()
)
self.collector.labels(group="ever_logged_in").set(result)

(result,) = (
session.query(func.count(func.distinct(UserToken.user_id)))
.where(
col(UserToken.valid_until) >= now,
)
.one()
)
self.collector.labels(group="with_token").set(result)


class Documents(Metric):
class Documents(GaugeMetric):
def __init__(self):
self.collector = Gauge("transcribe_documents", "Documents")
self.collector = Gauge("transcribee_documents", "Documents")

def refresh(self, session: Session):
(result,) = session.query(func.count(Document.id)).one()
self.collector.set(result)


class Queue(Metric):
class Queue(GaugeMetric):
def __init__(self):
self.collector = Gauge(
"transcribee_queue_seconds", "Queue length in seconds", ["task_type"]
Expand Down Expand Up @@ -116,19 +147,25 @@ def refresh(self, session: Session):
self.collector.labels(task_type=task_type.value).set(count)


METRIC_CLASSES: List[type[Metric]] = [TasksInState, Workers, Users, Documents, Queue]
METRICS: List[Metric] = []
GAUGE_METRIC_CLASSES: List[type[GaugeMetric]] = [
TasksInState,
Workers,
Users,
Documents,
Queue,
]
GAUGE_METRICS: List[GaugeMetric] = []


def refresh_metrics():
with SessionContextManager(path="repeating_task:refresh_metrics") as session:
for metric in METRICS:
for metric in GAUGE_METRICS:
metric.refresh(session)


def init_metrics():
for klass in METRIC_CLASSES:
METRICS.append(klass())
for klass in GAUGE_METRIC_CLASSES:
GAUGE_METRICS.append(klass())


security = HTTPBasic()
Expand Down
4 changes: 4 additions & 0 deletions backend/transcribee_backend/models/user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import uuid
from typing import Optional

from pydantic import BaseModel, ConstrainedStr
from sqlmodel import Column, DateTime, Field, Relationship, SQLModel
Expand All @@ -18,6 +19,9 @@ class User(UserBase, table=True):
)
password_hash: bytes
password_salt: bytes
last_seen: Optional[datetime.datetime] = Field(
sa_column=Column(DateTime(timezone=True), nullable=True)
)


class CreateUser(UserBase):
Expand Down
5 changes: 5 additions & 0 deletions backend/transcribee_backend/routers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ def logout(
@user_router.get("/me/")
def read_user(
token: UserToken = Depends(get_user_token),
session: Session = Depends(get_session),
) -> UserBase:
token.user.last_seen = now_tz_aware()
session.add(token.user)
session.commit()

return UserBase(username=token.user.username)


Expand Down

0 comments on commit 5efffe3

Please sign in to comment.