···5566if TYPE_CHECKING:
77 from kefi.models.kudos.models import Action
88+ from kefi.models.plazas.models import Attendance, Plaza
899101011class User(SQLModel, table=True):
···2829 sa_relationship_kwargs=dict(foreign_keys="[Transaction.receiver_id]"),
2930 )
3031 is_admin: bool = False
3232+ attendances: list["Attendance"] = Relationship(back_populates="user")
31333234 def get_short_name(self):
3335 """Gets the short name of the user."""
···5153 # Action, the reference of the action used to send the transaction
5254 action_id: int | None = Field(default=None, foreign_key="action.id")
5355 action: Optional["Action"] = Relationship(back_populates="transactions")
5656+5757+ # Attendance, the reference of the attendance used to send the transaction
5858+ attendance_id: int | None = Field(default=None, foreign_key="attendance.id")
5959+ attendance: Optional["Attendance"] = Relationship(back_populates="transactions")
54605561 # Sender user, if not defined, is a system transaction
5662 sender_id: int | None = Field(default=None, foreign_key="user.id")
kefi/models/plazas/__init__.py
This is a binary file and will not be displayed.
+85
kefi/models/plazas/helpers.py
···11+import datetime
22+33+import pytz
44+from sqlmodel import Session, select
55+66+from kefi.config import settings
77+from kefi.models.core.helpers import available_balance
88+from kefi.models.core.models import Transaction, User
99+from kefi.models.plazas.models import Attendance, Plaza
1010+1111+1212+def next_plaza_appointment() -> datetime.datetime:
1313+ """Gets the next plaza date and time appointment."""
1414+ today = datetime.datetime.today()
1515+ next_date = (today + datetime.timedelta((4 - today.weekday()) % 7)).date()
1616+ default_time = datetime.time(
1717+ hour=settings.PLAZA_DEFAULT_HOUR, minute=settings.PLAZA_DEFAULT_MINUTE
1818+ )
1919+ return datetime.datetime.combine(date=next_date, time=default_time).astimezone(
2020+ pytz.timezone("Europe/Madrid")
2121+ )
2222+2323+2424+def get_or_create_current_plaza(session: Session) -> Plaza:
2525+ """Gets the current available plaza."""
2626+ now = datetime.datetime.now()
2727+ query = select(Plaza).filter(Plaza.date >= now).order_by("date")
2828+ results = session.exec(query)
2929+ first = results.first()
3030+ if first:
3131+ return first
3232+ plaza = Plaza(date=next_plaza_appointment())
3333+ session.add(plaza)
3434+ return plaza
3535+3636+3737+def is_attending(user: User, session: Session) -> bool:
3838+ """Checks if the user is attending to the current plaza."""
3939+ plaza = get_or_create_current_plaza(session=session)
4040+ query = select(Attendance).filter(
4141+ Attendance.user == user, Attendance.plaza == plaza
4242+ )
4343+ results = session.exec(query)
4444+ return results.first() is not None
4545+4646+4747+def create_attendance(user: User, session: Session) -> Attendance:
4848+ """Creates the transaction that spends the kefis and also creates the attendance
4949+ for the next plaza.
5050+ """
5151+ plaza = get_or_create_current_plaza(session=session)
5252+ balance = available_balance(user=user, session=session)
5353+ price = settings.PLAZA_PRICE
5454+ if balance < price:
5555+ raise ValueError("The user doesn't have enough balance")
5656+ query = select(Attendance).filter(
5757+ Attendance.user == user, Attendance.plaza == plaza
5858+ )
5959+ results = session.exec(query)
6060+ if results.first():
6161+ raise ValueError("The user is already in the plaza")
6262+ attendance = Attendance(plaza=plaza, user=user)
6363+ session.add(attendance)
6464+ transaction = Transaction(amount=-price, user=user, attendance=attendance)
6565+ session.add(transaction)
6666+ return attendance
6767+6868+6969+def delete_attendance(user: User, session: Session) -> None:
7070+ """Deletes the attendance and the associated transaction."""
7171+ plaza = get_or_create_current_plaza(session=session)
7272+ attendance_query = select(Attendance).filter(
7373+ Attendance.user == user, Attendance.plaza == plaza
7474+ )
7575+ attendance_results = session.exec(attendance_query)
7676+ attendance = attendance_results.first()
7777+ if not attendance:
7878+ raise ValueError("The user is not in the plaza")
7979+ session.delete(attendance)
8080+ transaction_query = select(Transaction).filter(
8181+ Transaction.attendance == attendance, Transaction.user == user
8282+ )
8383+ transaction_results = session.exec(transaction_query)
8484+ transaction = transaction_results.first()
8585+ session.delete(transaction)
+46
kefi/models/plazas/models.py
···11+import datetime
22+from typing import TYPE_CHECKING
33+44+from sqlmodel import Field, Relationship, SQLModel, UniqueConstraint
55+66+from kefi.models.core.models import User
77+88+if TYPE_CHECKING:
99+1010+ from kefi.models.core.models import Transaction
1111+1212+1313+class Plaza(SQLModel, table=True):
1414+ """A plaza is a meeting session that represents a moment scheduled to create a
1515+ group call with the different groups of attendees.
1616+ """
1717+1818+ id: int | None = Field(default=None, primary_key=True)
1919+ date: datetime.datetime = Field(
2020+ unique=True, index=True
2121+ ) # When the meetings are going to be created
2222+ attendances: list["Attendance"] = Relationship(back_populates="plaza")
2323+2424+2525+class Attendance(SQLModel, table=True):
2626+ """An attendance is a user who has spent kefis to be part of a meetings session in
2727+ the plaza.
2828+ """
2929+3030+ __table_args__ = (
3131+ UniqueConstraint("plaza_id", "user_id", name="plaza_id_user_id_unique"),
3232+ )
3333+ id: int | None = Field(default=None, primary_key=True)
3434+ # Meeting session that the attendance makes reference
3535+ plaza_id: int = Field(foreign_key="plaza.id")
3636+ plaza: Plaza = Relationship(
3737+ back_populates="attendances",
3838+ sa_relationship_kwargs=dict(foreign_keys="[Attendance.plaza_id]"),
3939+ )
4040+ # User who is going to attend to the meeting session
4141+ user_id: int = Field(foreign_key="user.id")
4242+ user: User = Relationship(
4343+ back_populates="attendances",
4444+ sa_relationship_kwargs=dict(foreign_keys="[Attendance.user_id]"),
4545+ )
4646+ transactions: list["Transaction"] = Relationship(back_populates="attendance")