matplobbot-shared 0.1.30__py3-none-any.whl → 0.1.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of matplobbot-shared might be problematic. Click here for more details.
- {matplobbot_shared-0.1.30.dist-info → matplobbot_shared-0.1.31.dist-info}/METADATA +1 -1
- {matplobbot_shared-0.1.30.dist-info → matplobbot_shared-0.1.31.dist-info}/RECORD +5 -5
- shared_lib/services/schedule_service.py +72 -27
- {matplobbot_shared-0.1.30.dist-info → matplobbot_shared-0.1.31.dist-info}/WHEEL +0 -0
- {matplobbot_shared-0.1.30.dist-info → matplobbot_shared-0.1.31.dist-info}/top_level.txt +0 -0
|
@@ -2,9 +2,9 @@ shared_lib/__init__.py,sha256=Wxuw1wbvCOsKqs6WfIQ05cx_vndhPs6rH2krMatFRqA,45
|
|
|
2
2
|
shared_lib/database.py,sha256=vGiCxk1m_xZxaJQtjQPC_7G2Wxnu-ptWx5Z7q_XDIGs,20706
|
|
3
3
|
shared_lib/i18n.py,sha256=VBWQWVF-k_HDiidYo_RUPyUCM7oL897z5hOw9jvOoYY,1762
|
|
4
4
|
shared_lib/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
shared_lib/services/schedule_service.py,sha256=
|
|
5
|
+
shared_lib/services/schedule_service.py,sha256=UIyfNqXLJPsstGR8tGhbZ_hnK64QNxbaB-fUc7itX34,4355
|
|
6
6
|
shared_lib/services/university_api.py,sha256=Ui-zjfKOHCCf2Imh8CNtVOWegwuep7IB8gO9IKNUrrE,1898
|
|
7
|
-
matplobbot_shared-0.1.
|
|
8
|
-
matplobbot_shared-0.1.
|
|
9
|
-
matplobbot_shared-0.1.
|
|
10
|
-
matplobbot_shared-0.1.
|
|
7
|
+
matplobbot_shared-0.1.31.dist-info/METADATA,sha256=4b8ZQlFh79zZWjllQEuBRj2Vil3v0pPr4CUr6nMLg7o,395
|
|
8
|
+
matplobbot_shared-0.1.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
matplobbot_shared-0.1.31.dist-info/top_level.txt,sha256=L8mrC50YWCe19jmh_zrUZFvXSkhsnES5K6y027G1838,11
|
|
10
|
+
matplobbot_shared-0.1.31.dist-info/RECORD,,
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# bot/services/schedule_service.py
|
|
2
2
|
|
|
3
3
|
from typing import List, Dict, Any
|
|
4
|
-
from datetime import datetime, date
|
|
4
|
+
from datetime import datetime, date, time
|
|
5
5
|
from collections import defaultdict
|
|
6
|
+
from ics import Calendar, Event
|
|
7
|
+
from zoneinfo import ZoneInfo
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
from shared_lib.i18n import translator
|
|
10
|
+
|
|
11
|
+
names_shorter = defaultdict(lambda: 'Unknown')
|
|
8
12
|
to_add = {
|
|
9
13
|
'Практические (семинарские) занятия': 'Семинар',
|
|
10
14
|
'Лекции': 'Лекция',
|
|
@@ -15,38 +19,79 @@ names_shorter.update(to_add)
|
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
def format_schedule(schedule_data: List[Dict[str, Any]], lang: str, entity_name: str, start_date: date) -> str:
|
|
22
|
+
def format_schedule(schedule_data: List[Dict[str, Any]], lang: str, entity_name: str, entity_type: str, start_date: date, is_week_view: bool = False) -> str:
|
|
19
23
|
"""Formats a list of lessons into a readable daily schedule."""
|
|
20
24
|
if not schedule_data:
|
|
21
|
-
|
|
25
|
+
# Different message for single day vs week
|
|
26
|
+
no_lessons_key = "schedule_no_lessons_week" if is_week_view else "schedule_no_lessons_day"
|
|
27
|
+
return f"🗓 *Расписание для \"{entity_name}\"*\n\n{translator.gettext(lang, no_lessons_key)}"
|
|
22
28
|
|
|
23
29
|
# Group lessons by date
|
|
24
|
-
days =
|
|
30
|
+
days = defaultdict(list)
|
|
25
31
|
for lesson in schedule_data:
|
|
26
|
-
|
|
27
|
-
if date_str not in days:
|
|
28
|
-
days[date_str] = []
|
|
29
|
-
days[date_str].append(lesson)
|
|
32
|
+
days[lesson['date']].append(lesson)
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
formatted_days = []
|
|
35
|
+
# Iterate through sorted dates to build the full schedule string
|
|
32
36
|
for date_str, lessons in sorted(days.items()):
|
|
33
37
|
date_obj = datetime.strptime(date_str, "%Y-%m-%d").date()
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
38
|
+
|
|
39
|
+
day_header = f"*{date_obj.strftime('%A, %d.%m.%Y')}*"
|
|
40
|
+
|
|
41
|
+
formatted_lessons = []
|
|
42
|
+
for lesson in sorted(lessons, key=lambda x: x['beginLesson']):
|
|
43
|
+
lesson_details = [
|
|
44
|
+
f"`{lesson['beginLesson']} - {lesson['endLesson']}`",
|
|
45
|
+
f"{lesson['discipline']} | {names_shorter[lesson['kindOfWork']]}"
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
if entity_type == 'group':
|
|
49
|
+
lesson_details.append(f"*{lesson['auditorium']}* | {lesson['lecturer_title'].replace('_',' ')}")
|
|
50
|
+
elif entity_type == 'person': # Lecturer
|
|
51
|
+
lesson_details.append(f"*{lesson['auditorium']}* | {lesson.get('group', 'Группа не указана')}")
|
|
52
|
+
elif entity_type == 'auditorium':
|
|
53
|
+
lesson_details.append(f"{lesson.get('group', 'Группа не указана')} | {lesson['lecturer_title'].replace('_',' ')}")
|
|
54
|
+
else: # Fallback to a generic format
|
|
55
|
+
lesson_details.append(f"*{lesson['auditorium']}* | {lesson['lecturer_title'].replace('_',' ')}")
|
|
56
|
+
|
|
57
|
+
formatted_lessons.append("\n".join(lesson_details))
|
|
58
|
+
|
|
59
|
+
formatted_days.append(f"{day_header}\n" + "\n\n".join(formatted_lessons))
|
|
60
|
+
|
|
61
|
+
main_header = f"🗓 *Расписание для \"{entity_name}\"*"
|
|
62
|
+
return f"{main_header}\n\n" + "\n\n---\n\n".join(formatted_days)
|
|
63
|
+
|
|
64
|
+
def generate_ical_from_schedule(schedule_data: List[Dict[str, Any]], entity_name: str) -> str:
|
|
65
|
+
"""
|
|
66
|
+
Generates an iCalendar (.ics) file string from schedule data.
|
|
67
|
+
"""
|
|
68
|
+
cal = Calendar()
|
|
69
|
+
moscow_tz = ZoneInfo("Europe/Moscow")
|
|
70
|
+
|
|
71
|
+
if not schedule_data:
|
|
72
|
+
return cal.serialize()
|
|
73
|
+
|
|
74
|
+
for lesson in schedule_data:
|
|
75
|
+
try:
|
|
76
|
+
event = Event()
|
|
77
|
+
event.name = f"{lesson['discipline']} ({names_shorter[lesson['kindOfWork']]})"
|
|
48
78
|
|
|
49
|
-
|
|
79
|
+
lesson_date = datetime.strptime(lesson['date'], "%Y-%m-%d").date()
|
|
80
|
+
start_time = time.fromisoformat(lesson['beginLesson'])
|
|
81
|
+
end_time = time.fromisoformat(lesson['endLesson'])
|
|
82
|
+
|
|
83
|
+
event.begin = datetime.combine(lesson_date, start_time, tzinfo=moscow_tz)
|
|
84
|
+
event.end = datetime.combine(lesson_date, end_time, tzinfo=moscow_tz)
|
|
50
85
|
|
|
51
|
-
|
|
52
|
-
|
|
86
|
+
event.location = f"{lesson['auditorium']}, {lesson['building']}"
|
|
87
|
+
|
|
88
|
+
description_parts = [f"Преподаватель: {lesson['lecturer_title'].replace('_',' ')}"]
|
|
89
|
+
if 'group' in lesson: description_parts.append(f"Группа: {lesson['group']}")
|
|
90
|
+
event.description = "\n".join(description_parts)
|
|
91
|
+
|
|
92
|
+
cal.events.add(event)
|
|
93
|
+
except (ValueError, KeyError) as e:
|
|
94
|
+
logging.warning(f"Skipping lesson due to parsing error: {e}. Lesson data: {lesson}")
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
return cal.serialize()
|
|
File without changes
|
|
File without changes
|