From 4445c613f734a55351f5b58c8bd1b01a232e8e36 Mon Sep 17 00:00:00 2001 From: Mihail Date: Sun, 5 Apr 2026 17:45:26 +0400 Subject: [PATCH 1/4] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=20?= =?UTF-8?q?=D0=BF=D0=B0=D1=80=D1=81=D0=B5=D1=80=20=D1=80=D0=B0=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- parser.py | 191 ++++++++++++++++++++++++++++++++++++++++++++++ static/index.html | 36 +++++++++ static/style.css | 62 +++++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 parser.py create mode 100644 static/index.html create mode 100644 static/style.css diff --git a/parser.py b/parser.py new file mode 100644 index 00000000..9715a7e4 --- /dev/null +++ b/parser.py @@ -0,0 +1,191 @@ +import requests +from bs4 import BeautifulSoup +from typing import List, Dict, Optional, Any + + +BASE_URL = "https://ssau.ru/rasp" + + +def get_headers() -> Dict[str, str]: + return { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" + } + + +def get_groups() -> List[Dict[str, str]]: + return [ + {"id": "1213641978", "name": "6413-100503D"}, + {"id": "1282690301", "name": "6411-100503D"}, + {"id": "1282690279", "name": "6412-100503D"}, + ] + + +def get_teachers() -> List[Dict[str, str]]: + return [ + {"id": "432837452", "name": "Юзькив Р.Р."}, + {"id": "114869468", "name": "Сергеев А.В."}, + {"id": "62061001", "name": "Мясников В.В."}, + {"id": "594502705", "name": "Чернышев П.В."}, + {"id": "335824546", "name": "Максимов А.И."}, + {"id": "664017039", "name": "Борисов А.Н."}, + {"id": "364272302", "name": "Агафонов А.А."}, + {"id": "147619112", "name": "Кузнецов А.В."}, + {"id": "333991624", "name": "Веричев А.В."}, + {"id": "544973937", "name": "Шапиро Д.А."}, + ] + + +def build_url(schedule_type: str, entity_id: str, week: Optional[int] = None) -> str: + param = "groupId" if schedule_type == "group" else "staffId" + params = {param: entity_id} + if week: + params["selectedWeek"] = str(week) + query = "&".join(f"{k}={v}" for k, v in params.items()) + return f"{BASE_URL}?{query}" + + +def get_week_number(soup: BeautifulSoup) -> int: + week_span = soup.select_one("span.week-nav-current_week") + if not week_span: + return 30 + + try: + return int(week_span.get_text(strip=True).split()[0]) + except ValueError: + return 30 + + +def get_entity_name(soup: BeautifulSoup, default_id: str) -> str: + title = soup.select_one(".info-block__title") or soup.select_one("h1.h1-text") + if not title: + return default_id + + text = title.get_text(strip=True) + return text.replace("Расписание, ", "").strip() + + +def get_days(soup: BeautifulSoup) -> List[Dict[str, str]]: + days = [] + for head in soup.select(".schedule__head")[1:]: + weekday = head.select_one(".schedule__head-weekday") + date = head.select_one(".schedule__head-date") + if weekday and date: + days.append({ + "weekday": weekday.get_text(strip=True).capitalize(), + "date": date.get_text(strip=True), + }) + return days + + +def parse_groups(groups_block) -> Dict[str, Any]: + if not groups_block: + return {"subgroup": "", "group_info": "", "group_was_parsed": False} + + subgroup = "" + caption = groups_block.select_one("span.caption-text") + if caption and "подгруппы" in caption.get_text().lower(): + subgroup = caption.get_text(strip=True) + + group_links = groups_block.select("a.schedule__group") + group_info = ", ".join(link.get_text(strip=True) for link in group_links) + + return { + "subgroup": subgroup, + "group_info": group_info, + "group_was_parsed": bool(group_links), + } + + +def parse_lesson(lesson_elem) -> Dict[str, Any]: + type_chip = lesson_elem.select_one(".schedule__lesson-type-chip") + lesson_type = type_chip.get_text(strip=True) if type_chip else "" + + subject = lesson_elem.select_one(".schedule__discipline") + subject = subject.get_text(strip=True) if subject else "" + + room_elem = lesson_elem.select_one(".schedule__place") + room = room_elem.get_text(strip=True) if room_elem else "online" + + teacher_elem = lesson_elem.select_one(".schedule__teacher") + teacher = teacher_elem.get_text(strip=True) if teacher_elem else "" + + groups_block = lesson_elem.select_one(".schedule__groups") + groups_data = {} + + if groups_block: + groups_data = parse_groups(groups_block) + else: + group_links = lesson_elem.select("a.schedule__group") + if group_links: + group_info = ", ".join(link.get_text(strip=True) for link in group_links) + groups_data = { + "subgroup": "", + "group_info": group_info, + "group_was_parsed": True, + } + else: + groups_data = { + "subgroup": "", + "group_info": "", + "group_was_parsed": False, + } + + return { + "subject": subject, + "type": lesson_type, + "teacher": teacher, + "room": room, + "subgroup": groups_data["subgroup"], + "groups": groups_data["group_info"], + } + + +def build_grid(soup: BeautifulSoup, num_days: int) -> List[List[Optional[List[Dict[str, Any]]]]]: + all_cells = [] + + for item in soup.select(".schedule__item"): + if "schedule__head" in (item.get("class") or []): + continue + lessons = item.select(".schedule__lesson") + cell_data = [parse_lesson(lesson) for lesson in lessons] if lessons else None + all_cells.append(cell_data) + return [all_cells[i:i + num_days] for i in range(0, len(all_cells), num_days)] + + +def get_time_slots(soup: BeautifulSoup) -> List[str]: + time_slots = [] + for time_block in soup.select(".schedule__time"): + times = time_block.select(".schedule__time-item") + if len(times) >= 2: + start = times[0].get_text(strip=True) + end = times[1].get_text(strip=True) + time_slots.append(f"{start}–{end}") + return time_slots + + +def get_schedule(schedule_type: str, entity_id: str, week: Optional[int] = None) -> Dict[str, Any]: + url = build_url(schedule_type, entity_id, week) + + response = requests.get(url, headers=get_headers()) + response.raise_for_status() + + soup = BeautifulSoup(response.text, "html.parser") + + current_week = get_week_number(soup) + entity_name = get_entity_name(soup, entity_id) + days = get_days(soup) + num_days = len(days) or 6 + + grid = build_grid(soup, num_days) + time_slots = get_time_slots(soup) + + while len(time_slots) < len(grid): + time_slots.append("—") + + return { + "week": current_week, + "entity_name": entity_name, + "days": days, + "time_slots": time_slots[:len(grid)], + "grid": grid, + } \ No newline at end of file diff --git a/static/index.html b/static/index.html new file mode 100644 index 00000000..7aa02c83 --- /dev/null +++ b/static/index.html @@ -0,0 +1,36 @@ + + + + + + + + Lab2 + + + +
+
+

Расписание, 6411-100503DX

+
+
+
+

Введите ФИО преподавателя

+ + +
+
+

Введите номер группы

+ + +
+
+ +
+

6411-100503DX

+
+
+
+ + + \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 00000000..3d81aa7e --- /dev/null +++ b/static/style.css @@ -0,0 +1,62 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: #F5F7FC; +} + +.main-container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + background-color: #b5d7de; + padding-left: 30px; +} + +.h1-group-name { + padding-top: 20px; + padding-bottom: 20px; +} + +.search-block { + display: flex; + flex-direction: column; + margin-bottom: 30px; +} + +.teacherSearch>p, +.groupSearch>p { + font-weight: bold; +} + +.teacherSearch { + padding-bottom: 20px; +} + +.teacherSearchInput, +.groupSearchInput { + height: 30px; + width: 200px; + font-size: 15px; + border-radius: 8px; +} + +button { + height: 30px; + width: 70px; + font-size: 15px; + border-radius: 8px; +} + +.info-block { + display: flex; + flex-direction: column; +} + +.h2-group-name { + text-align: center; +} + From d416cf17ae73acc358d41de0e378fe652d56051a Mon Sep 17 00:00:00 2001 From: Mihail Date: Mon, 6 Apr 2026 01:29:42 +0400 Subject: [PATCH 2/4] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=20=D0=BF=D0=B0=D1=80=D1=81=D0=B5=D1=80=20+=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BF=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D0=B9=20=D0=B1=D1=8D=D0=BA=D0=B5=D0=BD=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/parser.cpython-314.pyc | Bin 0 -> 12379 bytes parser.py | 16 ++++---- server.py | 58 +++++++++++++++++++++++++++++ static/index.html | 2 +- 4 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 __pycache__/parser.cpython-314.pyc create mode 100644 server.py diff --git a/__pycache__/parser.cpython-314.pyc b/__pycache__/parser.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5f390776f3f2c97995241014a76a5356b49ff0d GIT binary patch literal 12379 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 literal 0 HcmV?d00001 diff --git a/parser.py b/parser.py index 9715a7e4..e071652a 100644 --- a/parser.py +++ b/parser.py @@ -1,4 +1,4 @@ -import requests +import aiohttp from bs4 import BeautifulSoup from typing import List, Dict, Optional, Any @@ -12,7 +12,7 @@ def get_headers() -> Dict[str, str]: } -def get_groups() -> List[Dict[str, str]]: +async def get_groups() -> List[Dict[str, str]]: return [ {"id": "1213641978", "name": "6413-100503D"}, {"id": "1282690301", "name": "6411-100503D"}, @@ -20,7 +20,7 @@ def get_groups() -> List[Dict[str, str]]: ] -def get_teachers() -> List[Dict[str, str]]: +async def get_teachers() -> List[Dict[str, str]]: return [ {"id": "432837452", "name": "Юзькив Р.Р."}, {"id": "114869468", "name": "Сергеев А.В."}, @@ -163,13 +163,15 @@ def get_time_slots(soup: BeautifulSoup) -> List[str]: return time_slots -def get_schedule(schedule_type: str, entity_id: str, week: Optional[int] = None) -> Dict[str, Any]: +async def get_schedule(schedule_type: str, entity_id: str, week: Optional[int] = None) -> Dict[str, Any]: url = build_url(schedule_type, entity_id, week) - response = requests.get(url, headers=get_headers()) - response.raise_for_status() + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=get_headers()) as response: + response.raise_for_status() + html = await response.text() - soup = BeautifulSoup(response.text, "html.parser") + soup = BeautifulSoup(html, "html.parser") current_week = get_week_number(soup) entity_name = get_entity_name(soup, entity_id) diff --git a/server.py b/server.py new file mode 100644 index 00000000..2be91952 --- /dev/null +++ b/server.py @@ -0,0 +1,58 @@ +from aiohttp import web +from aiohttp.web import Request, FileResponse +import os + +from parser import get_groups, get_teachers, get_schedule + + +async def handle_root(request: Request) -> FileResponse: + return FileResponse("static/index.html") + + +async def handle_api_groups(request: Request) -> web.Response: + try: + groups = await get_groups() + return web.json_response(groups) + except Exception as e: + return web.json_response({"error": str(e)}, status=500) + + +async def handle_api_teachers(request: Request) -> web.Response: + try: + teachers = await get_teachers() + return web.json_response(teachers) + except Exception as e: + return web.json_response({"error": str(e)}, status=500) + + +async def handle_api_schedule(request: Request) -> web.Response: + type_param = request.query.get("type", "group") + id_param = request.query.get("id") + week_param = request.query.get("week") + + if not id_param: + return web.json_response({"error": "Need id"}, status=400) + + if type_param not in ["group", "teacher"]: + return web.json_response({"error": "type = group or teacher"}, status=400) + + try: + week = int(week_param) if week_param else None + result = await get_schedule(type_param, id_param, week) + return web.json_response(result) + except Exception as e: + return web.json_response({"error": str(e)}, status=500) + + +app = web.Application() + +app.router.add_get("/", handle_root) +app.router.add_get("/api/groups", handle_api_groups) +app.router.add_get("/api/teachers", handle_api_teachers) +app.router.add_get("/api/schedule", handle_api_schedule) +app.router.add_static("/static", "static") + + + +if __name__ == "__main__": + web.run_app(app, host="127.0.0.1", port=8000) \ No newline at end of file diff --git a/static/index.html b/static/index.html index 7aa02c83..002ae06b 100644 --- a/static/index.html +++ b/static/index.html @@ -4,7 +4,7 @@ - + Lab2 From 7fd4adcdf3a14c51b30ca7b5e6a938bb0efc791f Mon Sep 17 00:00:00 2001 From: Mihail Date: Mon, 6 Apr 2026 10:24:19 +0400 Subject: [PATCH 3/4] =?UTF-8?q?=D0=B3=D0=BE=D1=82=D0=BE=D0=B2=D1=8B=D0=B9?= =?UTF-8?q?=20=D1=84=D1=80=D0=BE=D0=BD=D1=82=D0=B5=D0=BD=D0=B4=20=D1=80?= =?UTF-8?q?=D0=B0=D1=81=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/parser.cpython-314.pyc | Bin 12379 -> 0 bytes server.py | 2 +- static/index.html | 15 ++- static/script.js | 182 +++++++++++++++++++++++++++++ static/style.css | 141 ++++++++++++++++++++++ 5 files changed, 335 insertions(+), 5 deletions(-) delete mode 100644 __pycache__/parser.cpython-314.pyc create mode 100644 static/script.js diff --git a/__pycache__/parser.cpython-314.pyc b/__pycache__/parser.cpython-314.pyc deleted file mode 100644 index e5f390776f3f2c97995241014a76a5356b49ff0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12379 zcmc&aYj9K7nfK~mNmma`vLqXfAIMKoj3qy08$T1k1```XE=FWh7p<@b2!yTND;03N zsnT}3X4omFn@p4~nJ81bHB;K9Te6|uBu&_|vpd`ETty~Q@3I5i&CE__|CmxXo6P>$ z?>kp78}rDI&K_Ime)l}SbM86c`?fiJnlWP!WC0U$;lpm=&G z6~U)!Cq1HysMaIQ>|{pR2s@&Vs7Evr&4@OlT~G0BgyYp~C|)WdN5GxRhT5@LB>} z`3iv75!eP}RKi;ozQGuFp01~yHJ{+e?RL^~$<}~B8VMeXhW5@x!%kLa$AV%+W`}~4 z5t(}?90|@$`$ICbW%|^t{YWGd7JJ=pQS?V$Lewqz#qcBznDB>*g&(fl-R+RYC@*zD zwfS2VH3el=)U)&vnx`kAMoAN@`=JiiCfJ8ua>HCNsy@MXvPK9*qQbOOC8+V05hDUj zUvp?iIPQ;#IDtC3Y^ZnQMBtzpm~3gE2>S&wAh^P(WTVgLpPrtH_#*+IPcTDM9sI;f z$YRu|6hmw7VD|F(`TFx>eB&zz$m?CGrY14VC7cKOIwYDH#REm)44tZ3{XQ6~Wy|5f zbYymB*UW4%6!N<}U9FCd`-9U{GbcpH?r}$3tE;uo0R>&2KF7%}kJGUw91aEc2M&$~ zBks(^_^z={j!^Jez_BARd2Gh%*mgviIUaDgdAeMH0^g$fl7DcTaRchC8Rm zorCOr!}-XCZI^6`k=I|mvCc)xl&^w@`J@5poAA5439>qBJ)poXFV(O_*CP+IuSrwd;^=<8K9bKNbp6<;uJMBLnm^H#%M@w64YiDc65Y}&Q@9Jso zXl)}kZTHo*clVH*_FRp!b1Sw_kK1=x0Cp6GDwrzya0|Kt7&NO=$2(=||^-%}&*DP-q|F4!+jUg}63`9aN%s#a2V z|DGb9kUbRuc$__jedb_yb!4CQ02v5m1gL~SCVDmwq8#mHx&dC0y_?>)P5po4a~EU7Mr0DAJqK zTgjO8GwCh(gGTc%>ANnO>uPW90-|b*B5vt?@@(=Y>0Lnc3+X*<^B?f0E=#`vd{?D+6l^eN946M)dM8j>NQ(d*C%sK33ty`!~V!8H#vznuIQG^mKRhbaX3gx&YJvCCn=( zz2}f#cEO6R%0fnDp-8|#c?3nE6PA(C*f{`lO!U?GcB!F9;(C#b$V;EZ_2bKIyi2O@ zm$-p^&49!WuKtqiLl8V>GaPiO%z!Lv$g^l?_i`5HV5&p}9{}J{BA8bdv?|XO=~d)@ zl8OZ{&lXy{Sp+&*A&`4~73}v;3uDyfV`Mpc{xTHMZ;eKU%w}g}|K08htC`^vuo=bUkbO}mu zEqH44b2+Pmr+0w}4>SAQ+;o`rsy1YMO@Y{~q9$^d3FTFJvapxs8Jrr=?kOH6+YjwF zFS`fFT#-E4cMFw+au~zv30x48j9~S7)kdlvl!sF*tBD|&wdApLO4f=I|Di(w7{x#+ zFc}F|}@-Gn!sjI46C$}|k}6%jwU$>C7!1YJKEEP<0@VNk9*7!8J|d{H6f>JQCK z`a|NND_`3TokY|`$UBm~@8a$^hZ0-j^t>3ajcN@Vuv zOmJFO2P1*wqRegGvUk|Gj~{bt1k}I+QikAyOjd)f=07fL$U~IXFGK^vDG`^nsR?GA zn{IxN!Vp%$PaKCVMrA6R7B(zaw4L60`BZE;W3ovl>tow*X$?1P8xrH+ub9)!y_n=` zGDh>+k<%kdd*8zLzaG0dc4>FY-j_1=#fER1%IBV+e<5BL*GdM5#5(S`iEv8@L$;D3 zA8y(LL*|WJw+dK{QCN%>L8~i_q!JLD;D+pBJcRWkn#9OM9BsXW+4+_wd|cW_-U@St zW+dUtk-5GH05Z&;JR2wtFz2Z}`O;oG$2NzdUlnzrrk<*#CJ+ZnUV1f#K-;N1i+PVW z*S7G?vB?3dSp4Kht*0E+1d2J5G}IKZToqVl)nxP_)yGs*V0$sM>%_2s+Jz#hW!nFh zmdU6ffN1hz!T3d5R)Z%H3_I1bUg0R;%yfV(tIR1{;A8~aghRoItoQmu(ZH}E%n0B| z?0|t~9is8!2qNxSQvmz5;!M zv5ZvmfF5*?y27f0Do|+J3!>4hT2rVUzoZ*6kOxIv>( zEsQ}XYlT2KC;33hHUWoV2I zWen!C{ipk-wXK)yQijg7p)YCZOBn_vcAy}Ma`FbH0+B>V$e@o;u>}vZpjr#iL%d3m zw!(H84Auz;{sOM$HfSzxAoKi`($khZn7ur(gpUj3ml`gKSGFyj{ITmsV-Kk+d@b}L z3C$?-wY~8DG0H`S=?BnhUiR{Iv23(Z+rLIpNV}`kX`Z^72ee@FEm$vu--1b@|4|TM zUUo%U50tGg+oAtzbk3`JO^#Rgp!dEap)d&*gp|Gm0PvZi z1b7D2h2fk*s%lADTnj?l(k@xr=h%$3{8jfkw^Y}cvi8quGiKY_ub=+9w7xTC?wVt7 z8LX$fUyYoLAghj^9?e)P;@#=W=455_E$|3h}6;evQ+TjJyoAe;lQg{hNaA=}2(p)1#V29GLjVH}y`2ek-AMB01q3kzj4~%i4-(%*7>5S3p1?j-U$RAkPxwV2 ziKR|$pDiV(7_bP2g^4bUf6ghf3cr0`>M!C-g6?zGCdV>Y z2g!uEIzs^DT^-PkFO;rsweoas(Xy`3+N|pXk{_dW7a3*n-4|)X(ic_2X8bfZ6r4U* zY~%@jSiGKmTQ=Udiu3b4G%7eh>vnxmdDZ^Lz-t2wk@!Hm(j!%R{=V|FyT}jUsBzHX z+Z6P+Iy7?vOh@$Kg+UyPJvcKH0`X%cOCkD*fgl{lDs6UAA$W_CKOdQ_fLe2)CFAV%FXtjSo`#kVGm3kRk4q4xulc}(KQZktPEBN@}Ww5c&^YLuFKQl{S6 zj$2xjWZSUNoo?<+Hut5P2hz>klFi#v&BKef;f!rv+E$;m)h{$iw)&LKJ;&X))}*bC zNh?8M?Tw9QO3PlYI9HLxRY|Sel`7p8dwNMj>1^{O8O}a`G{dbsKa{9URkajCk_ATo zW6K~R088>Pxr35AFL#_McOWZ79m#89y3m*;3^@`d9|3dli#b4+>4wkh-ffi z1g!41u?2}xQ1MyJ%wu6@9)P(Z2ibz5kj1=utYrC|HDUDwFl+O3)_65JIi1CWC(Iu=LfhB7F_K5j0PU z*T9?Ll!yuIc5zAxl$s#G3?;n9%kZUM6>s%wd7D?w8@)Q-SZogRb|@(+E^%wXJSv;5 zEE21sfGDCchL~N;$iB^-q%6^=pn#}(zmaU5qFvl+-9D7=*=SVefFbSwz(x_$pt`M&*!18)vojwA-s z5aX@y|L4lj1Ux$+QkckFAqcPAAy5}RcoYJ3;9OxeRGVE5nH6ScAXKF+1el0M_^ zC$N}k2=vs6EsFLiB*g>FDhg#1!f9wj1l}{mT*_D~)0U>BrAgW_l(Gy%2*Wcl?@U`8 zlGcU=k7R9Fw6?zA9vi)Fsz{p}lBNc!u|H)R0HxvToI9L0)g(XRnaG^YARlly%h@D^K>Cq(}I_rFQhBM6@GF+(wu^G-fKbp|rus3Ho%Y1V@bi>-bRHDTgzIL?}RNN7wB>onF zN7>cI(LylRSm;7h5rmPSm1SrED`n_+DiDrK(sH}JScE(dd|!-+gD@CS9k@4WN;mFJbIcRau1er^Bt)Z$+6 z;&We;_I~MKrf=;0@{Oks-01V&==u|-o1&vZ8|M>_PnQ2AA$D-YAEZt5jySm9A#lAJ zo(9Jk`>sfQ-dQaQMnO9aM!6_r>yx+9MsTT_RZZ8t?G5J?c}IK-0_)%*uoh~cI;wI} zQ5DAd;Y6XJx$=5>w<;IskE+6IuoBoX`X{SsGOs457s0g#0g7h>6M1+209E7>H zd$QjRbJ1q!0$TJ1VgCs)y{TwDXbNUxReWq@3zyZakg>$fo6P zy{w)L`9*P=ic#d%Zm^`$aF!W}#1i2p`0!ON_@@sCWF{1tc5=i&7tUb0&L0Z-CIg`m zcu$D+pnGTA`JmhR4FD~`U@xoyN!k}@eB{aGK07iSQv7NW`KKw@( z7x?am$^`pE?oDoCP0G=hc63XQ?#uk5Ve@Z|cJ%9;&oxUmZ7FN}oc7kx=+Ct&`%^#H zE*yG!BGL3wne*q`5A9D$&+ePkfs9FWYm?mCc*RFt{T%~U>i&a~YVExI>^scWoquQg zM{d#mY|6kV-On!6&imsduNzawMtDmac!}jd`@Nbf+d_Z#I|DBw^M6oo-`cLZR%P4T z%`S@_BRS#rKGxNoVu@6cLdB8;Pc@V{8&ec#!x=n`@4pB2Vhv=+wOOBx*8Ce5{2Er$?H0ZN3@r6h+Q*_ReXHkfQypxZdH6cqc zKgC$h@rSzsg*Ydy=`tdV_g%cU)h+p)+eoK zI#}ydR`=YtjIlgztWFxM6J-f)LR{!rXq0MNKQgufYpv;AGE&BJi7hKoSdKRGvD+A# z6au7AS$VF_O)DBok7N0+`=sUPLby)HpzVZAK(pXuXtS$gV6ovEnCj9Q& zp;QUd6i(Ln({Q$)Z5=MUeXxhcuFF$x{_MPu_Ayaal<}!@@vPaOq3C+Qe7rq)xa zC0At#rl84keIY26)&GxySM62rK{IAW%He{;CG{Tk@>ir>{SJsNE}N$YE*0T%YIY5F zz-6-Hdbkhzi24c8rAm4ZuAb$NnZnu$JQ5_yTkxQ{3A|lI(*76nZFLiPp@^iSw)zRY z_o9^QbHd|(`{jTpY?!FXwcP*JJXYfbUXmoK2y00Z){-37qB3KyY%>H0r|iVODcMt< z!&G?kd2tI?$|k&#;e~s%;PYy{dN@ER;i=WI(r|^#YjmsYDJ?ZjkCkbuvHybKb@;vQ zpqeSN{y=*Q9cd|jAu?0=;igZVNhKONB#G6)Lz+m9w2Y9^7 zI5H;$#PH0t7=S|$_zp!M7>m!{g$sy~OdqW;;Rl!z5f8R&_Rkam!T+KeE_XmQ^3}d`eR1*9=#O^I z_oZ#U@V{v5y?P{Lw5P!yGBz$6H)JYm->81A`g`lswvBT`pQSB()9^8RoWPP`eS{`+|e`c^V<^a_jWGo8^J?Na}`OhBHs8B=fEhadES`e z%I3d{t|T~+5D!hPyHKB)`o2>t-$V+R3>x$xHEPZ88)1&Wwe|kHZDVfQF!K*>y`Sf1 z4D|sU0ry&F4vHQ^qOt}e zAHnIvMQ5|9ABoo}JcSTSICA%u+}$Hwv68Maw4?Y11>#1sT6r0o>pwn&m)Zt}pF%Ba zd+|$xX diff --git a/server.py b/server.py index 2be91952..b45fa543 100644 --- a/server.py +++ b/server.py @@ -50,7 +50,7 @@ async def handle_api_schedule(request: Request) -> web.Response: app.router.add_get("/api/groups", handle_api_groups) app.router.add_get("/api/teachers", handle_api_teachers) app.router.add_get("/api/schedule", handle_api_schedule) -app.router.add_static("/static", "static") +app.router.add_static('/static/', path='static', name='static') diff --git a/static/index.html b/static/index.html index 002ae06b..815bb4bc 100644 --- a/static/index.html +++ b/static/index.html @@ -11,26 +11,33 @@
-

Расписание, 6411-100503DX

+

Расписание, 641X-100503D

Введите ФИО преподавателя

- +

Введите номер группы

- +
+
+ + + +
+

6411-100503DX

+ + - \ No newline at end of file diff --git a/static/script.js b/static/script.js new file mode 100644 index 00000000..bb6917a5 --- /dev/null +++ b/static/script.js @@ -0,0 +1,182 @@ +$(document).ready(function () { + + const groupBtn = $('.groupSearchBtn'); + const groupInput = $('.groupSearchInput'); + const teacherBtn = $('.teacherSearchBtn'); + const teacherInput = $('.teacherSearchInput'); + const h2GroupName = $('.h2-group-name'); + const scheduleDiv = $('.schedule'); + const prevWeekBtn = $('#prevWeekBtn'); + const nextWeekBtn = $('#nextWeekBtn'); + const currentWeekInfo = $('#currentWeekInfo'); + + + let currentType = null; + let currentId = null; + let currentName = null; + let currentWeek = null; + + + function renderSchedule(data) { + if (!data.days || data.days.length === 0) { + scheduleDiv.html('

Расписание не найдено

'); + return; + } + + currentWeek = data.week; + currentWeekInfo.text(`Неделя ${currentWeek}`); + + let table = ''; + + table += ''; + table += ''; + table += ''; + for (let i = 0; i < data.days.length; i++) { + const day = data.days[i]; + table += ``; + } + table += ''; + table += ''; + + table += ''; + + for (let row = 0; row < data.time_slots.length; row++) { + table += ''; + table += ``; + + for (let col = 0; col < data.days.length; col++) { + const cellData = data.grid && data.grid[row] ? data.grid[row][col] : null; + + if (cellData && Array.isArray(cellData) && cellData.length > 0) { + table += ''; + } else { + table += ''; + } + } + table += ''; + } + + table += ''; + table += '
Время${day.weekday}
${day.date}
${data.time_slots[row]}'; + for (let k = 0; k < cellData.length; k++) { + const lesson = cellData[k]; + table += '
'; + table += `
${lesson.subject || '—'}
`; + table += `
${lesson.type || '—'}
`; + table += `
${lesson.teacher || '—'}
`; + table += `
${lesson.room || '—'}
`; + if (lesson.subgroup) { + table += `
${lesson.subgroup}
`; + } + if (lesson.groups && lesson.groups !== "") { + table += `
${lesson.groups}
`; + } + table += '
'; + if (k < cellData.length - 1) { + table += '
'; + } + } + table += '
'; + + scheduleDiv.html(table); + } + + + async function loadSchedule(type, id, name, week = null) { + try { + scheduleDiv.html('

Загрузка расписания...

'); + let url = `/api/schedule?type=${type}&id=${id}`; + if (week !== null) { + url += `&week=${week}`; + } + const response = await fetch(url); + const data = await response.json(); + currentType = type; + currentId = id; + currentName = name; + h2GroupName.text(data.entity_name || name); + renderSchedule(data); + + } catch (error) { + scheduleDiv.html('

Ошибка при загрузке расписания

'); + } + } + + + + async function nextWeek() { + if (!currentType || !currentId) { + alert('Сначала найдите группу или преподавателя'); + return; + } + const nextWeekNumber = currentWeek + 1; + await loadSchedule(currentType, currentId, currentName, nextWeekNumber); + } + + + + async function prevWeek() { + if (!currentType || !currentId) { + alert('Сначала найдите группу или преподавателя'); + return; + } + const prevWeekNumber = currentWeek - 1; + await loadSchedule(currentType, currentId, currentName, prevWeekNumber); + } + + + + async function searchGroup() { + const query = groupInput.val().trim(); + if (!query) { + alert('Введите номер группы'); + return; + } + try { + const response = await fetch('/api/groups'); + const groups = await response.json(); + const group = groups.find(g => g.name.startsWith(query)); + if (!group) { + alert(`Группа начинающаяся с "${query}" не найдена. Доступные группы: ${groups.map(g => g.name).join(', ')}`); + return; + } + await loadSchedule('group', group.id, group.name); + } catch (error) { + alert('Ошибка при поиске группы'); + } + } + + + + async function searchTeacher() { + const query = teacherInput.val().trim(); + + if (!query) { + alert('Введите ФИО преподавателя'); + return; + } + + try { + const response = await fetch('/api/teachers'); + const teachers = await response.json(); + + const teacher = teachers.find(t => { + const surname = t.name.split(' ')[0]; + return surname.toLowerCase() === query.toLowerCase() || + t.name.toLowerCase().includes(query.toLowerCase()); + }); + + if (!teacher) { + alert(`Преподаватель "${query}" не найден.`); + return; + } + + await loadSchedule('teacher', teacher.id, teacher.name); + + } catch (error) { alert('Ошибка при поиске преподавателя') }; + } + groupBtn.on('click', searchGroup); + teacherBtn.on('click', searchTeacher); + prevWeekBtn.on('click', prevWeek); + nextWeekBtn.on('click', nextWeek); +}); \ No newline at end of file diff --git a/static/style.css b/static/style.css index 3d81aa7e..fb7cacba 100644 --- a/static/style.css +++ b/static/style.css @@ -14,6 +14,8 @@ body { margin: 0 auto; background-color: #b5d7de; padding-left: 30px; + padding-right: 30px; + padding-bottom: 30px; } .h1-group-name { @@ -42,6 +44,8 @@ body { width: 200px; font-size: 15px; border-radius: 8px; + padding-left: 10px; + border: none; } button { @@ -49,6 +53,11 @@ button { width: 70px; font-size: 15px; border-radius: 8px; + border: none; +} + +button:hover { + background-color: #e0e0e0; } .info-block { @@ -60,3 +69,135 @@ button { text-align: center; } +.schedule { + margin-top: 20px; + overflow-x: auto; +} + +.schedule-table { + width: 100%; + border-collapse: collapse; + background-color: white; + font-size: 14px; +} + +.schedule-table th { + background-color: #4a90e2; + color: white; + padding: 12px 8px; + font-weight: bold; + border: 1px solid #357abd; +} + +.schedule-table td { + padding: 10px 8px; + vertical-align: top; + border: 1px solid #ddd; +} + +.time-slot { + background-color: #e8f0fe; + font-weight: bold; + text-align: center; + width: 80px; +} + +.lesson-cell { + background-color: #fafafa; +} + +.lesson { + margin-bottom: 10px; + padding: 5px; +} + +.lesson:last-child { + margin-bottom: 0; +} + +.lesson-subject { + font-weight: bold; + margin-bottom: 5px; +} + +.lesson-type { + font-size: 12px; + margin-bottom: 4px; +} + +.lesson-teacher { + font-size: 12px; + margin-bottom: 4px; +} + +.lesson-room { + color: #28a745; + font-size: 12px; +} + +.lesson-subgroup { + color: #ff9800; + font-size: 11px; + margin-top: 4px; +} + +.lesson-groups { + color: #6c757d; + font-size: 11px; + margin-top: 4px; +} + +.lesson-separator { + margin: 8px 0; + border: 0; + border-top: 1px dashed #ddd; +} + +.empty-cell { + background-color: #f9f9f9; + color: #999; + text-align: center; +} + +.week-info { + margin-top: 20px; + text-align: center; + font-weight: bold; + color: #333; + padding: 10px; + background-color: #e8f0fe; + border-radius: 8px; +} + +.week-navigation { + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + margin-bottom: 20px; + padding: 10px; + background-color: #e8f0fe; + border-radius: 8px; +} + +.prev-week-btn, +.next-week-btn { + height: 35px; + width: auto; + padding: 0 15px; + font-size: 14px; + border-radius: 8px; + background-color: #4a90e2; + color: white; + border: none; +} + +.prev-week-btn:hover, +.next-week-btn:hover { + background-color: #357abd; +} + +.current-week-info { + font-size: 16px; + font-weight: bold; +} \ No newline at end of file From d3a6e9ba0caf0ec82b7179970dc6648ead6d2699 Mon Sep 17 00:00:00 2001 From: Mihail Date: Mon, 6 Apr 2026 11:35:43 +0400 Subject: [PATCH 4/4] =?UTF-8?q?requirements.txt=20+=20=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D0=BF=D1=82=D0=B8=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | Bin 0 -> 80 bytes server.py | 1 - static/index.html | 2 +- static/style.css | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..caaa1d9d8af953ffff5a645de9104418b2729890 GIT binary patch literal 80 zcmezWFOeaWA)g_Gp#%sE7;J&im_d)h5KNjf@G@{QBr&8iBr=o&
-

Расписание, 641X-100503D

+

Расписание групп, 641X-100503D

diff --git a/static/style.css b/static/style.css index fb7cacba..799258d3 100644 --- a/static/style.css +++ b/static/style.css @@ -200,4 +200,115 @@ button:hover { .current-week-info { font-size: 16px; font-weight: bold; +} + +@media screen and (max-width: 980px) { + .main-container { + padding: 15px; + } + + .schedule-table { + min-width: 700px; + font-size: 13px; + } + + .time-slot { + width: 70px; + font-size: 12px; + } + + .schedule-table th { + padding: 10px 6px; + font-size: 12px; + } + + p { + margin-bottom: 10px; + } + + .current-week-info { + text-align: center; + } +} + +@media screen and (max-width: 768px) { + .main-container { + padding: 15px; + } + + p { + margin-bottom: 10px; + } + + .h1-group-name { + font-size: 20px; + padding-top: 15px; + padding-bottom: 15px; + } + + .teacherSearch, + .groupSearch { + display: flex; + flex-direction: column; + } + + .teacherSearchInput, + .groupSearchInput { + width: 100%; + margin-bottom: 8px; + } + + .teacherSearch button, + .groupSearch button { + width: 100%; + } + + .week-navigation { + gap: 10px; + padding: 8px; + } + + .prev-week-btn, + .next-week-btn { + padding: 0 12px; + font-size: 13px; + height: 32px; + } + + .current-week-info { + font-size: 14px; + } + + .h2-group-name { + font-size: 16px; + } + + .schedule-table { + font-size: 12px; + min-width: 650px; + } + + .schedule-table th { + padding: 8px 4px; + font-size: 11px; + } + + .schedule-table td { + padding: 6px 4px; + } + + .time-slot { + width: 60px; + font-size: 11px; + } + + .lesson-subject { + font-size: 11px; + } + + .lesson-type, + .lesson-teacher, + .lesson-room { + font-size: 10px; + } } \ No newline at end of file