1
0
mirror of https://github.com/XFox111/GUTSchedule.git synced 2026-04-22 06:58:01 +03:00

Cabinet functionality updated (#25)

Cabinet authorization has been turned on
Occupation check in functionality
This commit is contained in:
Michael Gordeev
2020-11-12 15:21:55 +03:00
committed by GitHub
parent 018981002b
commit 6c475dd621
16 changed files with 1457 additions and 1173 deletions
@@ -26,10 +26,21 @@ namespace GUTSchedule.Droid.Activities
public static int SelectedCalendarIndex { get; set; }
public static int Reminder { get; set; }
private List<(string, string)> _availableOccupations;
private List<(string, string)> AvailableOccupations
{
get => _availableOccupations;
set
{
_availableOccupations = value;
applyForOccupation.Visibility = value.Count > 0 ? ViewStates.Visible : ViewStates.Gone;
}
}
DateTime startDate = DateTime.Today;
DateTime endDate = DateTime.Today.AddDays(7);
Button start, end, export;
Button start, end, export, applyForOccupation, validateCredential;
Button forDay, forWeek, forMonth, forSemester;
Spinner faculty, course, group, reminder, calendar;
CheckBox groupTitle, authorize;
@@ -81,10 +92,19 @@ namespace GUTSchedule.Droid.Activities
start.Text = startDate.ToShortDateString();
groupTitle.Checked = prefs.GetBoolean("AddGroupToHeader", false);
//authorize.Checked = prefs.GetBoolean("Authorize", true);
authorize.Checked = prefs.GetBoolean("Authorize", true);
email.Text = prefs.GetString("email", "");
password.Text = prefs.GetString("password", "");
try
{
AvailableOccupations = await Parser.CheckAvailableOccupations(email.Text, password.Text);
}
catch
{
AvailableOccupations = new List<(string, string)>();
}
}
private void Export_Click(object sender, EventArgs e)
@@ -119,7 +139,7 @@ namespace GUTSchedule.Droid.Activities
// According to this SO thread: https://stackoverflow.com/questions/1925486/android-storing-username-and-password
// I consider Preferences as safe enough method for storing credentials
// А во-вторых, даже такой казалось бы небезопасный метод хранения учетных данных в сто раз надежнее того дерьма,
// что творится на серверах Бонча (я не шучу, там все ОЧЕНЬ плохо)
// что творится на серверах Бонча (я не шучу, там пиздец)
// Ну и в-третьих: Андроид - это пиздец и настоящий ад разработчика. И если бы была моя воля, я бы под него никогда не писал #FuckAndroid
// З.Ы. Помнишь про второй пункт? Так вот, если ты используешь такой же пароль как в ЛК где-то еще, настоятельно рекомендую его поменять
PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("email", email.Text).Apply();
@@ -192,6 +212,8 @@ namespace GUTSchedule.Droid.Activities
forWeek = FindViewById<Button>(Resource.Id.forWeek);
forMonth = FindViewById<Button>(Resource.Id.forMonth);
forSemester = FindViewById<Button>(Resource.Id.forSemester);
applyForOccupation = FindViewById<Button>(Resource.Id.applyForOccupation);
validateCredential = FindViewById<Button>(Resource.Id.validateCredential);
faculty = FindViewById<Spinner>(Resource.Id.faculty);
course = FindViewById<Spinner>(Resource.Id.course);
@@ -261,6 +283,40 @@ namespace GUTSchedule.Droid.Activities
endDate = new DateTime(DateTime.Today.Year, 8, 31);
end.Text = endDate.ToShortDateString();
};
applyForOccupation.Click += async (s, e) =>
{
try
{
applyForOccupation.Visibility = ViewStates.Gone;
var occupations = await Parser.CheckAvailableOccupations(email.Text, password.Text);
await Parser.ApplyForOccupations(email.Text, password.Text, occupations);
Toast.MakeText(ApplicationContext, Resources.GetText(Resource.String.attendSuccess), ToastLength.Short).Show();
}
catch (Exception ex)
{
Toast.MakeText(ApplicationContext, $"{Resources.GetText(Resource.String.attendFailed)}\n{ex.Message}", ToastLength.Short).Show();
}
AvailableOccupations = await Parser.CheckAvailableOccupations(email.Text, password.Text);
};
validateCredential.Click += async (s, e) =>
{
try
{
validateCredential.Enabled = false;
await Parser.VaildateAuthorization(email.Text, password.Text);
PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("email", email.Text).Apply();
PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("password", password.Text).Apply();
AvailableOccupations = await Parser.CheckAvailableOccupations(email.Text, password.Text);
Toast.MakeText(ApplicationContext, Resources.GetText(Resource.String.validationSuccess), ToastLength.Short).Show();
}
catch (Exception ex)
{
Toast.MakeText(ApplicationContext, $"{Resources.GetText(Resource.String.validationFailed)}\n{ex.Message}", ToastLength.Short).Show();
}
validateCredential.Enabled = true;
};
start.Click += Start_Click;
end.Click += End_Click;
@@ -24,7 +24,7 @@
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<AndroidUseAapt2>true</AndroidUseAapt2>
<LangVersion>8.0</LangVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
@@ -138,6 +138,9 @@
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat">
<Version>28.0.0.3</Version>
</PackageReference>
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
File diff suppressed because it is too large Load Diff
@@ -11,6 +11,15 @@
android:orientation="vertical"
android:padding="10dp">
<Button
android:id="@+id/applyForOccupation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/applyForOccupation"
android:background="@color/colorPrimary"
android:textColor="@android:color/white"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -22,15 +31,15 @@
android:id="@+id/authorization"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:checked="false"
android:checked="true"
android:text="@string/authorizeCheckbox"/>
<LinearLayout
android:id="@+id/studentParams"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="match_parent"
@@ -67,8 +76,7 @@
android:id="@+id/professorParams"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
@@ -91,6 +99,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textWebPassword"/>
<Button
android:id="@+id/validateCredential"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/validateCredential"/>
</LinearLayout>
<TextView
@@ -1,102 +1,110 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<string name="appName">ГУТ.Расписание</string>
<string name="appName">ГУТ.Расписание</string>
<!-- StartActivity -->
<string name="initializationStatus">Инициализация</string>
<string name="permissionsCheckStatus">Проверка наличия разрешений</string>
<string name="calendarLoadingStatus">Загрузка списка календарей</string>
<string name="facultiesLoadingStatus">Загрузка списка факультативов</string>
<string name="offsetDatesLoadingStatus">Загрузка дат смещения</string>
<!-- StartActivity -->
<string name="initializationStatus">Инициализация</string>
<string name="permissionsCheckStatus">Проверка наличия разрешений</string>
<string name="calendarLoadingStatus">Загрузка списка календарей</string>
<string name="facultiesLoadingStatus">Загрузка списка факультативов</string>
<string name="offsetDatesLoadingStatus">Загрузка дат смещения</string>
<string name="calendarAccessTitle">Доступ к календарю</string>
<string name="calendarAccessRationale">Разрешите приложению получать доступ к календарю. Без этого разрешения приложение не сможет добавлять расписание в ваш календарь</string>
<string name="calendarAccessTitle">Доступ к календарю</string>
<string name="calendarAccessRationale">Разрешите приложению получать доступ к календарю. Без этого разрешения приложение не сможет добавлять расписание в ваш календарь</string>
<string name="createCalendarTitle">Создайте новый календарь</string>
<string name="createCalendarMessage">На вашем устройстве нет календарей пригодных для записи расписания</string>
<string name="createCalendarTitle">Создайте новый календарь</string>
<string name="createCalendarMessage">На вашем устройстве нет календарей пригодных для записи расписания</string>
<string name="connectionFailMessage">Невозможно загрузить расписание. Проверьте интернет-соединение или попробуйте позже</string>
<string name="repeat">Повторить</string>
<string name="quit">Выйти</string>
<string name="close">Закрыть</string>
<string name="connectionFailMessage">Невозможно загрузить расписание. Проверьте интернет-соединение или попробуйте позже</string>
<string name="repeat">Повторить</string>
<string name="quit">Выйти</string>
<string name="close">Закрыть</string>
<!-- MainActivity -->
<string name="scheduleParametersTitle">Параметры расписания</string>
<string name="authorizeCheckbox">Авторизоваться через Личный кабинет</string>
<string name="facultySpinner">Факультет</string>
<string name="courseSpinner">Курс</string>
<string name="groupSpinner">Группа</string>
<!-- MainActivity -->
<string name="scheduleParametersTitle">Параметры расписания</string>
<string name="authorizeCheckbox">Авторизоваться через Личный кабинет</string>
<string name="facultySpinner">Факультет</string>
<string name="courseSpinner">Курс</string>
<string name="groupSpinner">Группа</string>
<string name="passwordField">Пароль</string>
<string name="passwordField">Пароль</string>
<string name="applyForOccupation">Начать занятие</string>
<string name="validateCredential">Проверить данные</string>
<string name="exportParametersTitle">Параметры экспорта</string>
<string name="dateRange">Диапазон экспорта</string>
<string name="attendFailed">Что-то пошло не так. Попроуйте снова или выполните действие через личный кабинет</string>
<string name="attendSuccess">Регистрация на занятие выполнена</string>
<string name="forDayButton">На день</string>
<string name="forWeekButton">На неделю</string>
<string name="forMonthButton">На месяц</string>
<string name="forSemesterButton">На семестр</string>
<string name="validationSuccess">Авторизация выполнена успешно</string>
<string name="validationFailed">Ошибка авторизации. Проверьте данные.</string>
<string name="reminderSpinner">Напоминать за</string>
<string name="reminderNote">(i) Внимание, при экспорте в облачный Google-календарь, Google автоматически ставит уведомление за пол часа, если его не поставил пользователь</string>
<string name="noReminderOption">Нет</string>
<string name="inTimeReminderOption">Во время начала</string>
<string name="fiveMinuteReminderOption">За 5 минут</string>
<string name="tenMinuteReminderOption">За 10 минут</string>
<string name="exportParametersTitle">Параметры экспорта</string>
<string name="dateRange">Диапазон экспорта</string>
<string name="addGroupToTitleCheckbox">Добавить номер группы в заголовок</string>
<string name="titleNote">(i) Не касается преподавательского расписания</string>
<string name="forDayButton">На день</string>
<string name="forWeekButton">На неделю</string>
<string name="forMonthButton">На месяц</string>
<string name="forSemesterButton">На семестр</string>
<string name="destinationCalendarSpinner">Конечный календарь</string>
<string name="reminderSpinner">Напоминать за</string>
<string name="reminderNote">(i) Внимание, при экспорте в облачный Google-календарь, Google автоматически ставит уведомление за пол часа, если его не поставил пользователь</string>
<string name="noReminderOption">Нет</string>
<string name="inTimeReminderOption">Во время начала</string>
<string name="fiveMinuteReminderOption">За 5 минут</string>
<string name="tenMinuteReminderOption">За 10 минут</string>
<string name="addScheduleButton">Добавить расписание</string>
<string name="addGroupToTitleCheckbox">Добавить номер группы в заголовок</string>
<string name="titleNote">(i) Не касается преподавательского расписания</string>
<string name="copyrights">©2020 Михаил Гордеев, ИСиТ, ИСТ-942</string>
<string name="destinationCalendarSpinner">Конечный календарь</string>
<string name="clearCalendarOption">Очистить расписание</string>
<string name="reportErrorOption">Сообщить об ошибке</string>
<string name="addScheduleButton">Добавить расписание</string>
<string name="invalidDateRangeError">Ошибка: Неправильный диапазон дат</string>
<string name="authorizationState">Авторизация...</string>
<string name="invalidAuthorizationError">Ошибка: Введите корректные учетные данные</string>
<string name="authorizationError">Ошибка авторизации</string>
<string name="invalidCredentialError">Ошибка авторизации: Неверный e-mail и/или пароль</string>
<string name="groupSelectionError">Ошибка: Не выбрана группа</string>
<string name="copyrights">©2020 Михаил Гордеев, ИСиТ, ИСТ-942</string>
<string name="clearScheduleTitle">Очистка расписания</string>
<string name="clearScheduleMessage">Это действие удалит экспортированное расписание из всех доступных календарей. \nДанное действие затронет только расписание, экспортированное этим приложением \n\'Все\' - удалит все события расписания, включая прошедшие \n\'Только новые\' - удалит будущие события расписания
</string>
<string name="clearAllOption">Все</string>
<string name="clearUpcomingOption">Только новые</string>
<string name="cancelOption">Отмена</string>
<string name="clearingStatus">Очистка...</string>
<string name="clearCalendarOption">Очистить расписание</string>
<string name="reportErrorOption">Сообщить об ошибке</string>
<!-- ExportActivity -->
<string name="loadingStatus">Загрузка</string>
<string name="potatoLoadingStatus">Загрузка расписания с картофельных серверов Бонча</string>
<string name="scheduleLoadingStatus">Загрузка расписания</string>
<string name="calendarExportStatus">Экспортирование в календарь</string>
<string name="doneStatus">Готово</string>
<string name="invalidDateRangeError">Ошибка: Неправильный диапазон дат</string>
<string name="authorizationState">Авторизация...</string>
<string name="invalidAuthorizationError">Ошибка: Введите корректные учетные данные</string>
<string name="authorizationError">Ошибка авторизации</string>
<string name="invalidCredentialError">Ошибка авторизации: Неверный e-mail и/или пароль</string>
<string name="groupSelectionError">Ошибка: Не выбрана группа</string>
<!-- AboutActivity -->
<string name="aboutTitle">О приложении</string>
<string name="appDescription">Приложение для экспорта перподавательского и учебного расписаний Санкт-Петербургского Государственного Университета Телекоммуникаций им. проф. М.А. Бонч-Бруевича</string>
<string name="developedBy">Разработано Михаилом Гордеевым, ИСТ-942, ИСиТ в Научно-образовательном центре \"Технологии информационных образовательных систем\"</string>
<string name="contributorsTitle">Свой вклад в разработку внесли</string>
<string name="clearScheduleTitle">Очистка расписания</string>
<string name="clearScheduleMessage">Это действие удалит экспортированное расписание из всех доступных календарей. \nДанное действие затронет только расписание, экспортированное этим приложением \n\'Все\' - удалит все события расписания, включая прошедшие \n\'Только новые\' - удалит будущие события расписания
</string>
<string name="clearAllOption">Все</string>
<string name="clearUpcomingOption">Только новые</string>
<string name="cancelOption">Отмена</string>
<string name="clearingStatus">Очистка...</string>
<string name="specialThanksTitle">Особые благодарности</string>
<string name="specialThanksPeople">Виталий Мошков, Анастасия Годунова</string>
<!-- ExportActivity -->
<string name="loadingStatus">Загрузка</string>
<string name="potatoLoadingStatus">Загрузка расписания с картофельных серверов Бонча</string>
<string name="scheduleLoadingStatus">Загрузка расписания</string>
<string name="calendarExportStatus">Экспортирование в календарь</string>
<string name="doneStatus">Готово</string>
<string name="contactsTitle">Контакты</string>
<string name="websiteContact">Веб-сайт</string>
<string name="twitterContact">Твиттер</string>
<string name="vkontakteContact">ВКонтакте</string>
<!-- AboutActivity -->
<string name="aboutTitle">О приложении</string>
<string name="appDescription">Приложение для экспорта перподавательского и учебного расписаний Санкт-Петербургского Государственного Университета Телекоммуникаций им. проф. М.А. Бонч-Бруевича</string>
<string name="developedBy">Разработано Михаилом Гордеевым, ИСТ-942, ИСиТ в Научно-образовательном центре \"Технологии информационных образовательных систем\"</string>
<string name="contributorsTitle">Свой вклад в разработку внесли</string>
<string name="linksTitle">Полезные ссылки</string>
<string name="privacyPolicyLink">Политика конфиденциальности</string>
<string name="repositoryLink">Репозиторий GitHub</string>
<string name="notsLink">НОЦ \"ТИОС\"</string>
<string name="sutLink">СПбГУТ</string>
<string name="specialThanksTitle">Особые благодарности</string>
<string name="specialThanksPeople">Виталий Мошков, Анастасия Годунова</string>
<string name="feedbackButton">Оставить отзыв</string>
<string name="contactsTitle">Контакты</string>
<string name="websiteContact">Веб-сайт</string>
<string name="twitterContact">Твиттер</string>
<string name="vkontakteContact">ВКонтакте</string>
<string name="linksTitle">Полезные ссылки</string>
<string name="privacyPolicyLink">Политика конфиденциальности</string>
<string name="repositoryLink">Репозиторий GitHub</string>
<string name="notsLink">НОЦ \"ТИОС\"</string>
<string name="sutLink">СПбГУТ</string>
<string name="feedbackButton">Оставить отзыв</string>
</resources>
@@ -1,100 +1,108 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<string name="appName">GUT.Schedule</string>
<string name="appName">GUT.Schedule</string>
<string name="initializationStatus">Initialization</string>
<string name="permissionsCheckStatus">Checking permissions</string>
<string name="calendarLoadingStatus">Loading calendars list</string>
<string name="facultiesLoadingStatus">Loading faculties list</string>
<string name="offsetDatesLoadingStatus">Loading offset dates</string>
<string name="initializationStatus">Initialization</string>
<string name="permissionsCheckStatus">Checking permissions</string>
<string name="calendarLoadingStatus">Loading calendars list</string>
<string name="facultiesLoadingStatus">Loading faculties list</string>
<string name="offsetDatesLoadingStatus">Loading offset dates</string>
<string name="calendarAccessTitle">Calendar access</string>
<string name="calendarAccessRationale">Grant permission to the app to access calendar. Without it the app will not be able to add schedule to your calendar</string>
<string name="calendarAccessTitle">Calendar access</string>
<string name="calendarAccessRationale">Grant permission to the app to access calendar. Without it the app will not be able to add schedule to your calendar</string>
<string name="createCalendarTitle">Create new calendar</string>
<string name="createCalendarMessage">There\'s no calendars on your device we can write schedule to</string>
<string name="createCalendarTitle">Create new calendar</string>
<string name="createCalendarMessage">There\'s no calendars on your device we can write schedule to</string>
<string name="connectionFailMessage">Unable to load schedule. Check your internet connection or try again later</string>
<string name="repeat">Try again</string>
<string name="quit">Quit</string>
<string name="close">Close</string>
<string name="connectionFailMessage">Unable to load schedule. Check your internet connection or try again later</string>
<string name="repeat">Try again</string>
<string name="quit">Quit</string>
<string name="close">Close</string>
<!-- MainActivity -->
<string name="scheduleParametersTitle">Schedule parameters</string>
<string name="authorizeCheckbox">Authorize via Personal cabinet</string>
<string name="facultySpinner">Faculty</string>
<string name="courseSpinner">Course</string>
<string name="groupSpinner">Group</string>
<!-- MainActivity -->
<string name="scheduleParametersTitle">Schedule parameters</string>
<string name="authorizeCheckbox">Authorize via Personal cabinet</string>
<string name="facultySpinner">Faculty</string>
<string name="courseSpinner">Course</string>
<string name="groupSpinner">Group</string>
<string name="passwordField">Password</string>
<string name="passwordField">Password</string>
<string name="applyForOccupation">Apply for occupation</string>
<string name="validateCredential">Validate credential</string>
<string name="exportParametersTitle">Export parameters</string>
<string name="dateRange">Export range</string>
<string name="attendFailed">Something went wrong. Please try again or do it via personal cabinet</string>
<string name="attendSuccess">Successfully registered for occupation</string>
<string name="forDayButton">For day</string>
<string name="forWeekButton">For week</string>
<string name="forMonthButton">For month</string>
<string name="forSemesterButton">For semester</string>
<string name="validationSuccess">Authorized successfully</string>
<string name="validationFailed">Authorization failed. Check your credential.</string>
<string name="reminderSpinner">Set reminders for</string>
<string name="reminderNote">(i) Attention, if you choose \"None\" for cloud-based Google calendars Google sets default reminder for events automatically sets reminders for 30 minutes if there\'s no reminder set by user</string>
<string name="noReminderOption">None</string>
<string name="inTimeReminderOption">At the start of event</string>
<string name="fiveMinuteReminderOption">5 minutes</string>
<string name="tenMinuteReminderOption">10 minutes</string>
<string name="exportParametersTitle">Export parameters</string>
<string name="dateRange">Export range</string>
<string name="addGroupToTitleCheckbox">Add group number to event title</string>
<string name="titleNote">(i) This doesn\'t affect professors\' schedule</string>
<string name="forDayButton">For day</string>
<string name="forWeekButton">For week</string>
<string name="forMonthButton">For month</string>
<string name="forSemesterButton">For semester</string>
<string name="destinationCalendarSpinner">Destination calendar</string>
<string name="reminderSpinner">Set reminders for</string>
<string name="reminderNote">(i) Attention, if you choose \"None\" for cloud-based Google calendars Google sets default reminder for events automatically (usually, 30 minutes)</string>
<string name="noReminderOption">None</string>
<string name="inTimeReminderOption">At the start of event</string>
<string name="fiveMinuteReminderOption">5 minutes</string>
<string name="tenMinuteReminderOption">10 minutes</string>
<string name="addScheduleButton">Add schedule</string>
<string name="addGroupToTitleCheckbox">Add group number to event title</string>
<string name="titleNote">(i) This doesn\'t affect professors\' schedule</string>
<string name="copyrights">©2020 Michael Gordeev, IST, IST-942</string>
<string name="destinationCalendarSpinner">Destination calendar</string>
<string name="clearCalendarOption">Clear schedule</string>
<string name="reportErrorOption">Report error</string>
<string name="addScheduleButton">Add schedule</string>
<string name="invalidDateRangeError">Error: Invalid date range</string>
<string name="authorizationState">Authorization...</string>
<string name="invalidAuthorizationError">Error: Invalid credential</string>
<string name="authorizationError">Authorization error</string>
<string name="invalidCredentialError">Authorization error: Invalid e-mail and/or password</string>
<string name="groupSelectionError">Error: no group was selected</string>
<string name="copyrights">©2020 Michael Gordeev, IST, IST-942</string>
<string name="clearScheduleTitle">Clear schedule</string>
<string name="clearScheduleMessage">This action will purge exported schedule from all available calendars. \nIt will affect only events created by the app. \n\'All\' - will purge all timetable events including the past ones \n\'Upcoming\' - will affect only upcoming timetable events</string>
<string name="clearAllOption">All</string>
<string name="clearUpcomingOption">Upcoming</string>
<string name="cancelOption">Cancel</string>
<string name="clearingStatus">Clearing...</string>
<string name="clearCalendarOption">Clear schedule</string>
<string name="reportErrorOption">Report error</string>
<!-- ExportActivity -->
<string name="loadingStatus">Loading</string>
<string name="potatoLoadingStatus">Loading schedule from SPbSUT potato servers</string>
<string name="scheduleLoadingStatus">Loading schedule</string>
<string name="calendarExportStatus">Exporting to calendar</string>
<string name="doneStatus">Done</string>
<string name="invalidDateRangeError">Error: Invalid date range</string>
<string name="authorizationState">Authorization...</string>
<string name="invalidAuthorizationError">Error: Invalid credential</string>
<string name="authorizationError">Authorization error</string>
<string name="invalidCredentialError">Authorization error: Invalid e-mail and/or password</string>
<string name="groupSelectionError">Error: no group was selected</string>
<!-- AboutActivity -->
<string name="aboutTitle">About application</string>
<string name="appDescription">Application for SPbSUT professors\' and students\' schedule export</string>
<string name="developedBy">Developed by Michael Gordeev (IST-942, IST) in the \"Technologies of Informational and Educational Systems\" Research and Educational Center</string>
<string name="contributorsTitle">Contributors</string>
<string name="clearScheduleTitle">Clear schedule</string>
<string name="clearScheduleMessage">This action will purge exported schedule from all available calendars. \nIt will affect only events created by the app. \n\'All\' - will purge all timetable events including the past ones \n\'Upcoming\' - will affect only upcoming timetable events</string>
<string name="clearAllOption">All</string>
<string name="clearUpcomingOption">Upcoming</string>
<string name="cancelOption">Cancel</string>
<string name="clearingStatus">Clearing...</string>
<string name="specialThanksTitle">Special thanks</string>
<string name="specialThanksPeople">Vitaliy Moshkov, Anastasiya Godunova</string>
<!-- ExportActivity -->
<string name="loadingStatus">Loading</string>
<string name="potatoLoadingStatus">Loading schedule from SPbSUT potato servers</string>
<string name="scheduleLoadingStatus">Loading schedule</string>
<string name="calendarExportStatus">Exporting to calendar</string>
<string name="doneStatus">Done</string>
<string name="contactsTitle">Contacts</string>
<string name="websiteContact">Website</string>
<string name="twitterContact">Twitter</string>
<string name="vkontakteContact">Vkontakte</string>
<!-- AboutActivity -->
<string name="aboutTitle">About application</string>
<string name="appDescription">Application for SPbSUT professors\' and students\' schedule export</string>
<string name="developedBy">Developed by Michael Gordeev (IST-942, IST) in the \"Technologies of Informational and Educational Systems\" Research and Educational Center</string>
<string name="contributorsTitle">Contributors</string>
<string name="linksTitle">Useful links</string>
<string name="privacyPolicyLink">Privacy policy</string>
<string name="repositoryLink">GitHub Repository</string>
<string name="notsLink">\"TIES\" REC</string>
<string name="sutLink">SPbSUT</string>
<string name="specialThanksTitle">Special thanks</string>
<string name="specialThanksPeople">Vitaliy Moshkov, Anastasiya Godunova</string>
<string name="feedbackButton">Leave feedback</string>
<string name="contactsTitle">Contacts</string>
<string name="websiteContact">Website</string>
<string name="twitterContact">Twitter</string>
<string name="vkontakteContact">Vkontakte</string>
<string name="linksTitle">Useful links</string>
<string name="privacyPolicyLink">Privacy policy</string>
<string name="repositoryLink">GitHub Repository</string>
<string name="notsLink">\"TIES\" REC</string>
<string name="sutLink">SPbSUT</string>
<string name="feedbackButton">Leave feedback</string>
</resources>
@@ -13,9 +13,7 @@ namespace GUTSchedule.Test
[Test]
public async Task ScheduleListTest()
{
Assert.Warn("Feature is temporarly disabled. Skipping test");
Assert.Pass();
/*JObject secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json")) as JObject;
JObject secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json")) as JObject;
var list = await Parser.GetSchedule(new CabinetExportParameters
{
Email = secrets["testEmail"].ToObject<string>(),
@@ -36,7 +34,43 @@ namespace GUTSchedule.Test
Console.WriteLine(i.StartTime.ToShortDateString());
Console.WriteLine($"{i.StartTime.ToShortTimeString()}-{i.EndTime.ToShortTimeString()}");
Console.WriteLine(i.Opponent);
}*/
}
}
[Test]
public async Task OccupationsCheckTest()
{
JObject secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json")) as JObject;
var list = await Parser.CheckAvailableOccupations(secrets["testEmail"].ToObject<string>(), secrets["testPassword"].ToObject<string>());
Assert.IsNotNull(list);
if (list.Count < 1)
{
Assert.Warn("No available occupations");
return;
}
Console.WriteLine("Available occupations:");
list.ForEach(i => Console.WriteLine($"{i.Item1} / {i.Item2}"));
}
[Test]
public async Task ApplyForOccupationsTest()
{
JObject secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json")) as JObject;
var list = await Parser.CheckAvailableOccupations(secrets["testEmail"].ToObject<string>(), secrets["testPassword"].ToObject<string>());
Assert.IsNotNull(list);
if (list.Count < 1)
{
Assert.Warn("No available occupations to test");
return;
}
Console.WriteLine("Available occupations:");
list.ForEach(i => Console.WriteLine($"{i.Item1} / {i.Item2}"));
await Parser.ApplyForOccupations(secrets["testEmail"].ToObject<string>(), secrets["testPassword"].ToObject<string>(), list);
}
}
}
@@ -61,15 +61,18 @@
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<StackPanel Padding="10" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Uid="applyForOccupation" x:Name="applyForOccupation" Content="Register for lesson" Click="ApplyForLesson" Style="{StaticResource AccentButtonStyle}" Visibility="Collapsed"/>
<TextBlock x:Uid="scheduleParametersTitle" Style="{StaticResource SubtitleTextBlockStyle}" Text="Schedule parameters"/>
<CheckBox x:Uid="authorizeCheckbox" Content="Authorize via personal cabinet" Checked="ChangeAuthorizationMethod" Unchecked="ChangeAuthorizationMethod" IsChecked="False" IsEnabled="False" x:Name="authorize"/>
<StackPanel x:Name="credentialMethod" Visibility="Collapsed">
<CheckBox x:Uid="authorizeCheckbox" Content="Authorize via personal cabinet" Checked="ChangeAuthorizationMethod" Unchecked="ChangeAuthorizationMethod" IsChecked="True" x:Name="authorize"/>
<StackPanel x:Name="credentialMethod" Visibility="Visible">
<TextBox x:Uid="email" PlaceholderText="E-mail" x:Name="email" IsSpellCheckEnabled="False"/>
<PasswordBox x:Uid="password" PlaceholderText="Password" x:Name="password"/>
<Button x:Uid="validateCredential" x:Name="validateCredential" Content="Validate credential" Click="ValidateCredential"/>
<CheckBox x:Uid="remember" Content="Remember" x:Name="rememberCredential" Checked="RememberCredential_Checked" Unchecked="RememberCredential_Checked"/>
</StackPanel>
<StackPanel x:Name="defaultMethod" Visibility="Visible">
<StackPanel x:Name="defaultMethod" Visibility="Collapsed">
<ComboBox x:Uid="facultySpinner" x:Name="faculty" PlaceholderText="No schedule is available" Header="Course" SelectionChanged="Faculty_SelectionChanged"/>
<ComboBox x:Uid="courseSpinner" x:Name="course" Header="Course" SelectionChanged="Course_SelectionChanged">
<ComboBoxItem Content="1"/>
@@ -24,6 +24,17 @@ namespace GUTSchedule.UWP.Pages
private readonly ResourceLoader resources = ResourceLoader.GetForCurrentView();
static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
private List<(string, string)> _availableOccupations;
private List<(string, string)> AvailableOccupations
{
get => _availableOccupations;
set
{
_availableOccupations = value;
applyForOccupation.Visibility = value.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
}
}
public MainPage() =>
InitializeComponent();
@@ -34,7 +45,7 @@ namespace GUTSchedule.UWP.Pages
PackageVersion ver = Package.Current.Id.Version;
version.Text = $"v{ver.Major}.{ver.Minor}.{ver.Build}.{ver.Revision}";
//authorize.IsChecked = (bool?)settings.Values["Authorize"] ?? true;
authorize.IsChecked = (bool?)settings.Values["Authorize"] ?? true;
if (vault.RetrieveAll() is IReadOnlyList<PasswordCredential> credentials && credentials.Count > 0)
{
email.Text = credentials.First().UserName;
@@ -59,6 +70,15 @@ namespace GUTSchedule.UWP.Pages
reminder.SelectedIndex = (int?)settings.Values["Reminder"] ?? 2;
addGroupToTitle.IsChecked = (bool?)settings.Values["AddGroupToTitle"] ?? false;
try
{
AvailableOccupations = await Parser.CheckAvailableOccupations(email.Text, password.Password);
}
catch
{
AvailableOccupations = new List<(string, string)>();
}
}
catch (HttpRequestException e)
{
@@ -281,5 +301,49 @@ namespace GUTSchedule.UWP.Pages
await dialog.ShowAsync();
}
private async void ValidateCredential(object sender, RoutedEventArgs e)
{
try
{
validateCredential.IsEnabled = false;
await Parser.VaildateAuthorization(email.Text, password.Password);
if (rememberCredential.IsChecked.Value)
vault.Add(new PasswordCredential
{
UserName = email.Text,
Password = password.Password,
Resource = "xfox111.gutschedule"
});
else
foreach (PasswordCredential credential in vault.RetrieveAll())
vault.Remove(credential);
AvailableOccupations = await Parser.CheckAvailableOccupations(email.Text, password.Password);
await new MessageDialog(resources.GetString("validationSuccess")).ShowAsync();
}
catch (Exception ex)
{
await new MessageDialog($"{resources.GetString("validationFailed")}\n{ex.Message}").ShowAsync();
}
validateCredential.IsEnabled = true;
}
private async void ApplyForLesson(object sender, RoutedEventArgs e)
{
try
{
applyForOccupation.Visibility = Visibility.Collapsed;
AvailableOccupations = await Parser.CheckAvailableOccupations(email.Text, password.Password);
await Parser.ApplyForOccupations(email.Text, password.Password, AvailableOccupations);
await new MessageDialog(resources.GetString("attendSuccess")).ShowAsync();
}
catch (Exception ex)
{
await new MessageDialog($"{resources.GetString("attendFailed")}\n{ex.Message}").ShowAsync();
applyForOccupation.Visibility = Visibility.Visible;
}
}
}
}
@@ -160,7 +160,10 @@
<value>Schedule is cleared</value>
</data>
<data name="clearScheduleMessage" xml:space="preserve">
<value>This action will purge exported schedule from all available calendars.
<value>This action will purge exported schedule from all available calendars.
It will affect only events created by the app.
'All' - will purge all timetable events including the past ones
'Upcoming' - will affect only upcoming timetable events</value>
</data>
<data name="clearScheduleTitle.PrimaryButtonText" xml:space="preserve">
<value>Clear schedule</value>
@@ -327,4 +330,22 @@
<data name="websiteContact.Text" xml:space="preserve">
<value>Website</value>
</data>
<data name="applyForOccupation.Content" xml:space="preserve">
<value>Apply for occupation</value>
</data>
<data name="validateCredential.Content" xml:space="preserve">
<value>Validate credential</value>
</data>
<data name="attendFailed" xml:space="preserve">
<value>Something went wrong. Please try again or do it via personal cabinet</value>
</data>
<data name="attendSuccess" xml:space="preserve">
<value>Successfully registered for occupation</value>
</data>
<data name="validationSuccess" xml:space="preserve">
<value>Authorized successfully</value>
</data>
<data name="validationFailed" xml:space="preserve">
<value>Authorization failed. Check your credential.</value>
</data>
</root>
@@ -160,7 +160,10 @@
<value>Расписание очищено</value>
</data>
<data name="clearScheduleMessage" xml:space="preserve">
<value>Это действие удалит экспортированное расписание из всех доступных календарей.
<value>Это действие удалит экспортированное расписание из всех доступных календарей.
Данное действие затронет только расписание, экспортированное этим приложением
'Все' - удалит все события расписания, включая прошедшие
'Только новые' - удалит будущие события расписания</value>
</data>
<data name="clearScheduleTitle.PrimaryButtonText" xml:space="preserve">
<value>Очистить расписание</value>
@@ -327,4 +330,22 @@
<data name="websiteContact.Text" xml:space="preserve">
<value>Веб-сайт</value>
</data>
<data name="applyForOccupation.Content" xml:space="preserve">
<value>Начать занятие</value>
</data>
<data name="validateCredential.Content" xml:space="preserve">
<value>Проверить данные</value>
</data>
<data name="attendFailed" xml:space="preserve">
<value>Что-то пошло не так. Попроуйте снова или выполните действие через личный кабинет</value>
</data>
<data name="attendSuccess" xml:space="preserve">
<value>Регистрация на занятие выполнена</value>
</data>
<data name="validationSuccess" xml:space="preserve">
<value>Авторизация выполнена успешно</value>
</data>
<data name="validationFailed" xml:space="preserve">
<value>Ошибка авторизации. Проверьте данные.</value>
</data>
</root>
+29 -1
View File
@@ -8,13 +8,14 @@ using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace GUTSchedule
{
public static class Parser
{
private static async Task<HttpClient> VaildateAuthorization(string email, string password)
public static async Task<HttpClient> VaildateAuthorization(string email, string password)
{
if (string.IsNullOrWhiteSpace(email))
throw new ArgumentNullException(nameof(email));
@@ -46,6 +47,8 @@ namespace GUTSchedule
throw new System.Security.VerificationException(responseQuery["error"].Replace("|", "; "));
}
await client.GetAsync("https://lk.sut.ru/cabinet/?login=yes");
return client;
}
@@ -440,5 +443,30 @@ namespace GUTSchedule
return schedule;
}
public static async Task<List<(string, string)>> CheckAvailableOccupations(string email, string password)
{
HttpClient client = await VaildateAuthorization(email, password);
HttpResponseMessage response = await client.GetAsync("https://lk.sut.ru/cabinet//project/cabinet/forms/raspisanie_bak.php");
string responseContent = await response.GetString();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(responseContent);
IHtmlDocument doc = new HtmlParser().ParseDocument(responseContent);
List<(string, string)> occupations = doc.QuerySelectorAll(".simple-little-table td[align=left] > span[id] > a").Select(i =>
{
string[] parameters = new Regex(@"(?<=\()[0-9,]*(?=\))").Match(i.Attributes["onclick"].Value).Value.Split(',');
return (parameters[0], parameters[1]);
}).ToList();
return occupations;
}
public static async Task ApplyForOccupations(string email, string password, List<(string, string)> occupations)
{
HttpClient client = await VaildateAuthorization(email, password);
foreach (var i in occupations)
await client.GetAsync($"https://lk.sut.ru/cabinet/project/cabinet/forms/raspisanie_bak.php?open=1&rasp={i.Item1}&week={i.Item2}");
}
}
}
@@ -1,3 +1,3 @@
- Updated schedule parser Обновлен парсер расписания
Personal cabinet authorization still disabled due to some server-side connection issues
- Personal cabinet authorization is available again
- Added ability to validate your personal cabinet credential
- Added ability to apply for lesson in one click
@@ -1,3 +1,3 @@
- Обновлен парсер расписания
Авторизация через личный кабинет все еще отключена из-за проблем подкключения на серверной стороне
- Авторизация через личный кабинет снова доступна
- Добавлена возможность проверить введеные данные личного кабинета
- Добавлена возможность регистрироваться на занятия с помощью одного клика
@@ -1,3 +1,3 @@
- Updated schedule parser Обновлен парсер расписания
Personal cabinet authorization still disabled due to some server-side connection issues
- Personal cabinet authorization is available again
- Added ability to validate your personal cabinet credential
- Added ability to apply for lesson in one click
@@ -1,3 +1,3 @@
- Обновлен парсер расписания
Авторизация через личный кабинет все еще отключена из-за проблем подкключения на серверной стороне
- Авторизация через личный кабинет снова доступна
- Добавлена возможность проверить введеные данные личного кабинета
- Добавлена возможность регистрироваться на занятия с помощью одного клика