Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
98ad852
wip: add webmaster data to test DB
jbriones1 Sep 28, 2025
2ae6aea
wip: add officer models and update GET current officer docs
jbriones1 Sep 28, 2025
14cef35
wip: get current officers done
jbriones1 Sep 28, 2025
af9e464
feat: Migration for Elections and Officers
jbriones1 Sep 29, 2025
c3130b0
fix: update election type max length to 32
jbriones1 Sep 29, 2025
c554946
fix: elections and officers datetime to DATE
jbriones1 Sep 29, 2025
1a5a3c1
fix: update officer term constraint migration
jbriones1 Sep 29, 2025
777948c
fix: fix lint errors in constraint update
jbriones1 Sep 29, 2025
ce28f15
wip: removed some unused imports and updated some constraints
jbriones1 Sep 29, 2025
ab736be
wip: update GET all officers
jbriones1 Sep 29, 2025
223f1e5
wip: update Officer models
jbriones1 Sep 29, 2025
b59fffd
fix: site users couldn't have NULL log in time
jbriones1 Sep 29, 2025
d817743
wip: add endpoint information for officer terms and officer info POST
jbriones1 Sep 29, 2025
75f6644
wip: PATCH officer term and officer info done
jbriones1 Sep 29, 2025
c9b561b
wip: DELETE officer term works
jbriones1 Sep 29, 2025
87bf0d5
fix: return to timezone unaware datetimes
jbriones1 Sep 29, 2025
0892540
fix: election timestamps back to datetime
jbriones1 Sep 29, 2025
422617b
wip: GET all officers dynamically shows fields
jbriones1 Sep 29, 2025
30785b9
fix: lint issues
jbriones1 Sep 29, 2025
8c7f4be
wip: get current officers works
jbriones1 Sep 29, 2025
c9673d2
test: Suppress SQLAlchemy logs and marked tests WIP
jbriones1 Sep 30, 2025
f3ac5ac
fix: suppress asyncio warning
jbriones1 Sep 30, 2025
f39c994
wip: refactored the unit test fixtures to the configuration file
jbriones1 Sep 30, 2025
f5c8796
fix: change database set up to once per module
jbriones1 Sep 30, 2025
c93ea35
fix: unit tests working with only one set up
jbriones1 Oct 1, 2025
86435f8
fix: event loop fixed
jbriones1 Oct 1, 2025
8e17f27
wip: add admin_client
jbriones1 Oct 1, 2025
1502065
fix: unit tests split up and all pass
jbriones1 Oct 1, 2025
5c3c3f9
fix: github workflow update to pass CI
jbriones1 Oct 1, 2025
422c99d
fix(tests): update GitHub actions and re-enabled unit tests
jbriones1 Oct 1, 2025
7262d5b
fix(tests): move conftest to integration folder, moved unit tests
jbriones1 Oct 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/pytest_unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,4 @@ jobs:
pip install -r requirements.txt

- name: Run unit tests
run: |
source ./venv/bin/activate
pytest ./tests/unit -v
run: PYTHONPATH=src ./venv/bin/python -m pytest ./tests/unit -v
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ requires-python = ">= 3.11" # older versions untested, but we use new features o
Homepage = "https://api.sfucsss.org/"

[tool.pytest.ini_options]
pythonpath = "./src/"
pythonpath = ["src"]
log_cli = true
log_cli_level = "INFO"
testpaths = [
"tests",
]
]
norecursedirs = "tests/wip"
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"

[tool.ruff]
line-length = 120
Expand Down
2 changes: 2 additions & 0 deletions src/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import blog.tables
import database
import elections.tables
import nominees.tables
import officers.tables
import registrations.tables
from alembic import context

# this is the Alembic Config object, which provides
Expand Down
40 changes: 40 additions & 0 deletions src/alembic/versions/0a2c458d1ddd_site_user_timestamps_nullable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""site_user_timestamps_nullable

Revision ID: 0a2c458d1ddd
Revises: a5c42bcdda5c
Create Date: 2025-09-28 20:52:02.486734

"""
from collections.abc import Sequence

from sqlalchemy.dialects import postgresql

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "0a2c458d1ddd"
down_revision: str | None = "a5c42bcdda5c"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("site_user", "first_logged_in",
existing_type=postgresql.TIMESTAMP(),
nullable=True)
op.alter_column("site_user", "last_logged_in",
existing_type=postgresql.TIMESTAMP(),
nullable=True)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("site_user", "last_logged_in",
existing_type=postgresql.TIMESTAMP(),
nullable=False)
op.alter_column("site_user", "first_logged_in",
existing_type=postgresql.TIMESTAMP(),
nullable=False)
# ### end Alembic commands ###
53 changes: 53 additions & 0 deletions src/alembic/versions/0c717bd88d06_elections_timestamp_datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""elections_timestamp_datetime

Revision ID: 0c717bd88d06
Revises: 0a2c458d1ddd
Create Date: 2025-09-28 22:25:28.864945

"""
from collections.abc import Sequence
from typing import Union

import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "0c717bd88d06"
down_revision: str | None = "0a2c458d1ddd"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("election", "datetime_start_nominations",
existing_type=sa.DATE(),
type_=sa.DateTime(),
existing_nullable=False)
op.alter_column("election", "datetime_start_voting",
existing_type=sa.DATE(),
type_=sa.DateTime(),
existing_nullable=False)
op.alter_column("election", "datetime_end_voting",
existing_type=sa.DATE(),
type_=sa.DateTime(),
existing_nullable=False)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("election", "datetime_end_voting",
existing_type=sa.DateTime(),
type_=sa.DATE(),
existing_nullable=False)
op.alter_column("election", "datetime_start_voting",
existing_type=sa.DateTime(),
type_=sa.DATE(),
existing_nullable=False)
op.alter_column("election", "datetime_start_nominations",
existing_type=sa.DateTime(),
type_=sa.DATE(),
existing_nullable=False)
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""elections-officers-datetime-to-date

