krz/hutch-stats

Server-side utility for calculating contributions for sourcehut users.

clone: git clone https://gitbay.org/krz/hutch-stats.git

main: src/srht_contrib/schemas.py · raw

 1from __future__ import annotations
 2
 3from datetime import date, datetime
 4from typing import Literal
 5
 6from pydantic import BaseModel, Field, model_validator
 7
 8
 9class NormalizedEvent(BaseModel):
10    service: str
11    event_type: str
12    actor: str
13    repo_name: str | None = None
14    resource_id: str
15    external_uid: str
16    occurred_at: datetime
17    weight: float
18    raw_payload_json: dict | None = None
19
20
21class ContributionDay(BaseModel):
22    date: date
23    count: int
24    score: float
25
26
27class ContributionIndexMetadata(BaseModel):
28    is_indexed: bool
29    last_polled_at: datetime | None = None
30    indexing_state: Literal["pending", "indexed", "error"]
31    is_recent_window_backfilled: bool = False
32    recent_backfill_state: Literal["pending", "in_progress", "completed", "error"] = "pending"
33    recent_backfill_completed_at: datetime | None = None
34
35
36class ContributionCalendarResponse(ContributionIndexMetadata):
37    actor: str
38    from_date: date = Field(alias="from")
39    to_date: date = Field(alias="to")
40    days: list[ContributionDay]
41
42    model_config = {"populate_by_name": True}
43
44
45class ContributionStatsResponse(ContributionIndexMetadata):
46    actor: str
47    from_date: date = Field(alias="from")
48    to_date: date = Field(alias="to")
49    total_events: int
50    total_score: float
51    active_days: int
52    longest_streak: int
53    current_streak: int
54
55    model_config = {"populate_by_name": True}
56
57
58class HealthResponse(BaseModel):
59    status: str
60
61
62class PollResponse(BaseModel):
63    actor: str
64    inserted_events: int
65    services: list[str]
66
67
68class TrackedRepositoryCreateRequest(BaseModel):
69    actor: str
70    repo_name: str
71
72
73class TrackedRepositoryUpdateRequest(BaseModel):
74    actor: str | None = None
75    repo_name: str | None = None
76
77    @model_validator(mode="after")
78    def validate_any_field_present(self) -> "TrackedRepositoryUpdateRequest":
79        if self.actor is None and self.repo_name is None:
80            raise ValueError("Provide `actor`, `repo_name`, or both.")
81        return self
82
83
84class TrackedRepositoryResponse(BaseModel):
85    id: int
86    service: str
87    actor: str
88    repo_name: str