-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/event issue #108 ready for review #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c3a33a6
d517c24
9ac1ab5
ea8d416
e03cede
108cd5b
700f13f
784e384
0d1dcb1
9f03e92
91aeb59
43ec487
d910593
7f82f03
8caf569
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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), | ||
| 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 ### | ||
| 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 | ||
|
|
||
|
|
||
| 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
|
||
| extract('year', EventDB.end_time) == year | ||
| ) | ||
| ))).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, | ||
| extract('month', EventDB.start_time) == month | ||
| ), | ||
| and_( | ||
| extract('year', EventDB.end_time) == year, | ||
| extract('month', EventDB.end_time) == month | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| ).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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 EventPublicAlso, I think it's fine to return the |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| from pydantic import BaseModel, ConfigDict, model_validator | ||
| import datetime | ||
|
|
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update these to say |
||
|
|
||
| class EventPublic(BaseModel): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning the |
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update these to say |
||
|
|
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update these to say |
||
|
|
||
| @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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update these to say |
||
|
|
||
| @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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
|
||
| class EventDelete(BaseModel): | ||
| result: bool | ||
| eid: int | ||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update these to say |
||
|
|
||
| __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, | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer using Pydantic's |
||
|
|
||
There was a problem hiding this comment.
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_dateso its more clear what is being stored. Sorry, it wasn't clear in the spec.