Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/alembic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ jobs:
run: |
# for python3.11 (dear internet gods: we'll update to 3.13 or something in a year, i promise)
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.11 python3.11-venv
python3.11 -m pip install --upgrade pip
python3.11 -m venv venv
sudo apt install python3.13 python3.13-venv
python3.13 -m pip install --upgrade pip
python3.13 -m venv venv
source ./venv/bin/activate
pip install .

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pytest_unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:

- uses: actions/setup-python@v4
with:
python-version: '3.11'
python-version: '3.13'

- uses: actions/cache@v4
id: cache
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ jobs:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
with:
version: 0.6.9
version: 0.15.12
27 changes: 13 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
[project]
name = "csss-site-backend"
version = "0.1"
requires-python = "~=3.11.0" # older versions untested, but we use new features often
requires-python = "~=3.13.0"

dependencies = [
# major
"fastapi==0.115.6",
"gunicorn==21.2.0",
"uvicorn[standard]==0.27.1",
"sqlalchemy[asyncio]==2.0.27",
"asyncpg==0.29.0",
"alembic==1.13.1",
"google-api-python-client==2.143.0",
"fastapi==0.135.4",
"gunicorn==25.3.0",
"uvicorn[standard]==0.46.0",
"sqlalchemy[asyncio]==2.0.49",
"asyncpg==0.31.0",
"alembic==1.18.4",
"google-api-python-client==2.194.0",

# minor
"pyOpenSSL==24.0.0", # for generating cryptographically secure random numbers
"xmltodict==0.13.0",
"requests==2.31.0",
"httpx==0.28.1",
]

[project.optional-dependencies]
dev = [
"ruff==0.6.9", # linting and formatter
"ruff==0.15.12", # linting and formatter
]

