Add event type handling for duties in the system

- Introduced a new `event_type` column in the `duties` table to categorize duties as 'duty', 'unavailable', or 'vacation'.
- Updated the duty schedule import functionality to parse and store event types from the JSON input.
- Enhanced the API response to include event types for each duty, improving the calendar display logic.
- Modified the web application to visually differentiate between duty types in the calendar and duty list.
- Updated tests to cover new event type functionality and ensure correct parsing and storage of duties.
- Revised README documentation to reflect changes in duty event types and their representation in the system.
This commit is contained in:
2026-02-17 23:01:07 +03:00
parent 78a1696a69
commit 7a963eccd1
12 changed files with 279 additions and 60 deletions

View File

@@ -5,10 +5,9 @@ from datetime import date
import pytest
from db import init_db
from db.models import Base
from db.repository import get_duties
from db.session import get_session
from importers.duty_schedule import DutyScheduleResult, parse_duty_schedule
from importers.duty_schedule import DutyScheduleEntry, DutyScheduleResult, parse_duty_schedule
from handlers.import_duty_schedule import _run_import
@@ -36,17 +35,28 @@ def test_import_creates_users_and_duties(db_url):
start_date=date(2026, 2, 16),
end_date=date(2026, 2, 18),
entries=[
("Ivanov I.I.", [date(2026, 2, 16), date(2026, 2, 18)]),
("Petrov P.P.", [date(2026, 2, 17)]),
DutyScheduleEntry(
full_name="Ivanov I.I.",
duty_dates=[date(2026, 2, 16), date(2026, 2, 18)],
unavailable_dates=[],
vacation_dates=[],
),
DutyScheduleEntry(
full_name="Petrov P.P.",
duty_dates=[date(2026, 2, 17)],
unavailable_dates=[],
vacation_dates=[],
),
],
)
num_users, num_duties = _run_import(db_url, result, 6, 0)
num_users, num_duty, num_unav, num_vac = _run_import(db_url, result, 6, 0)
assert num_users == 2
assert num_duties == 3
assert num_duty == 3
assert num_unav == 0
assert num_vac == 0
session = get_session(db_url)
try:
# to_date inclusive: duty on 18th has start_at 2026-02-18T06:00:00Z, so use 2026-02-19
duties = get_duties(session, "2026-02-16", "2026-02-19")
finally:
session.close()
@@ -56,6 +66,8 @@ def test_import_creates_users_and_duties(db_url):
assert "2026-02-16T06:00:00Z" in starts
assert "2026-02-17T06:00:00Z" in starts
assert "2026-02-18T06:00:00Z" in starts
for d, _ in duties:
assert d.event_type == "duty"
def test_import_replaces_duties_in_range(db_url):
@@ -63,7 +75,14 @@ def test_import_replaces_duties_in_range(db_url):
result1 = DutyScheduleResult(
start_date=date(2026, 2, 16),
end_date=date(2026, 2, 17),
entries=[("Sidorov", [date(2026, 2, 16), date(2026, 2, 17)])],
entries=[
DutyScheduleEntry(
full_name="Sidorov",
duty_dates=[date(2026, 2, 16), date(2026, 2, 17)],
unavailable_dates=[],
vacation_dates=[],
)
],
)
_run_import(db_url, result1, 9, 0)
@@ -77,7 +96,14 @@ def test_import_replaces_duties_in_range(db_url):
result2 = DutyScheduleResult(
start_date=date(2026, 2, 16),
end_date=date(2026, 2, 17),
entries=[("Sidorov", [date(2026, 2, 17)])],
entries=[
DutyScheduleEntry(
full_name="Sidorov",
duty_dates=[date(2026, 2, 17)],
unavailable_dates=[],
vacation_dates=[],
)
],
)
_run_import(db_url, result2, 9, 0)
@@ -97,9 +123,11 @@ def test_import_full_flow_parse_then_import(db_url):
'"schedule": [{"name": "Alexey A.", "duty": "\u0431; ; \u0432"}]}'
).encode("utf-8")
parsed = parse_duty_schedule(raw)
num_users, num_duties = _run_import(db_url, parsed, 6, 0)
num_users, num_duty, num_unav, num_vac = _run_import(db_url, parsed, 6, 0)
assert num_users == 1
assert num_duties == 2
assert num_duty == 2
assert num_unav == 0
assert num_vac == 0
session = get_session(db_url)
try:
@@ -108,3 +136,32 @@ def test_import_full_flow_parse_then_import(db_url):
session.close()
assert len(duties) == 2
assert duties[0][1] == "Alexey A."
assert duties[0][0].event_type == "duty"
def test_import_event_types_unavailable_vacation(db_url):
"""Import creates records with event_type duty, unavailable, vacation."""
result = DutyScheduleResult(
start_date=date(2026, 2, 16),
end_date=date(2026, 2, 18),
entries=[
DutyScheduleEntry(
full_name="Mixed User",
duty_dates=[date(2026, 2, 16)],
unavailable_dates=[date(2026, 2, 17)],
vacation_dates=[date(2026, 2, 18)],
),
],
)
num_users, num_duty, num_unav, num_vac = _run_import(db_url, result, 6, 0)
assert num_users == 1
assert num_duty == 1 and num_unav == 1 and num_vac == 1
session = get_session(db_url)
try:
duties = get_duties(session, "2026-02-16", "2026-02-19")
finally:
session.close()
assert len(duties) == 3
types = {d[0].event_type for d in duties}
assert types == {"duty", "unavailable", "vacation"}