Revision ID: 876041e5b41c
Revises: 243190df5588
Create Date: 2025-09-28 18:01:03.913302

"""
from collections.abc import Sequence
from typing import Union

import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "876041e5b41c"
down_revision: str | None = "243190df5588"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("election", "datetime_start_nominations",
existing_type=postgresql.TIMESTAMP(),
type_=sa.Date(),
existing_nullable=False)
op.alter_column("election", "datetime_start_voting",
existing_type=postgresql.TIMESTAMP(),
type_=sa.Date(),
existing_nullable=False)
op.alter_column("election", "datetime_end_voting",
existing_type=postgresql.TIMESTAMP(),
type_=sa.Date(),
existing_nullable=False)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("election", "datetime_end_voting",
existing_type=sa.Date(),
type_=postgresql.TIMESTAMP(),
existing_nullable=False)
op.alter_column("election", "datetime_start_voting",
existing_type=sa.Date(),
type_=postgresql.TIMESTAMP(),
existing_nullable=False)
op.alter_column("election", "datetime_start_nominations",
existing_type=sa.Date(),
type_=postgresql.TIMESTAMP(),
existing_nullable=False)
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""update_officer_term_constraint_71

Revision ID: a5c42bcdda5c
Revises: 876041e5b41c
Create Date: 2025-09-28 18:03:54.856781

"""
from collections.abc import Sequence

import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "a5c42bcdda5c"
down_revision: str | None = "876041e5b41c"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("election", "type",
existing_type=sa.VARCHAR(length=64),
type_=sa.String(length=32),
nullable=False)
op.create_unique_constraint(op.f("uq_officer_term_computing_id"), "officer_term", ["computing_id", "position", "start_date"])
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f("uq_officer_term_computing_id"), "officer_term", type_="unique")
op.alter_column("election", "type",
existing_type=sa.String(length=32),
type_=sa.VARCHAR(length=64),
nullable=True)
# ### end Alembic commands ###
15 changes: 10 additions & 5 deletions src/auth/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,21 @@ class SiteUser(Base):
)

# first and last time logged into the CSSS API
first_logged_in: Mapped[datetime] = mapped_column(DateTime, nullable=False, server_default=func.now())
last_logged_in: Mapped[datetime] = mapped_column(DateTime, nullable=False, server_default=func.now())
first_logged_in: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_logged_in: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)

# optional user information for display purposes
profile_picture_url: Mapped[str | None] = mapped_column(Text, nullable=True)

def serialize(self) -> dict[str, str | int | bool | None]:
return {

res = {
"computing_id": self.computing_id,
"first_logged_in": self.first_logged_in.isoformat(),
"last_logged_in": self.last_logged_in.isoformat(),
"profile_picture_url": self.profile_picture_url
}
if self.first_logged_in is not None:
res["first_logged_in"] = self.first_logged_in.isoformat()
if self.last_logged_in is not None:
res["last_logged_in"] = self.last_logged_in.isoformat()

return res
4 changes: 2 additions & 2 deletions src/cron/daily.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import github
import google_api
import utils
from database import _db_session
from database import get_db_session
from officers.crud import all_officers, get_user_by_username

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -55,7 +55,7 @@ async def update_github_permissions(db_session):
_logger.info("updated github permissions")

async def update_permissions():
db_session = _db_session()
db_session = get_db_session()

update_google_permissions(db_session)
db_session.commit()
Expand Down
7 changes: 3 additions & 4 deletions src/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def setup_database():
# TODO: where is sys.stdout piped to? I want all these to go to a specific logs folder
sessionmanager = DatabaseSessionManager(
SQLALCHEMY_TEST_DATABASE_URL if os.environ.get("LOCAL") else SQLALCHEMY_DATABASE_URL,
{ "echo": True },
{ "echo": False },
)

@contextlib.asynccontextmanager
Expand All @@ -116,10 +116,9 @@ async def lifespan(app: FastAPI):
await sessionmanager.close()


async def _db_session():
async def get_db_session():
async with sessionmanager.session() as session:
yield session


# TODO: what does this do again?
DBSession = Annotated[AsyncSession, Depends(_db_session)]
DBSession = Annotated[AsyncSession, Depends(get_db_session)]
10 changes: 5 additions & 5 deletions src/elections/tables.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import date, datetime

from sqlalchemy import (
DateTime,
Expand All @@ -24,10 +24,10 @@ class Election(Base):
# Slugs are unique identifiers
slug: Mapped[str] = mapped_column(String(MAX_ELECTION_SLUG), primary_key=True)
name: Mapped[str] = mapped_column(String(MAX_ELECTION_NAME), nullable=False)
type: Mapped[ElectionTypeEnum] = mapped_column(String(64), default=ElectionTypeEnum.GENERAL)
datetime_start_nominations: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
datetime_start_voting: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
datetime_end_voting: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
type: Mapped[ElectionTypeEnum] = mapped_column(String(32), default=ElectionTypeEnum.GENERAL)
datetime_start_nominations: Mapped[datetime] = mapped_column(DateTime, nullable=False)
datetime_start_voting: Mapped[datetime] = mapped_column(DateTime, nullable=False)
datetime_end_voting: Mapped[datetime] = mapped_column(DateTime, nullable=False)

# a comma-separated string of positions which must be elements of OfficerPosition
# By giving it the type `StringList`, the database entry will automatically be marshalled to the correct form
Expand Down
Loading