krz/hutch-stats
Server-side utility for calculating contributions for sourcehut users.
clone: git clone https://gitbay.org/krz/hutch-stats.git
main: alembic/versions/20260409_0001_initial_schema.py · raw
1"""initial schema"""
2
3from __future__ import annotations
4
5from alembic import op
6import sqlalchemy as sa
7from sqlalchemy import inspect
8
9from srht_contrib.config import Settings
10
11
12revision = "20260409_0001"
13down_revision = None
14branch_labels = None
15depends_on = None
16
17
18def _table_names() -> set[str]:
19 return set(inspect(op.get_bind()).get_table_names())
20
21
22def _index_names(table_name: str) -> set[str]:
23 return {index["name"] for index in inspect(op.get_bind()).get_indexes(table_name)}
24
25
26def _tracked_repositories_needs_upgrade() -> bool:
27 inspector = inspect(op.get_bind())
28 columns = {column["name"]: column for column in inspector.get_columns("tracked_repositories")}
29 actor_column = columns.get("actor")
30 if actor_column is None or actor_column.get("nullable", True):
31 return True
32
33 unique_constraints = {constraint["name"] for constraint in inspector.get_unique_constraints("tracked_repositories")}
34 return "uq_tracked_repository_service_actor_name" not in unique_constraints
35
36
37def _upgrade_tracked_repositories() -> None:
38 default_actor = Settings().default_actor
39 op.execute(
40 sa.text(
41 """
42 CREATE TABLE tracked_repositories__alembic_new (
43 id INTEGER NOT NULL PRIMARY KEY,
44 service VARCHAR(32) NOT NULL,
45 repo_name VARCHAR(255) NOT NULL,
46 actor VARCHAR(255) NOT NULL,
47 CONSTRAINT uq_tracked_repository_service_actor_name UNIQUE (service, actor, repo_name)
48 )
49 """
50 )
51 )
52 op.execute(
53 sa.text(
54 """
55 INSERT INTO tracked_repositories__alembic_new (id, service, repo_name, actor)
56 SELECT id, service, repo_name, COALESCE(actor, :default_actor)
57 FROM tracked_repositories
58 """
59 ).bindparams(default_actor=default_actor)
60 )
61 op.execute(sa.text("DROP TABLE tracked_repositories"))
62 op.execute(sa.text("ALTER TABLE tracked_repositories__alembic_new RENAME TO tracked_repositories"))
63
64
65def upgrade() -> None:
66 table_names = _table_names()
67
68 if "contribution_events" not in table_names:
69 op.create_table(
70 "contribution_events",
71 sa.Column("id", sa.Integer(), primary_key=True),
72 sa.Column("service", sa.String(length=32), nullable=False),
73 sa.Column("event_type", sa.String(length=64), nullable=False),
74 sa.Column("actor", sa.String(length=255), nullable=False),
75 sa.Column("repo_name", sa.String(length=255), nullable=True),
76 sa.Column("resource_id", sa.String(length=255), nullable=False),
77 sa.Column("external_uid", sa.String(length=255), nullable=False),
78 sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False),
79 sa.Column("weight", sa.Float(), nullable=False),
80 sa.Column("raw_payload_json", sa.JSON(), nullable=True),
81 sa.UniqueConstraint("service", "external_uid", name="uq_contribution_event_service_uid"),
82 )
83
84 contribution_event_indexes = _index_names("contribution_events")
85 if "ix_contribution_events_actor_occurred_at" not in contribution_event_indexes:
86 op.create_index(
87 "ix_contribution_events_actor_occurred_at",
88 "contribution_events",
89 ["actor", "occurred_at"],
90 unique=False,
91 )
92 if "ix_contribution_events_service_occurred_at" not in contribution_event_indexes:
93 op.create_index(
94 "ix_contribution_events_service_occurred_at",
95 "contribution_events",
96 ["service", "occurred_at"],
97 unique=False,
98 )
99
100 if "sync_states" not in table_names:
101 op.create_table(
102 "sync_states",
103 sa.Column("id", sa.Integer(), primary_key=True),
104 sa.Column("service", sa.String(length=32), nullable=False),
105 sa.Column("actor", sa.String(length=255), nullable=False),
106 sa.Column("cursor_value", sa.Text(), nullable=True),
107 sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
108 sa.UniqueConstraint("service", "actor", name="uq_sync_state_service_actor"),
109 )
110
111 if "tracked_repositories" not in table_names:
112 op.create_table(
113 "tracked_repositories",
114 sa.Column("id", sa.Integer(), primary_key=True),
115 sa.Column("service", sa.String(length=32), nullable=False),
116 sa.Column("repo_name", sa.String(length=255), nullable=False),
117 sa.Column("actor", sa.String(length=255), nullable=False),
118 sa.UniqueConstraint("service", "actor", "repo_name", name="uq_tracked_repository_service_actor_name"),
119 )
120 elif _tracked_repositories_needs_upgrade():
121 _upgrade_tracked_repositories()
122
123 if "actor_aliases" not in table_names:
124 op.create_table(
125 "actor_aliases",
126 sa.Column("id", sa.Integer(), primary_key=True),
127 sa.Column("canonical_actor", sa.String(length=255), nullable=False),
128 sa.Column("alias", sa.String(length=255), nullable=False),
129 sa.UniqueConstraint("alias", name="uq_actor_alias_alias"),
130 )
131
132
133def downgrade() -> None:
134 table_names = _table_names()
135
136 if "actor_aliases" in table_names:
137 op.drop_table("actor_aliases")
138 if "tracked_repositories" in table_names:
139 op.drop_table("tracked_repositories")
140 if "sync_states" in table_names:
141 op.drop_table("sync_states")
142 if "contribution_events" in table_names:
143 contribution_event_indexes = _index_names("contribution_events")
144 if "ix_contribution_events_service_occurred_at" in contribution_event_indexes:
145 op.drop_index("ix_contribution_events_service_occurred_at", table_name="contribution_events")
146 if "ix_contribution_events_actor_occurred_at" in contribution_event_indexes:
147 op.drop_index("ix_contribution_events_actor_occurred_at", table_name="contribution_events")
148 op.drop_table("contribution_events")