models: add next_user_to_send_request()

This commit is contained in:
lemoer 2024-12-20 00:54:41 +01:00
parent da2352642e
commit f6f99b860b

107
models.py
View File

@ -11,7 +11,8 @@ import datetime
import pytest
def utc_now():
return datetime.datetime.now(datetime.timezone.utc)
# We remove the timezone information, because SQLite does not support it
return datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
class User(SQLModel, table=True):
id: int = Field(primary_key=True)
@ -41,6 +42,19 @@ def all_users_sorted(session: Session) -> List[User]:
users.sort(key=lambda u: u.sort_time())
return users
def next_user_to_send_request(session: Session, task: "Task") -> User | None:
# Return the user who should send the next request (or None if we already
# sent requests for this Task to all users)
requested_users = [r.user for r in task.participation_requests]
users = all_users_sorted(session)
for u in users:
if u in requested_users:
continue
return u
class Task(SQLModel, table=True):
id: int = Field(primary_key=True)
name: str
@ -65,8 +79,8 @@ class Task(SQLModel, table=True):
def additional_requests_to_be_sent(self):
# return the number of additional requests to be sent
return self.required_number_of_participants \
- len(self.accepted_participation_requests()) \
- len(self.requested_participation_requests())
- len(self.accepted_requests()) \
- len(self.requested_requests())
def freshly_expired_requests(self, now) -> List["ParticipationRequest"]:
expired_requests = []
@ -152,7 +166,6 @@ class AlreadyRejected(StateTransitionError):
class Timeout(StateTransition):
pass
class ParticipationRequest(SQLModel, table=True):
id: int = Field(primary_key=True)
user_id: int = Field(foreign_key="user.id")
@ -283,6 +296,90 @@ def test_all_users_sorted(session):
users = all_users_sorted(session)
assert users == [u1, u3, u2]
def test_next_user(session):
u1 = User(name="u1")
u2 = User(name="u2")
u3 = User(name="u3")
session.add(u1)
session.add(u2)
session.add(u3)
session.commit()
# Starting order
users = all_users_sorted(session)
assert users == [u1, u2, u3]
t1 = Task(name="t1", required_number_of_participants=2, due=utc_now(), timeout=10)
session.add(t1)
assert t1.additional_requests_to_be_sent() == 2
assert next_user_to_send_request(session, t1) == u1
r1 = ParticipationRequest(user=u1, task=t1, requested_at=utc_now())
session.add(r1)
assert next_user_to_send_request(session, t1) == u2
# Starting order
users = all_users_sorted(session)
assert users == [u1, u2, u3]
# Another additional request should be sent out.
assert t1.additional_requests_to_be_sent() == 1
# After rejecting, two additional requests should be sent out.
assert isinstance(r1.try_reject(utc_now()).unwrap(), RejectInTime)
assert t1.additional_requests_to_be_sent() == 2
# Create another Task
t2 = Task(name="t2", required_number_of_participants=2, due=utc_now(), timeout=10)
session.add(t2)
# Next user should be user1 (because we rejected in t1)
assert next_user_to_send_request(session, t2) == u1
# Still starting order (because we rejected)
users = all_users_sorted(session)
assert users == [u1, u2, u3]
# When we change to accept, only one additional request should be sent out.
assert isinstance(r1.try_accept(utc_now()).unwrap(), AcceptAfterRejectAllowed)
assert t1.additional_requests_to_be_sent() == 1
# Next user should be u2 for t2 (because u1 accepted for t1)
assert next_user_to_send_request(session, t2) == u2
# Next user should be user2 for t1 also
assert next_user_to_send_request(session, t1) == u2
# Still starting order (because we rejected)
users = all_users_sorted(session)
assert users == [u2, u3, u1]
# Next user should be user2
assert next_user_to_send_request(session, t1) == u2
r3 = ParticipationRequest(user=u3, task=t1, requested_at=utc_now())
session.add(r3)
# Next user should still be user2
assert next_user_to_send_request(session, t1) == u2
r2 = ParticipationRequest(user=u2, task=t1, requested_at=utc_now())
session.add(r2)
# No user should be left
assert next_user_to_send_request(session, t1) is None
session.commit()
# We sent one request too much...
assert t1.additional_requests_to_be_sent() == -1
def test_accept(session):
now = datetime.datetime(2024, 12, 10, 0, 0)
dt = datetime.timedelta(days=1)
@ -390,5 +487,3 @@ def test_reject(session):
assert(r2.state == ParticipationState.REJECTED)
session.commit()