test = [
Expand Down Expand Up @@ -49,7 +48,7 @@ asyncio_default_fixture_loop_scope = "function"
[tool.ruff]
line-length = 120
indent-width = 4
target-version = "py311"
target-version = "py313"
exclude = [
"src/alembic/*"
]
Expand All @@ -66,8 +65,8 @@ ignore = ["E501", "F401", "N806"]
# [Based]Pyright: Type checker/LSP
[tool.pyright]
executionEnvironments = [
{ root = "src", pythonVersion = "3.11" },
{ root = "tests", extraPaths=["src"], pythonVersion = "3.11" }
{ root = "src", pythonVersion = "3.13" },
{ root = "tests", extraPaths=["src"], pythonVersion = "3.13" }
]
typeCheckingMode = "standard"
reportAny = "none" # Allow the use of `Any` type
Expand Down
2 changes: 1 addition & 1 deletion src/blog/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from datetime import date, datetime

import sqlalchemy
from blog.models import BlogPosts
from sqlalchemy import func

import database
from blog.models import BlogPosts


async def create_new_entry(
Expand Down
4 changes: 1 addition & 3 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import candidates.urls
import database
import elections.urls
import mountain_madness._2026.urls
import nominees.urls
import officers.urls
import permission.urls
Expand Down Expand Up @@ -59,7 +58,6 @@
app.include_router(nominees.urls.router)
app.include_router(officers.urls.router)
app.include_router(permission.urls.router)
app.include_router(mountain_madness._2026.urls.router)


@app.get("/")
Expand All @@ -73,7 +71,7 @@ async def validation_exception_handler(
exception: RequestValidationError,
):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
content=jsonable_encoder(
{
"detail": exception.errors(),
Expand Down
3 changes: 2 additions & 1 deletion src/scripts/migrate_from_about_officers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@

sys.path.append(str(Path(__file__).parent.parent.resolve()))

from officers.types import OfficerInfoDB, OfficerTermDB

from auth.crud import site_user_exists
from auth.tables import SiteUserDB
from data import semesters
from database import SQLALCHEMY_TEST_DATABASE_URL, DatabaseSessionManager
from officers.constants import OfficerPosition
from officers.types import OfficerInfoDB, OfficerTermDB

# This loads officer data from the https://github.com/CSSS/csss-site database into the provided database

Expand Down
2 changes: 1 addition & 1 deletion tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async def db_session(database_setup: DatabaseSessionManager):


@pytest_asyncio.fixture(scope="module", loop_scope="session")
async def client() -> AsyncGenerator[Any, None]:
async def client() -> AsyncGenerator[Any]:
# base_url is just a random placeholder url
# ASGITransport is just telling the async client to pass all requests to app
# `async with` syntax used so that the connecton will automatically be closed once done
Expand Down
40 changes: 18 additions & 22 deletions tests/integration/test_officers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async def test__read_execs(db_session: DBSession):
assert (await get_active_officer_terms(db_session, "abc22")) != []

abc11_officer_terms = await get_active_officer_terms(db_session, "abc11")
assert len(abc11_officer_terms) == 2
assert len(abc11_officer_terms) == 1
assert abc11_officer_terms[0].computing_id == "abc11"
assert abc11_officer_terms[0].position == OfficerPositionEnum.EXECUTIVE_AT_LARGE
assert abc11_officer_terms[0].start_date is not None
Expand All @@ -32,7 +32,7 @@ async def test__read_execs(db_session: DBSession):

current_exec_team = await current_officers(db_session)
assert current_exec_team is not None
assert len(current_exec_team) == 6
assert len(current_exec_team) == 5
# assert next(iter(current_exec_team)) == OfficerPositionEnum.EXECUTIVE_AT_LARGE
# assert next(iter(current_exec_team))["favourite_course_0"] == "CMPT 361"
# assert next(iter(current_exec_team.values()))[0].csss_email == OfficerPosition.to_email(OfficerPositionEnum.EXECUTIVE_AT_LARGE)
Expand All @@ -52,7 +52,7 @@ async def test__get_officers(client: AsyncClient):
response = await client.get("/officers/current")
assert response.status_code == 200
officers = response.json()
assert len(officers) == 6
assert len(officers) == 5
officer = next(o for o in officers if o["position"] == OfficerPositionEnum.EXECUTIVE_AT_LARGE)
assert "computing_id" not in officer
assert "discord_id" not in officer
Expand Down Expand Up @@ -167,7 +167,7 @@ async def test__get_current_officers_admin(admin_client: AsyncClient):
response = await admin_client.get("/officers/current")
assert response.status_code == 200
curr_officers = response.json()
assert len(curr_officers) == 6
assert len(curr_officers) == 5
officer = next(o for o in curr_officers if o["position"] == OfficerPositionEnum.EXECUTIVE_AT_LARGE)
assert "computing_id" in officer
assert "discord_id" in officer
Expand Down Expand Up @@ -243,15 +243,13 @@ async def test__admin_create_officer_term(admin_client: AsyncClient):
async def test__admin_patch_officer_info(admin_client: AsyncClient):
response = await admin_client.patch(
"officers/info/abc11",
content=json.dumps(
{
"legal_name": "Person A2",
"phone_number": "12345asdab67890",
"discord_name": "person_a_yeah",
"github_username": "person_a",
"google_drive_email": "person_a@gmail.com",
}
),
json={
"legal_name": "Person A2",
"phone_number": "12345asdab67890",
"discord_name": "person_a_yeah",
"github_username": "person_a",
"google_drive_email": "person_a@gmail.com",
},
)
assert response.status_code == 200
resJson = response.json()
Expand All @@ -263,15 +261,13 @@ async def test__admin_patch_officer_info(admin_client: AsyncClient):

response = await admin_client.patch(
"officers/info/aaabbbc",
content=json.dumps(
{
"legal_name": "Person AABBCC",
"phone_number": "1234567890",
"discord_name": None,
"github_username": None,
"google_drive_email": "person_aaa_bbb_ccc+spam@gmail.com",
}
),
json={
"legal_name": "Person AABBCC",
"phone_number": "1234567890",
"discord_name": None,
"github_username": None,
"google_drive_email": "person_aaa_bbb_ccc+spam@gmail.com",
},
)
assert response.status_code == 404

Expand Down
Loading
Loading