Skip to content
Open
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
1 change: 1 addition & 0 deletions src/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import nominees.tables
import officers.tables
import candidates.tables
import event.tables
from alembic import context

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

Revision ID: f4c493a24799
Revises: 0a2c458d1ddd
Create Date: 2026-05-15 23:00:45.680647

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'f4c493a24799'
down_revision: Union[str, None] = '0a2c458d1ddd'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('event_info',
sa.Column('eid', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('name', sa.String(length=64), nullable=False),
sa.Column('start_time', sa.DateTime(timezone=True), nullable=False),
sa.Column('end_time', sa.DateTime(timezone=True), nullable=False),
sa.Column('repeat', sa.String(length=64), nullable=False),
sa.Column('start_date', sa.Date(), nullable=True),
sa.Column('end_date', sa.Date(), nullable=True),
Comment on lines +30 to +31
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change these to repeat_start/end_date so its more clear what is being stored. Sorry, it wasn't clear in the spec.

sa.CheckConstraint('start_date < end_date', name=op.f('ck_event_info_check_start_date_before_end_date')),
sa.CheckConstraint('start_time < end_time', name=op.f('ck_event_info_check_start_time_before_end_time')),
sa.PrimaryKeyConstraint('eid', name=op.f('pk_event_info'))
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('event_info')
# ### end Alembic commands ###
79 changes: 79 additions & 0 deletions src/event/crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from collections.abc import Sequence

from sqlalchemy import select, or_, and_, extract, delete
from sqlalchemy.ext.asyncio import AsyncSession

from event.tables import EventDB

from datetime import datetime, date

Check failure on line 8 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

src/event/crud.py:1:1: I001 Import block is un-sorted or un-formatted help: Organize imports


async def get_all_events(
db_session: AsyncSession
) -> Sequence[EventDB]:
events = (await db_session.scalars(select(EventDB))).all()
return events


async def get_events_for_this_year(
db_session: AsyncSession,
year: int,
) -> Sequence[EventDB]:
events = (await db_session.scalars(select(EventDB).where
(
or_(
extract('year', EventDB.start_time) == year,

Check failure on line 25 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (W291)

src/event/crud.py:25:57: W291 Trailing whitespace help: Remove trailing whitespace

Check failure on line 25 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (Q000)

src/event/crud.py:25:21: Q000 Single quotes found but double quotes preferred help: Replace single quotes with double quotes
extract('year', EventDB.end_time) == year

Check failure on line 26 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (Q000)

src/event/crud.py:26:21: Q000 Single quotes found but double quotes preferred help: Replace single quotes with double quotes
)
))).all()
return events

async def get_events_for_this_year_month(
db_session: AsyncSession,
year: int,
month: int,
) -> Sequence[EventDB]:
events = (
await db_session.scalars(
select(EventDB).where(
or_(
and_(
extract('year', EventDB.start_time) == year,

Check failure on line 41 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (Q000)

src/event/crud.py:41:33: Q000 Single quotes found but double quotes preferred help: Replace single quotes with double quotes
extract('month', EventDB.start_time) == month

Check failure on line 42 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (Q000)

src/event/crud.py:42:33: Q000 Single quotes found but double quotes preferred help: Replace single quotes with double quotes
),
and_(
extract('year', EventDB.end_time) == year,

Check failure on line 45 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (Q000)

src/event/crud.py:45:33: Q000 Single quotes found but double quotes preferred help: Replace single quotes with double quotes
extract('month', EventDB.end_time) == month

Check failure on line 46 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (Q000)

src/event/crud.py:46:33: Q000 Single quotes found but double quotes preferred help: Replace single quotes with double quotes
)
)
)
)
).all()
return events


async def get_event_by_eid(
db_session: AsyncSession,
eid: int
) -> EventDB | None:
return (await db_session.execute(
select(EventDB).where(EventDB.eid == eid)
)).scalar_one_or_none()


async def create_event(
db_session: AsyncSession,
info: EventDB
):
db_session.add(info)


async def delete_event(
db_session: AsyncSession,
eid: int
):
result = await db_session.execute(delete(EventDB).where(
EventDB.eid == eid
))
# Return the number of rows affected
return result.rowcount

Check failure on line 79 in src/event/crud.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (W292)

src/event/crud.py:79:27: W292 No newline at end of file help: Add trailing newline
69 changes: 69 additions & 0 deletions src/event/models.py
Copy link
Copy Markdown
Contributor

@jbriones1 jbriones1 May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To reduce the amount of duplicated fields you can use inheritance. Makes it less error-prone to update the exact same field across multiple models.

class EventPublic(BaseModel):
    name: str
    start_time:
   ...

class Event(EventPublic):
    eid: int
   # has all of the fields from EventPublic

Also, I think it's fine to return the eid, so you can remove EventPublic and just return Event.

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from pydantic import BaseModel, ConfigDict, model_validator
import datetime

Check failure on line 2 in src/event/models.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (I001)

src/event/models.py:1:1: I001 Import block is un-sorted or un-formatted help: Organize imports

class Event(BaseModel):
model_config = ConfigDict(from_attributes=True)
eid: int
name: str
start_time: datetime.datetime
end_time: datetime.datetime
description: str | None = None
repeat: str | None = None
start_date: datetime.date | None = None
end_date: datetime.date | None = None
Comment on lines +12 to +13
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update these to say repeat_start/end_date.


class EventPublic(BaseModel):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning the eid is fine here, so you can remove this model and just use Event.

model_config = ConfigDict(from_attributes=True)
name: str
start_time: datetime.datetime
end_time: datetime.datetime
description: str | None = None
repeat: str | None = None
start_date: datetime.date | None = None
end_date: datetime.date | None = None
Comment on lines +22 to +23
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update these to say repeat_start/end_date.


class EventCreate(BaseModel):
name: str
start_time: datetime.datetime
end_time: datetime.datetime
description: str | None = None
repeat: str | None = None
start_date: datetime.date | None = None
end_date: datetime.date | None = None
Comment on lines +31 to +32
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update these to say repeat_start/end_date.


@model_validator(mode="after")
def validate_time_range(self) -> "EventCreate":
if self.start_time >= self.end_time:
raise ValueError("The event start must be before the event end")

if self.start_date and self.end_date:
if self.start_date > self.end_date:
raise ValueError("The event repeat start date must be before the end date")

if self.start_date and not self.end_date:
raise ValueError("The event can't have start date but not end date")

if not self.start_date and self.end_date:
raise ValueError("The event can't have end date but not start date")

return self

class EventUpdate(BaseModel):
name: str | None = None
start_time: datetime.datetime | None = None
end_time: datetime.datetime | None = None
description: str | None = None
repeat: str | None = None
start_date: datetime.date | None = None
end_date: datetime.date | None = None
Comment on lines +57 to +58
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update these to say repeat_start/end_date.


@model_validator(mode="after")
def validate_time_range(self) -> "EventUpdate":
if self.start_time and self.end_time:
if self.start_time > self.end_time:
raise ValueError("The event start time must be before end time")
return self
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You don't have to do this, but maybe you could add a separate validator in case this has a repeat_start_date and repeat_end_date on the object.


class EventDelete(BaseModel):
result: bool
eid: int
70 changes: 70 additions & 0 deletions src/event/tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from sqlalchemy import (
Integer,
String,
DateTime,
Text,
Date,
CheckConstraint
)
from sqlalchemy.orm import Mapped, mapped_column

from database import Base
from datetime import datetime, date

class EventDB(Base):
__tablename__ = "event_info"

eid: Mapped[int] = mapped_column(
Integer,
primary_key=True,
autoincrement=True
)
description: Mapped[str] = mapped_column(
Text,
nullable=True
)
name: Mapped[str] = mapped_column(
String(64)
)
start_time: Mapped[datetime] = mapped_column(
DateTime(timezone=True)
)
end_time: Mapped[datetime] = mapped_column(
DateTime(timezone=True)
)
repeat: Mapped[str] = mapped_column(
String(64)
)
start_date: Mapped[date] = mapped_column(
Date,
nullable=True
)
end_date: Mapped[date] = mapped_column(
Date,
nullable=True
)
Comment on lines +38 to +45
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update these to say repeat_start/end_date.


__table_args__ = (
CheckConstraint(
'start_time < end_time',
name='check_start_time_before_end_time'
),
CheckConstraint(
'start_date < end_date',
name='check_start_date_before_end_date'
)
)


def serialize(self) -> dict:
return{
"eid": self.eid,
"name": self.name,
"description": self.description,
"start_time": self.start_time,
"end_time": self.end_time,
"start_date": self.start_date,
"end_date": self.end_date,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer using Pydantic's model_dump() if this isn't used anywhere. If it isn't used just remove it so others don't make their own serialization objects.


Loading
Loading