diff --git a/backend/yet_another_calendar/web/api/bulk/integration.py b/backend/yet_another_calendar/web/api/bulk/integration.py index 67d2865..d20c760 100644 --- a/backend/yet_another_calendar/web/api/bulk/integration.py +++ b/backend/yet_another_calendar/web/api/bulk/integration.py @@ -4,7 +4,6 @@ from typing import Any, Iterable, Optional import icalendar -import pytz from fastapi import HTTPException from fastapi_cache import default_key_builder, FastAPICache from fastapi_cache.decorator import cache @@ -82,9 +81,9 @@ async def refresh_events( timezone: str, ) -> schema.RefreshedCalendarResponse: """Clear events cache.""" - cached_json = await get_cached_calendar(body, lms_user, jwt_token, calendar_id, cookies, timezone) + cached_json = await get_cached_calendar(body, lms_user, jwt_token, calendar_id, cookies) cached_calendar = schema.CalendarResponse.model_validate(cached_json) - calendar = await get_calendar(body, lms_user, jwt_token, calendar_id, cookies, timezone) + calendar = await get_calendar(body, lms_user, jwt_token, calendar_id, cookies) changed = cached_calendar.get_hash() != calendar.get_hash() try: cache_key = default_key_builder(get_cached_calendar, args=(body, jwt_token, calendar_id, cookies), kwargs={}) @@ -99,7 +98,7 @@ async def refresh_events( raise HTTPException(detail="Can't refresh redis", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) from None return schema.RefreshedCalendarResponse( **{**calendar.model_dump(by_alias=True), "changed": changed}, - ) + ).change_timezone(timezone) async def get_calendar( @@ -108,12 +107,7 @@ async def get_calendar( jwt_token: str, calendar_id: int, cookies: netology_schema.NetologyCookies, - timezone: str, ) -> schema.CalendarResponse: - try: - tz = pytz.timezone(timezone) - except pytz.exceptions.UnknownTimeZoneError: - raise HTTPException(detail="Wrong timezone", status_code=status.HTTP_400_BAD_REQUEST) from None lms_response = None async with asyncio.TaskGroup() as tg: netology_response = tg.create_task(netology_views.get_calendar(body, calendar_id, cookies)) @@ -126,7 +120,7 @@ async def get_calendar( "modeus_events": modeus_response.result(), "lms_events": lms_events, }}, - ).change_timezone(tz) + ) @cache(expire=settings.redis_events_time_live) @@ -136,6 +130,5 @@ async def get_cached_calendar( jwt_token: str, calendar_id: int, cookies: netology_schema.NetologyCookies, - timezone: str, ) -> schema.CalendarResponse: - return await get_calendar(body, lms_user, jwt_token, calendar_id, cookies, timezone) + return await get_calendar(body, lms_user, jwt_token, calendar_id, cookies) diff --git a/backend/yet_another_calendar/web/api/bulk/schema.py b/backend/yet_another_calendar/web/api/bulk/schema.py index cefcb85..192af5d 100644 --- a/backend/yet_another_calendar/web/api/bulk/schema.py +++ b/backend/yet_another_calendar/web/api/bulk/schema.py @@ -2,7 +2,10 @@ import hashlib from typing import Self +import pytz from pydantic import BaseModel, Field +from starlette import status +from starlette.exceptions import HTTPException from ..modeus import schema as modeus_schema from ..lms import schema as lms_schema @@ -20,7 +23,11 @@ class BulkResponse(BaseModel): netology: netology_schema.SerializedEvents utmn: UtmnResponse - def change_timezone(self, timezone: datetime.tzinfo) -> Self: + def change_timezone(self, timezone_name: str) -> Self: + try: + timezone = pytz.timezone(timezone_name) + except pytz.exceptions.UnknownTimeZoneError: + raise HTTPException(detail="Wrong timezone", status_code=status.HTTP_400_BAD_REQUEST) from None for homework in self.netology.homework: if homework.deadline: homework.deadline = homework.deadline.astimezone(timezone) diff --git a/backend/yet_another_calendar/web/api/bulk/views.py b/backend/yet_another_calendar/web/api/bulk/views.py index a2d7295..bcf8e68 100644 --- a/backend/yet_another_calendar/web/api/bulk/views.py +++ b/backend/yet_another_calendar/web/api/bulk/views.py @@ -29,8 +29,11 @@ async def get_calendar( Get events from Netology and Modeus, cached. """ - cached_calendar = await integration.get_cached_calendar(body, lms_user, jwt_token, calendar_id, cookies, time_zone) - return schema.CalendarResponse.model_validate(cached_calendar) + cached_calendar = await integration.get_cached_calendar(body, lms_user, jwt_token, calendar_id, cookies) + if isinstance(cached_calendar, schema.CalendarResponse): + return cached_calendar.change_timezone(time_zone) + # else cached + return schema.CalendarResponse.model_validate(cached_calendar).change_timezone(time_zone) @router.post("/refresh_events/") @@ -61,5 +64,6 @@ async def export_ics( """ Export into .ics format """ - calendar = await integration.get_calendar(body, lms_user, jwt_token, calendar_id, cookies, time_zone) - return StreamingResponse(integration.export_to_ics(calendar)) + calendar = await integration.get_calendar(body, lms_user, jwt_token, calendar_id, cookies) + calendar_with_timezone = calendar.change_timezone(time_zone) + return StreamingResponse(integration.export_to_ics(calendar_with_timezone)) diff --git a/docker-compose.yaml b/docker-compose.yaml index 6819d88..da98af1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -41,6 +41,8 @@ services: container_name: calendar-frontend env_file: - frontend/.env + environment: + REACT_APP_BACKEND_URL: http://127.0.0.1:8000 restart: always volumes: - ./frontend/src:/app/src diff --git a/frontend/src/components/Calendar/LessonTimes.jsx b/frontend/src/components/Calendar/LessonTimes.jsx index 8ff10d8..0ccd2d1 100644 --- a/frontend/src/components/Calendar/LessonTimes.jsx +++ b/frontend/src/components/Calendar/LessonTimes.jsx @@ -1,6 +1,13 @@ import React, {useEffect, useState} from 'react'; import camera from "../../img/camera.png"; +export function formatDateToAMPM(date) { + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + + return `${hours}:${minutes}`; +} + const LessonTimes = ({ events, selectedEvent, setSelectedEvent }) => { const [weekDays, setWeekDays] = useState(Array.from({length: 7}, () => [])); @@ -68,14 +75,8 @@ const LessonTimes = ({ events, selectedEvent, setSelectedEvent }) => { const lesson = lessons.find(lesson => { const lessonStartTime = new Date(lesson.start || lesson.starts_at); const lessonEndTime = new Date(lesson.end || lesson.ends_at); - const lessonStartFormatted = lessonStartTime.toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit' - }); - const lessonEndFormatted = lessonEndTime.toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit' - }); + const lessonStartFormatted = formatDateToAMPM(lessonStartTime); + const lessonEndFormatted = formatDateToAMPM(lessonEndTime); return ( lessonStartFormatted === timeSlot.split(' - ')[0] || diff --git a/frontend/src/pages/CalendarRoute.jsx b/frontend/src/pages/CalendarRoute.jsx index 08e24bd..3a5b0ae 100644 --- a/frontend/src/pages/CalendarRoute.jsx +++ b/frontend/src/pages/CalendarRoute.jsx @@ -4,6 +4,7 @@ import { bulkEvents, getTokenFromLocalStorage, getPersonIdLocalStorage, + getCalendarIdLocalStorage, } from '../services/api'; import Loader from "../elements/Loader"; import '../style/header.scss'; @@ -34,26 +35,32 @@ const CalendarRoute = () => { setError(null); try { - const courseData = await getNetologyCourse(getTokenFromLocalStorage()); - const calendarId = courseData?.id; - localStorage.setItem('calendarId', calendarId); + var calendarId + calendarId = getCalendarIdLocalStorage(); + if (!calendarId){ + const courseData = await getNetologyCourse(getTokenFromLocalStorage()); + calendarId = courseData?.id; + localStorage.setItem('calendarId', calendarId); + } - if (calendarId) { - const eventsResponse = await bulkEvents({ - calendarId, - timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone, - attendeePersonId: getPersonIdLocalStorage(), - timeMin: date.start, - timeMax: date.end, - sessionToken: getTokenFromLocalStorage(), - }); + if (!calendarId) { + console.error('Ошибка при получении calendar id:', calendarId); + } + const eventsResponse = await bulkEvents({ + calendarId, + timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone, + attendeePersonId: getPersonIdLocalStorage(), + timeMin: date.start, + timeMax: date.end, + sessionToken: getTokenFromLocalStorage(), + }); - if (eventsResponse?.data) { - setEvents(eventsResponse.data); - } else { - throw new Error('Не удалось получить события'); - } + if (eventsResponse?.data) { + setEvents(eventsResponse.data); + } else { + throw new Error('Не удалось получить события'); } + } catch (error) { console.error('Ошибка при получении данных с сервера:', error); setError("Ошибка при получении данных с сервера. Перезагрузите страницу!");