krz/hutch-stats

Server-side utility for calculating contributions for sourcehut users. contributions server sourcehut stats

Commit fe5efeb336

fe5efeb33639e963029c3a118ccc2e98bafccb58

parent: 9edd38e961

Verified · cmc

cmc <hello@cleberg.net> · 2026-04-12 01:45 UTC

feat: make sync overlap configurable, reduce default from 24h to 1h

Layout: unified · split

src/srht_contrib/config.py +3
@@ -35,6 +35,9 @@ class Settings(BaseSettings):
35 ) 35 )
36 default_actor: str = Field(default="~unknown", alias="DEFAULT_ACTOR") 36 default_actor: str = Field(default="~unknown", alias="DEFAULT_ACTOR")
37 poll_interval_seconds: int = Field(default=900, alias="POLL_INTERVAL_SECONDS") 37 poll_interval_seconds: int = Field(default=900, alias="POLL_INTERVAL_SECONDS")
38 sync_overlap_hours: int = Field(default=1, alias="SYNC_OVERLAP_HOURS")
39 srht_request_delay_seconds: float = Field(default=0.5, alias="SRHT_REQUEST_DELAY_SECONDS")
40 git_repo_discovery_ttl_seconds: int = Field(default=3600, alias="GIT_REPO_DISCOVERY_TTL_SECONDS")
38 actor_aliases_json: dict[str, list[str]] = Field( 41 actor_aliases_json: dict[str, list[str]] = Field(
39 default_factory=dict, 42 default_factory=dict,
40 alias="ACTOR_ALIASES_JSON", 43 alias="ACTOR_ALIASES_JSON",
src/srht_contrib/jobs/poller.py +9 −3
@@ -8,6 +8,7 @@ from sqlalchemy import select
8from sqlalchemy.exc import IntegrityError 8from sqlalchemy.exc import IntegrityError
9from sqlalchemy.orm import Session 9from sqlalchemy.orm import Session
10 10
11from srht_contrib.config import Settings
11from srht_contrib.models import ContributionEvent, ServiceBackfillState, SyncState, TrackedActor, TrackedRepository 12from srht_contrib.models import ContributionEvent, ServiceBackfillState, SyncState, TrackedActor, TrackedRepository
12from srht_contrib.schemas import NormalizedEvent 13from srht_contrib.schemas import NormalizedEvent
13from srht_contrib.services.git import GitIngestionService 14from srht_contrib.services.git import GitIngestionService
@@ -18,14 +19,19 @@ from srht_contrib.utils.repositories import canonicalize_repository_name
18 19
19 20
20logger = logging.getLogger(__name__) 21logger = logging.getLogger(__name__)
21SYNC_OVERLAP = timedelta(hours=24)
22RECENT_BACKFILL_BATCHES_PER_SERVICE = 5 22RECENT_BACKFILL_BATCHES_PER_SERVICE = 5
23 23
24 24
25class PollerService: 25class PollerService:
26 def __init__(self, todo_service: TodoIngestionService, git_service: GitIngestionService) -> None: 26 def __init__(
27 self,
28 todo_service: TodoIngestionService,
29 git_service: GitIngestionService,
30 settings: Settings,
31 ) -> None:
27 self.todo_service = todo_service 32 self.todo_service = todo_service
28 self.git_service = git_service 33 self.git_service = git_service
34 self._sync_overlap = timedelta(hours=settings.sync_overlap_hours)
29 35
30 def poll_all(self, db: Session, actor: str) -> int: 36 def poll_all(self, db: Session, actor: str) -> int:
31 self.track_actor_request(db, actor, update_last_requested=False) 37 self.track_actor_request(db, actor, update_last_requested=False)
@@ -104,7 +110,7 @@ class PollerService:
104 ) 110 )
105 since = datetime.now(tz=UTC) - timedelta(days=30) 111 since = datetime.now(tz=UTC) - timedelta(days=30)
106 if state and state.cursor_value: 112 if state and state.cursor_value:
107 since = datetime.fromisoformat(state.cursor_value.replace("Z", "+00:00")).astimezone(UTC) - SYNC_OVERLAP 113 since = datetime.fromisoformat(state.cursor_value.replace("Z", "+00:00")).astimezone(UTC) - self._sync_overlap
108 logger.info( 114 logger.info(
109 "Using sync cursor for %s actor=%s with overlap; since=%s", 115 "Using sync cursor for %s actor=%s with overlap; since=%s",
110 service_name, 116 service_name,
src/srht_contrib/main.py +1 −1
@@ -30,7 +30,7 @@ def build_poller(settings: Settings) -> PollerService:
30 git_client = SourceHutGraphQLClient(settings.git_srht_endpoint, settings.srht_token) 30 git_client = SourceHutGraphQLClient(settings.git_srht_endpoint, settings.srht_token)
31 todo_service = TodoIngestionService(todo_client, settings) 31 todo_service = TodoIngestionService(todo_client, settings)
32 git_service = GitIngestionService(git_client, settings) 32 git_service = GitIngestionService(git_client, settings)
33 return PollerService(todo_service=todo_service, git_service=git_service) 33 return PollerService(todo_service=todo_service, git_service=git_service, settings=settings)
34 34
35 35
36def create_app( 36def create_app(