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 SelectedCalendarIndex { get; set; }
public static int Reminder { 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 startDate = DateTime.Today;
DateTime endDate = DateTime.Today.AddDays(7); DateTime endDate = DateTime.Today.AddDays(7);
Button start, end, export; Button start, end, export, applyForOccupation, validateCredential;
Button forDay, forWeek, forMonth, forSemester; Button forDay, forWeek, forMonth, forSemester;
Spinner faculty, course, group, reminder, calendar; Spinner faculty, course, group, reminder, calendar;
CheckBox groupTitle, authorize; CheckBox groupTitle, authorize;
@@ -81,10 +92,19 @@ namespace GUTSchedule.Droid.Activities
start.Text = startDate.ToShortDateString(); start.Text = startDate.ToShortDateString();
groupTitle.Checked = prefs.GetBoolean("AddGroupToHeader", false); groupTitle.Checked = prefs.GetBoolean("AddGroupToHeader", false);
//authorize.Checked = prefs.GetBoolean("Authorize", true); authorize.Checked = prefs.GetBoolean("Authorize", true);
email.Text = prefs.GetString("email", ""); email.Text = prefs.GetString("email", "");
password.Text = prefs.GetString("password", ""); 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) 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 // 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 // I consider Preferences as safe enough method for storing credentials
// А во-вторых, даже такой казалось бы небезопасный метод хранения учетных данных в сто раз надежнее того дерьма, // А во-вторых, даже такой казалось бы небезопасный метод хранения учетных данных в сто раз надежнее того дерьма,
// что творится на серверах Бонча (я не шучу, там все ОЧЕНЬ плохо) // что творится на серверах Бонча (я не шучу, там пиздец)
// Ну и в-третьих: Андроид - это пиздец и настоящий ад разработчика. И если бы была моя воля, я бы под него никогда не писал #FuckAndroid // Ну и в-третьих: Андроид - это пиздец и настоящий ад разработчика. И если бы была моя воля, я бы под него никогда не писал #FuckAndroid
// З.Ы. Помнишь про второй пункт? Так вот, если ты используешь такой же пароль как в ЛК где-то еще, настоятельно рекомендую его поменять // З.Ы. Помнишь про второй пункт? Так вот, если ты используешь такой же пароль как в ЛК где-то еще, настоятельно рекомендую его поменять
PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("email", email.Text).Apply(); PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("email", email.Text).Apply();
@@ -192,6 +212,8 @@ namespace GUTSchedule.Droid.Activities
forWeek = FindViewById<Button>(Resource.Id.forWeek); forWeek = FindViewById<Button>(Resource.Id.forWeek);
forMonth = FindViewById<Button>(Resource.Id.forMonth); forMonth = FindViewById<Button>(Resource.Id.forMonth);
forSemester = FindViewById<Button>(Resource.Id.forSemester); forSemester = FindViewById<Button>(Resource.Id.forSemester);
applyForOccupation = FindViewById<Button>(Resource.Id.applyForOccupation);
validateCredential = FindViewById<Button>(Resource.Id.validateCredential);
faculty = FindViewById<Spinner>(Resource.Id.faculty); faculty = FindViewById<Spinner>(Resource.Id.faculty);
course = FindViewById<Spinner>(Resource.Id.course); course = FindViewById<Spinner>(Resource.Id.course);
@@ -261,6 +283,40 @@ namespace GUTSchedule.Droid.Activities
endDate = new DateTime(DateTime.Today.Year, 8, 31); endDate = new DateTime(DateTime.Today.Year, 8, 31);
end.Text = endDate.ToShortDateString(); 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; start.Click += Start_Click;
end.Click += End_Click; end.Click += End_Click;
@@ -138,6 +138,9 @@
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.3" /> <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.Core.Utils" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.CustomTabs" 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" /> <PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
File diff suppressed because it is too large Load Diff
@@ -11,6 +11,15 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="10dp"> 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 <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -22,15 +31,15 @@
android:id="@+id/authorization" android:id="@+id/authorization"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:enabled="false" android:checked="true"
android:checked="false"
android:text="@string/authorizeCheckbox"/> android:text="@string/authorizeCheckbox"/>
<LinearLayout <LinearLayout
android:id="@+id/studentParams" android:id="@+id/studentParams"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical"
android:visibility="gone">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -67,8 +76,7 @@
android:id="@+id/professorParams" android:id="@+id/professorParams"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical">
android:visibility="gone">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -91,6 +99,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textWebPassword"/> android:inputType="textWebPassword"/>
<Button
android:id="@+id/validateCredential"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/validateCredential"/>
</LinearLayout> </LinearLayout>
<TextView <TextView
@@ -28,6 +28,14 @@
<string name="groupSpinner">Группа</string> <string name="groupSpinner">Группа</string>
<string name="passwordField">Пароль</string> <string name="passwordField">Пароль</string>
<string name="applyForOccupation">Начать занятие</string>
<string name="validateCredential">Проверить данные</string>
<string name="attendFailed">Что-то пошло не так. Попроуйте снова или выполните действие через личный кабинет</string>
<string name="attendSuccess">Регистрация на занятие выполнена</string>
<string name="validationSuccess">Авторизация выполнена успешно</string>
<string name="validationFailed">Ошибка авторизации. Проверьте данные.</string>
<string name="exportParametersTitle">Параметры экспорта</string> <string name="exportParametersTitle">Параметры экспорта</string>
<string name="dateRange">Диапазон экспорта</string> <string name="dateRange">Диапазон экспорта</string>
@@ -27,6 +27,14 @@
<string name="groupSpinner">Group</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="attendFailed">Something went wrong. Please try again or do it via personal cabinet</string>
<string name="attendSuccess">Successfully registered for occupation</string>
<string name="validationSuccess">Authorized successfully</string>
<string name="validationFailed">Authorization failed. Check your credential.</string>
<string name="exportParametersTitle">Export parameters</string> <string name="exportParametersTitle">Export parameters</string>
<string name="dateRange">Export range</string> <string name="dateRange">Export range</string>
@@ -37,7 +45,7 @@
<string name="forSemesterButton">For semester</string> <string name="forSemesterButton">For semester</string>
<string name="reminderSpinner">Set reminders for</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="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="noReminderOption">None</string>
<string name="inTimeReminderOption">At the start of event</string> <string name="inTimeReminderOption">At the start of event</string>
<string name="fiveMinuteReminderOption">5 minutes</string> <string name="fiveMinuteReminderOption">5 minutes</string>
@@ -13,9 +13,7 @@ namespace GUTSchedule.Test
[Test] [Test]
public async Task ScheduleListTest() public async Task ScheduleListTest()
{ {
Assert.Warn("Feature is temporarly disabled. Skipping test"); JObject secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json")) as JObject;
Assert.Pass();
/*JObject secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json")) as JObject;
var list = await Parser.GetSchedule(new CabinetExportParameters var list = await Parser.GetSchedule(new CabinetExportParameters
{ {
Email = secrets["testEmail"].ToObject<string>(), Email = secrets["testEmail"].ToObject<string>(),
@@ -36,7 +34,43 @@ namespace GUTSchedule.Test
Console.WriteLine(i.StartTime.ToShortDateString()); Console.WriteLine(i.StartTime.ToShortDateString());
Console.WriteLine($"{i.StartTime.ToShortTimeString()}-{i.EndTime.ToShortTimeString()}"); Console.WriteLine($"{i.StartTime.ToShortTimeString()}-{i.EndTime.ToShortTimeString()}");
Console.WriteLine(i.Opponent); 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"/> <ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Padding="10" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <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"/> <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"/> <CheckBox x:Uid="authorizeCheckbox" Content="Authorize via personal cabinet" Checked="ChangeAuthorizationMethod" Unchecked="ChangeAuthorizationMethod" IsChecked="True" x:Name="authorize"/>
<StackPanel x:Name="credentialMethod" Visibility="Collapsed"> <StackPanel x:Name="credentialMethod" Visibility="Visible">
<TextBox x:Uid="email" PlaceholderText="E-mail" x:Name="email" IsSpellCheckEnabled="False"/> <TextBox x:Uid="email" PlaceholderText="E-mail" x:Name="email" IsSpellCheckEnabled="False"/>
<PasswordBox x:Uid="password" PlaceholderText="Password" x:Name="password"/> <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"/> <CheckBox x:Uid="remember" Content="Remember" x:Name="rememberCredential" Checked="RememberCredential_Checked" Unchecked="RememberCredential_Checked"/>
</StackPanel> </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="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"> <ComboBox x:Uid="courseSpinner" x:Name="course" Header="Course" SelectionChanged="Course_SelectionChanged">
<ComboBoxItem Content="1"/> <ComboBoxItem Content="1"/>
@@ -24,6 +24,17 @@ namespace GUTSchedule.UWP.Pages
private readonly ResourceLoader resources = ResourceLoader.GetForCurrentView(); private readonly ResourceLoader resources = ResourceLoader.GetForCurrentView();
static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; 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() => public MainPage() =>
InitializeComponent(); InitializeComponent();
@@ -34,7 +45,7 @@ namespace GUTSchedule.UWP.Pages
PackageVersion ver = Package.Current.Id.Version; PackageVersion ver = Package.Current.Id.Version;
version.Text = $"v{ver.Major}.{ver.Minor}.{ver.Build}.{ver.Revision}"; 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) if (vault.RetrieveAll() is IReadOnlyList<PasswordCredential> credentials && credentials.Count > 0)
{ {
email.Text = credentials.First().UserName; email.Text = credentials.First().UserName;
@@ -59,6 +70,15 @@ namespace GUTSchedule.UWP.Pages
reminder.SelectedIndex = (int?)settings.Values["Reminder"] ?? 2; reminder.SelectedIndex = (int?)settings.Values["Reminder"] ?? 2;
addGroupToTitle.IsChecked = (bool?)settings.Values["AddGroupToTitle"] ?? false; 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) catch (HttpRequestException e)
{ {
@@ -281,5 +301,49 @@ namespace GUTSchedule.UWP.Pages
await dialog.ShowAsync(); 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> <value>Schedule is cleared</value>
</data> </data>
<data name="clearScheduleMessage" xml:space="preserve"> <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>
<data name="clearScheduleTitle.PrimaryButtonText" xml:space="preserve"> <data name="clearScheduleTitle.PrimaryButtonText" xml:space="preserve">
<value>Clear schedule</value> <value>Clear schedule</value>
@@ -327,4 +330,22 @@
<data name="websiteContact.Text" xml:space="preserve"> <data name="websiteContact.Text" xml:space="preserve">
<value>Website</value> <value>Website</value>
</data> </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> </root>
@@ -160,7 +160,10 @@
<value>Расписание очищено</value> <value>Расписание очищено</value>
</data> </data>
<data name="clearScheduleMessage" xml:space="preserve"> <data name="clearScheduleMessage" xml:space="preserve">
<value>Это действие удалит экспортированное расписание из всех доступных календарей. <value>Это действие удалит экспортированное расписание из всех доступных календарей.
Данное действие затронет только расписание, экспортированное этим приложением
'Все' - удалит все события расписания, включая прошедшие
'Только новые' - удалит будущие события расписания</value>
</data> </data>
<data name="clearScheduleTitle.PrimaryButtonText" xml:space="preserve"> <data name="clearScheduleTitle.PrimaryButtonText" xml:space="preserve">
<value>Очистить расписание</value> <value>Очистить расписание</value>
@@ -327,4 +330,22 @@
<data name="websiteContact.Text" xml:space="preserve"> <data name="websiteContact.Text" xml:space="preserve">
<value>Веб-сайт</value> <value>Веб-сайт</value>
</data> </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> </root>
+29 -1
View File
@@ -8,13 +8,14 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace GUTSchedule namespace GUTSchedule
{ {
public static class Parser 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)) if (string.IsNullOrWhiteSpace(email))
throw new ArgumentNullException(nameof(email)); throw new ArgumentNullException(nameof(email));
@@ -46,6 +47,8 @@ namespace GUTSchedule
throw new System.Security.VerificationException(responseQuery["error"].Replace("|", "; ")); throw new System.Security.VerificationException(responseQuery["error"].Replace("|", "; "));
} }
await client.GetAsync("https://lk.sut.ru/cabinet/?login=yes");
return client; return client;
} }
@@ -440,5 +443,30 @@ namespace GUTSchedule
return schedule; 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 is available again
- Added ability to validate your personal cabinet credential
Personal cabinet authorization still disabled due to some server-side connection issues - Added ability to apply for lesson in one click
@@ -1,3 +1,3 @@
- Обновлен парсер расписания - Авторизация через личный кабинет снова доступна
- Добавлена возможность проверить введеные данные личного кабинета
Авторизация через личный кабинет все еще отключена из-за проблем подкключения на серверной стороне - Добавлена возможность регистрироваться на занятия с помощью одного клика
@@ -1,3 +1,3 @@
- Updated schedule parser Обновлен парсер расписания - Personal cabinet authorization is available again
- Added ability to validate your personal cabinet credential
Personal cabinet authorization still disabled due to some server-side connection issues - Added ability to apply for lesson in one click
@@ -1,3 +1,3 @@
- Обновлен парсер расписания - Авторизация через личный кабинет снова доступна
- Добавлена возможность проверить введеные данные личного кабинета
Авторизация через личный кабинет все еще отключена из-за проблем подкключения на серверной стороне - Добавлена возможность регистрироваться на занятия с помощью одного клика