From 8ad3a57b72746c51305b2fee4a3f31a5130248df Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Sat, 2 Sep 2023 19:57:09 +0600 Subject: [PATCH] Schedule parsing --- .../{ => DataModels}/LoginCredentials.cs | 0 Platonus.API/DataModels/LoginResponse.cs | 2 +- Platonus.API/DataModels/Schedule.cs | 123 ++++++++++++++++++ Platonus.API/DataModels/ScheduleRequest.cs | 2 +- Platonus.API/DataModels/ScheduleResponse.cs | 88 +++++++++++++ ...ndHttpHandler.cs => LoggingHttpHandler.cs} | 4 +- Platonus.API/PlatonusClient.cs | 65 ++++----- PlatonusSchedule.CLI/Program.cs | 2 +- 8 files changed, 239 insertions(+), 47 deletions(-) rename Platonus.API/{ => DataModels}/LoginCredentials.cs (100%) create mode 100644 Platonus.API/DataModels/Schedule.cs create mode 100644 Platonus.API/DataModels/ScheduleResponse.cs rename Platonus.API/{LoggindHttpHandler.cs => LoggingHttpHandler.cs} (89%) diff --git a/Platonus.API/LoginCredentials.cs b/Platonus.API/DataModels/LoginCredentials.cs similarity index 100% rename from Platonus.API/LoginCredentials.cs rename to Platonus.API/DataModels/LoginCredentials.cs diff --git a/Platonus.API/DataModels/LoginResponse.cs b/Platonus.API/DataModels/LoginResponse.cs index 443b98d..48b2ad0 100644 --- a/Platonus.API/DataModels/LoginResponse.cs +++ b/Platonus.API/DataModels/LoginResponse.cs @@ -1,6 +1,6 @@ namespace Platonus.API.DataModels; -class LoginResponse +internal class LoginResponse { /// session_id public string? sid { get; set; } diff --git a/Platonus.API/DataModels/Schedule.cs b/Platonus.API/DataModels/Schedule.cs new file mode 100644 index 0000000..d718593 --- /dev/null +++ b/Platonus.API/DataModels/Schedule.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Platonus.API.DataModels; + +public class Schedule +{ + public int SelectedYear { get; } + public int SelectedTermNumber { get; } + public int SelectedWeekNumber { get; } + public int CurrentWeekNumber { get; } + public List TermWeekNumbers { get; } + + internal Schedule(ScheduleResponse scheduleData) + { + SelectedYear = scheduleData.selectedStudyYear; + SelectedTermNumber = scheduleData.selectedTerm; + SelectedWeekNumber = scheduleData.selectedWeek; + CurrentWeekNumber = scheduleData.activeWeekNumber; + TermWeekNumbers = scheduleData.weekList; + + Days = new Day[scheduleData.timetable.days.Count]; + for (int di = 0; di < Days.Length; di++) + { + var dayData = scheduleData.timetable.days[(di + 1).ToString()]; + Days[di] = new Day(di, dayData.lessons.Count); + int li = 0; + foreach (var lessonWrapper in dayData.lessons) + { + var lessonHourData = scheduleData.lessonHours.Find(lh => lh.number.ToString() == lessonWrapper.Key); + if (lessonHourData is null) + throw new NullReferenceException($"lesson hour with number {lessonWrapper.Key} not found"); + Days[di].LessonHours[li].Time = new TimeRange(lessonHourData.start, lessonHourData.finish); + + switch (lessonWrapper.Value.lessons.Count) + { + case 0: + Days[di].LessonHours[li].Lesson = null; + break; + case 1: + var lessonData = lessonWrapper.Value.lessons[0]; + Days[di].LessonHours[li].Lesson = new Day.Lesson(lessonData); + break; + default: + throw new Exception($"lessonWrapper has data for more than one lessons ({lessonWrapper.Value.lessons.Count})"); + } + + li++; + } + } + } + + public readonly struct TimeRange + { + public readonly TimeSpan Start; + public readonly TimeSpan Finish; + + public TimeRange(TimeSpan from, TimeSpan to) + { + Start = from; + Finish = to; + } + + /// format: hh:mm:ss + public TimeRange(string startStr, string finishStr) + { + Start = TimeSpan.ParseExact(startStr, @"hh\:mm\:ss", CultureInfo.InvariantCulture); + Finish = TimeSpan.ParseExact(finishStr, @"hh\:mm\:ss", CultureInfo.InvariantCulture); + } + } + + /// days of week (monday=0, sunday=6) + public Day[] Days { get; } + + public class Day + { + public (TimeRange Time, Lesson? Lesson)[] LessonHours { get; } + internal DayOfWeek DayOfWeek { get; } + internal Day(int dayIndex, int dayLessonsCount) + { + LessonHours = new (TimeRange, Lesson?)[dayLessonsCount]; + DayOfWeek = dayIndex switch + { + 0 => DayOfWeek.Monday, + 1 => DayOfWeek.Tuesday, + 2 => DayOfWeek.Wednesday, + 3 => DayOfWeek.Thursday, + 4 => DayOfWeek.Friday, + 5 => DayOfWeek.Saturday, + 6 => DayOfWeek.Sunday, + _ => throw new Exception($"there is no day of week with index {dayIndex}") + }; + } + + public class Lesson + { + // example: Жанабеков Ж.О + public string TutorName { get; } + /// example: 420 б + public string Auditory { get; } + /// example: Байзак центр + public string Building { get; } + + /// example: Lecture + public string LessonTypeName { get; } + /// example: Программирование на языке С# + public string SubjectName { get; } + /// may not work, idk + public bool OnlineClass { get; } + + internal Lesson(ScheduleResponse.Timetable.Day.LessonWrapper.Lesson lessonData) + { + TutorName = lessonData.tutorName; + Auditory = lessonData.auditory; + Building = lessonData.building; + LessonTypeName = lessonData.groupTypeFullName; + SubjectName = lessonData.subjectName; + OnlineClass = lessonData.onlineClass; + } + } + } +} \ No newline at end of file diff --git a/Platonus.API/DataModels/ScheduleRequest.cs b/Platonus.API/DataModels/ScheduleRequest.cs index 3e95fca..5c8b776 100644 --- a/Platonus.API/DataModels/ScheduleRequest.cs +++ b/Platonus.API/DataModels/ScheduleRequest.cs @@ -1,6 +1,6 @@ namespace Platonus.API.DataModels; -public class ScheduleRequest +internal class ScheduleRequest { public int statusID { get; set; } = 1; public int studentTypeID { get; set; } = 1; diff --git a/Platonus.API/DataModels/ScheduleResponse.cs b/Platonus.API/DataModels/ScheduleResponse.cs new file mode 100644 index 0000000..db9e812 --- /dev/null +++ b/Platonus.API/DataModels/ScheduleResponse.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Platonus.API.DataModels; + +#nullable disable +internal class ScheduleResponse +{ + /// example: 2023 + [JsonRequired] public int selectedStudyYear { get; set; } + + /// 1 or 2 + [JsonRequired] public int selectedTerm { get; set; } + [JsonRequired] public int defaultSelectedTerm { get; set; } + + /// selected week number + [JsonRequired] public int selectedWeek { get; set; } + [JsonRequired] public int defaultSelectedWeek { get; set; } + /// current week number + [JsonRequired] public int activeWeekNumber { get; set; } + /// all week numbers + [JsonRequired] public List weekList { get; set; } + + /// all lesson hours in the week + [JsonRequired] public List lessonHours { get; set; } + public class LessonHour + { + /// number of the lesson in Timetable.Day.lessons + [JsonRequired] public int number { get; set; } + /// example: 08:00:00 + [JsonRequired] public string start { get; set; } + /// example: 08:50:00 + [JsonRequired] public string finish { get; set; } + /// duration in minutes, usually 50 + [JsonRequired] public int duration { get; set; } + /// idk what is it + [JsonRequired] public int shiftNumber { get; set; } + /// idk what is it + [JsonRequired] public int displayNumber { get; set; } + } + + /// all days with lesson hours with or without lessons + [JsonRequired] public Timetable timetable { get; set; } + public class Timetable + { + /// day number string, Day class + [JsonRequired] public Dictionary days { get; set; } + public class Day + { + /// Lesson.number string, LessonWrapper class + [JsonRequired] public Dictionary lessons { get; set; } + public class LessonWrapper + { + /// one or zero lessons at time + [JsonRequired] public List lessons { get; set; } + public class Lesson + { + /// number of the lesson, corresponds a number in a LessonHour + [JsonRequired] public int number { get; set; } + /// example: Жанабеков Ж.О + [JsonRequired] public string tutorName { get; set; } + /// same as tutorName + [JsonRequired] public string tutorShortName { get; set; } + /// example: 420 б + [JsonRequired] public string auditory { get; set; } + /// example: Байзак центр + [JsonRequired] public string building { get; set; } + /// some platonus internal id + [JsonRequired] public int lessonID { get; set; } + /// some platonus internal group id + [JsonRequired] public int studyGroupID { get; set; } + /// example: SFT6541-6-L + [JsonRequired] public string studygroupShortName { get; set; } + /// example: Программирование на языке С#,'Л' + [JsonRequired] public string studyGroupName { get; set; } + /// example: L + [JsonRequired] public string groupTypeShortName { get; set; } + /// example: Lecture + [JsonRequired] public string groupTypeFullName { get; set; } + /// example: Программирование на языке С# + [JsonRequired] public string subjectName { get; set; } + /// may not work, idk + [JsonRequired] public bool onlineClass { get; set; } + } + } + } + } +} diff --git a/Platonus.API/LoggindHttpHandler.cs b/Platonus.API/LoggingHttpHandler.cs similarity index 89% rename from Platonus.API/LoggindHttpHandler.cs rename to Platonus.API/LoggingHttpHandler.cs index 7efc8ca..fc27141 100644 --- a/Platonus.API/LoggindHttpHandler.cs +++ b/Platonus.API/LoggingHttpHandler.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; namespace Platonus.API; -public class LoggindHttpHandler : DelegatingHandler +public class LoggingHttpHandler : DelegatingHandler { - public LoggindHttpHandler(HttpMessageHandler innerHandler) : base(innerHandler) + public LoggingHttpHandler(HttpMessageHandler innerHandler) : base(innerHandler) { } void LogHttpRequest(HttpRequestMessage req) diff --git a/Platonus.API/PlatonusClient.cs b/Platonus.API/PlatonusClient.cs index 41b7255..3012e3c 100644 --- a/Platonus.API/PlatonusClient.cs +++ b/Platonus.API/PlatonusClient.cs @@ -30,7 +30,7 @@ public class PlatonusClient { CookieContainer = _cookies }; - _http = new HttpClient(new LoggindHttpHandler(httpClientHandler)) + _http = new HttpClient(new LoggingHttpHandler(httpClientHandler)) { BaseAddress = new Uri(base_url) }; @@ -50,54 +50,35 @@ public class PlatonusClient _language = userCredentials.Language; reqContent.Headers.Add("language", _language.Number.ToString()); - - try - { - // send POST request - var res = await _http.PostAsync("rest/api/login", reqContent); - // parse json - if (res.Content.Headers.ContentLength < 1) - throw new Exception("responce doesn't have any content"); - _loginResponse = await res.Content.ReadFromJsonAsync() - ?? throw new Exception("invalid login request rezult"); - if(_loginResponse.login_status != "success") - throw new Exception("invalid login request rezult"); - - // set session id and user token headers in HttpClient - _http.DefaultRequestHeaders.Add("sid", _loginResponse.sid); - _http.DefaultRequestHeaders.Add("token", _loginResponse.auth_token); - } - catch (HttpRequestException re) - { - HandleRequestException(re); - } + // send POST request + var res = await _http.PostAsync("rest/api/login", reqContent); + // parse json + if (res.Content.Headers.ContentLength < 1) + throw new Exception("responce doesn't have any content"); + _loginResponse = await res.Content.ReadFromJsonAsync() + ?? throw new Exception("invalid login request rezult"); + if(_loginResponse.login_status != "success") + throw new Exception("invalid login request rezult"); + + // set session id and user token headers in HttpClient + _http.DefaultRequestHeaders.Add("sid", _loginResponse.sid); + _http.DefaultRequestHeaders.Add("token", _loginResponse.auth_token); } - public async Task GetScheduleAsync() + public async Task GetScheduleAsync() { if (_language is null) throw new Exception("language is null, you need to login"); if (_loginResponse is null) throw new Exception("session id is null, you need to login"); - try - { - var req = JsonContent.Create(new ScheduleRequest()); - var res = await _http.PostAsync( - $"rest/schedule/userSchedule/student/initial/{_language.LiteralCode}", - req); - // var rezult = await res.Content.ReadFromJsonAsync(); - } - catch (HttpRequestException re) - { - HandleRequestException(re); - } - - } - - private void HandleRequestException(HttpRequestException re) - { - // TODO http exception handling - throw re; + var req = JsonContent.Create(new ScheduleRequest()); + var res = await _http.PostAsync( + $"rest/schedule/userSchedule/student/initial/{_language.LiteralCode}", + req); + ScheduleResponse scheduleData = await res.Content.ReadFromJsonAsync() + ?? throw new NullReferenceException("ScheduleResponse is null"); + var schedule = new Schedule(scheduleData); + return schedule; } } \ No newline at end of file diff --git a/PlatonusSchedule.CLI/Program.cs b/PlatonusSchedule.CLI/Program.cs index 2ae86f5..3ea323e 100644 --- a/PlatonusSchedule.CLI/Program.cs +++ b/PlatonusSchedule.CLI/Program.cs @@ -7,7 +7,7 @@ var loginCredentials = new LoginCredentials( ReadString("password"), PlatonusLanguage.English); await p.LoginAsync(loginCredentials); -await p.GetScheduleAsync(); +var schedule = await p.GetScheduleAsync(); return; string ReadString(string question)