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

Parser improvements and fixes, Tests (#23)

* Added unit tests, fixed and improved parser

* Updated translation files

* Updated unit tests to work with credentials

* Updated validation CI config

* Updated changelogs, improved CI/CD processes
This commit is contained in:
Michael Gordeev
2020-04-20 17:51:42 +03:00
committed by GitHub
parent 8579f9bbc6
commit a07cb6fd3e
20 changed files with 267 additions and 36 deletions
@@ -15,7 +15,7 @@ namespace GUTSchedule.Droid
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
public partial class Resource
{
@@ -51,7 +51,7 @@
<string name="addScheduleButton">Добавить расписание</string>
<string name="copyrights">©2020 Михаил Гордеев, ИКСС, ИКТ-907</string>
<string name="copyrights">©2020 Михаил Гордеев, ИСиТ, ИСТ-942</string>
<string name="clearCalendarOption">Очистить расписание</string>
<string name="reportErrorOption">Сообщить об ошибке</string>
@@ -81,7 +81,7 @@
<!-- AboutActivity -->
<string name="aboutTitle">О приложении</string>
<string name="appDescription">Приложение для экспорта перподавательского и учебного расписаний Санкт-Петербургского Государственного Университета Телекоммуникаций им. проф. М.А. Бонч-Бруевича</string>
<string name="developedBy">Разработано Михаилом Гордеевым, ИКТ-907, ИКСС в Научно-образовательном центре \"Технологии информационных образовательных систем\"</string>
<string name="developedBy">Разработано Михаилом Гордеевым, ИСТ-942, ИСиТ в Научно-образовательном центре \"Технологии информационных образовательных систем\"</string>
<string name="contributorsTitle">Свой вклад в разработку внесли</string>
<string name="specialThanksTitle">Особые благодарности</string>
@@ -50,7 +50,7 @@
<string name="addScheduleButton">Add schedule</string>
<string name="copyrights">©2020 Michael Gordeev, INS, ICT-907</string>
<string name="copyrights">©2020 Michael Gordeev, IST, IST-942</string>
<string name="clearCalendarOption">Clear schedule</string>
<string name="reportErrorOption">Report error</string>
@@ -79,7 +79,7 @@
<!-- 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 (ICT-907, INS) in the \"Technologies of Informational and Educational Systems\" Research Facility</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="specialThanksTitle">Special thanks</string>
@@ -93,7 +93,7 @@
<string name="linksTitle">Useful links</string>
<string name="privacyPolicyLink">Privacy policy</string>
<string name="repositoryLink">GitHub Repository</string>
<string name="notsLink">\"TIES\" RF</string>
<string name="notsLink">\"TIES\" REC</string>
<string name="sutLink">SPbSUT</string>
<string name="feedbackButton">Leave feedback</string>
@@ -0,0 +1,96 @@
using System;
using System.Threading.Tasks;
using GUTSchedule.Models;
using NUnit.Framework;
namespace GUTSchedule.Test
{
public class AnonymousScheduleUnitTest
{
[Test]
public async Task FacultiesListTest()
{
var list = await Parser.GetFaculties();
Assert.IsNotNull(list);
Assert.IsTrue(list.Count > 0);
Console.WriteLine("Faculties list:");
list.ForEach(i =>
Console.WriteLine($"{i.name} ({i.id})"));
}
[Test]
public async Task GroupListTest()
{
var faculties = await Parser.GetFaculties();
if (faculties == null || faculties.Count < 1)
{
Assert.Warn("No faculties found");
return;
}
var (id, name) = faculties[new Random().Next(0, faculties.Count)];
Console.WriteLine($"Randomly selected faculty: {name} ({id})");
var list = await Parser.GetGroups(id);
Assert.IsNotNull(list);
Assert.IsTrue(list.Count > 0);
Console.WriteLine("Groups list:");
list.ForEach(i =>
Console.WriteLine($"{i.name} ({i.id})"));
}
[Test]
public async Task ScheduleListTest()
{
var faculties = await Parser.GetFaculties();
if (faculties == null || faculties.Count < 1)
{
Assert.Warn("No faculties found");
return;
}
var faculty = faculties[new Random().Next(0, faculties.Count)];
Console.WriteLine($"Randomly selected faculty: {faculty.name} ({faculty.id})");
var groups = await Parser.GetGroups(faculty.id);
if (groups == null || groups.Count < 1)
{
Assert.Warn("No groups found");
return;
}
var group = groups[new Random().Next(0, groups.Count)];
Console.WriteLine($"Randomly selected group: {group.name} ({group.id})");
try
{
var list = await Parser.GetSchedule(new DefaultExportParameters
{
Course = "0",
FacultyId = faculty.id,
GroupId = group.id,
EndDate = DateTime.Today.AddDays(7),
StartDate = DateTime.Today
});
Assert.IsNotNull(list);
Assert.IsTrue(list.Count > 0);
Console.WriteLine("Events list:");
foreach (var i in list)
{
Console.WriteLine("--------------------------------------------------");
Console.WriteLine($"[{i.Group}] {i.Order}. {i.Name} ({i.Type})");
Console.WriteLine(i.Cabinet);
Console.WriteLine(i.StartTime.ToShortDateString());
Console.WriteLine($"{i.StartTime.ToShortTimeString()}-{i.EndTime.ToShortTimeString()}");
Console.WriteLine(i.Opponent);
}
}
catch (NullReferenceException e)
{
Assert.Warn(e.Message);
}
}
}
}
@@ -0,0 +1,40 @@
using NUnit.Framework;
using System.Threading.Tasks;
using GUTSchedule.Models;
using System;
using Newtonsoft.Json;
using System.IO;
namespace GUTSchedule.Test
{
public class CabinetScheduleUnitTest
{
[Test]
public async Task ScheduleListTest()
{
dynamic secrets = JsonConvert.DeserializeObject(File.ReadAllText(Directory.GetCurrentDirectory() + "\\TestCredential.json"));
var list = await Parser.GetSchedule(new CabinetExportParameters
{
Email = secrets.testEmail,
Password = secrets.testPassword,
EndDate = DateTime.Today.AddDays(7),
StartDate = DateTime.Today
});
Assert.IsNotNull(list);
Assert.IsTrue(list.Count > 0);
Console.WriteLine("Events list:");
foreach (var i in list)
{
Console.WriteLine("--------------------------------------------------");
Console.WriteLine($"[{i.Group}] {i.Order}. {i.Name} ({i.Type})");
Console.WriteLine(i.Cabinet);
Console.WriteLine(i.StartTime.ToShortDateString());
Console.WriteLine($"{i.StartTime.ToShortTimeString()}-{i.EndTime.ToShortTimeString()}");
Console.WriteLine(i.Opponent);
}
}
}
}
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<UserSecretsId>f8ba6b25-bdcb-442c-98aa-3bcf31300ad0</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="3.0.0" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GUTSchedule\GUTSchedule.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="TestCredential.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
{
"testEmail": "%EMAIL%",
"testPassword": "%PASSWORD%"
}
@@ -187,7 +187,7 @@
<value>Contacts</value>
</data>
<data name="contributorsTitle.Text" xml:space="preserve">
<value>Contributors</value>
<value>Contributors</value>
</data>
<data name="copyrights.Text" xml:space="preserve">
<value>©2020 Michael Gordeev, IST, IST-942</value>
@@ -208,7 +208,7 @@
<value>Export to</value>
</data>
<data name="destinationCalendar.Header" xml:space="preserve">
<value>Destination calendar</value>
<value>Destination calendar</value>
</data>
<data name="developedBy.Text" xml:space="preserve">
<value>Developed by Michael Gordeev (IST-942, IST) in the "Technologies of Informational and Educational Systems" Research and Educational Center</value>
@@ -274,7 +274,7 @@
<value>My schedule</value>
</data>
<data name="noReminder.Content" xml:space="preserve">
<value>None</value>
<value>None</value>
</data>
<data name="notsLink.Text" xml:space="preserve">
<value>"TIES" REC</value>
@@ -187,7 +187,7 @@
<value>Контакты</value>
</data>
<data name="contributorsTitle.Text" xml:space="preserve">
<value>Свой вклад в разработку внесли</value>
<value>Свой вклад в разработку внесли</value>
</data>
<data name="copyrights.Text" xml:space="preserve">
<value>©2020 Михаил Гордеев, ИСиТ, ИСТ-942</value>
@@ -208,7 +208,7 @@
<value>По</value>
</data>
<data name="destinationCalendar.Header" xml:space="preserve">
<value>Конечный календарь</value>
<value>Конечный календарь</value>
</data>
<data name="developedBy.Text" xml:space="preserve">
<value>Разработано Михаилом Гордеевым, ИСТ-942, ИСиТ в Научно-образовательном центре "Технологии информационных образовательных систем"</value>
+32
View File
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUTSchedule.UWP", "GUTSched
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GUTSchedule", "GUTSchedule\GUTSchedule.csproj", "{A6F6DE35-0EB4-4D11-9FF9-F4601595B639}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUTSchedule.Test", "GUTSchedule.Test\GUTSchedule.Test.csproj", "{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -146,6 +148,36 @@ Global
{A6F6DE35-0EB4-4D11-9FF9-F4601595B639}.Release|x64.Build.0 = Release|Any CPU
{A6F6DE35-0EB4-4D11-9FF9-F4601595B639}.Release|x86.ActiveCfg = Release|Any CPU
{A6F6DE35-0EB4-4D11-9FF9-F4601595B639}.Release|x86.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|ARM.ActiveCfg = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|ARM.Build.0 = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|ARM64.Build.0 = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|x64.ActiveCfg = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|x64.Build.0 = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|x86.ActiveCfg = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Debug|x86.Build.0 = Debug|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|Any CPU.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|Any CPU.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|ARM.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|ARM.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|ARM64.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|ARM64.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|x64.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|x64.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|x86.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release (APK)|x86.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|Any CPU.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|ARM.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|ARM.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|ARM64.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|ARM64.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|x64.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|x64.Build.0 = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|x86.ActiveCfg = Release|Any CPU
{B5DD5664-E1B4-4F48-B0CD-C0A126C7E58D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+20 -12
View File
@@ -25,7 +25,7 @@ namespace GUTSchedule
await client.GetAsync("https://cabs.itut.ru/cabinet/");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://cabs.itut.ru/cabinet/lib/autentificationok.php");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://lk.sut.ru/cabinet/lib/autentificationok.php");
request.SetContent(
("users", email),
("parole", password));
@@ -39,8 +39,9 @@ namespace GUTSchedule
if (!responseContent.StartsWith("1", StringComparison.OrdinalIgnoreCase))
{
Dictionary<string, string> responseQuery = new Dictionary<string, string>();
foreach (string i in responseContent.Split('&'))
responseQuery.Add(i.Split('=')[0], i.Split('=')[1]);
if (responseContent.Length > 1)
foreach (string i in responseContent.Split('&'))
responseQuery.Add(i.Split('=')[0], i.Split('=')[1]);
throw new System.Security.VerificationException(responseQuery["error"].Replace("|", "; "));
}
@@ -85,7 +86,7 @@ namespace GUTSchedule
schedule[k - 1].Name == schedule[k].Name &&
schedule[k - 1].Type == schedule[k].Type)
{
schedule[k - 1].Opponent += $" ({schedule[k-1].Cabinet}) \n {schedule[k].Opponent} ({schedule[k].Cabinet})";
schedule[k - 1].Opponent += $" ({schedule[k-1].Cabinet}) \n{schedule[k].Opponent} ({schedule[k].Cabinet})";
schedule[k - 1].Cabinet += "; " + schedule[k].Cabinet;
schedule.RemoveAt(k--);
}
@@ -229,11 +230,11 @@ namespace GUTSchedule
int order = int.Parse(item.GetAttribute("pair")) - 1;
Occupation occupation = new Occupation
{
Name = item.QuerySelector(".subect").TextContent,
Name = item.QuerySelector(".subect").TextContent.Replace(" (1)", "").Replace(" (2)", ""),
Type = item.QuerySelector(".type").TextContent.Replace("(", "").Replace(")", ""),
Group = groupName,
Opponent = item.QuerySelector(".teacher")?.GetAttribute("title").Replace("; ", "\n") ?? "",
Cabinet = item.QuerySelector(".aud")?.TextContent.Replace("ауд.: ", "").Replace("; Б22", "") ?? "СПбГУТ",
Opponent = item.QuerySelector(".teacher")?.GetAttribute("title").Replace("; ", "") ?? "",
Cabinet = item.QuerySelector(".aud")?.TextContent.Replace("ауд.: ", "").Replace("; Б22", "").Replace(" ", "") ?? "СПбГУТ",
Order = order > 50 ? $"Ф{order - 81}" : order.ToString()
};
@@ -309,10 +310,17 @@ namespace GUTSchedule
DateTime date = DateTime.Parse(item.FirstChild.FirstChild.TextContent, new CultureInfo("ru-RU"));
string rawTime = item.ChildNodes[2].TextContent;
rawTime = rawTime.Substring(rawTime.IndexOf('(')).Replace(")", "").Replace('.', ':');
occupation.StartTime = date.Add(TimeSpan.Parse(rawTime.Split('-')[0]));
occupation.EndTime = date.Add(TimeSpan.Parse(rawTime.Split('-')[1]));
try
{
rawTime = rawTime.Substring(rawTime.IndexOf('(')).Replace(")", "").Replace('.', ':');
occupation.StartTime = date.Add(TimeSpan.Parse(rawTime.Split('-')[0]));
occupation.EndTime = date.Add(TimeSpan.Parse(rawTime.Split('-')[1]));
}
catch
{
occupation.StartTime = date;
occupation.EndTime = date;
}
schedule.Add(occupation);
}
@@ -355,7 +363,7 @@ namespace GUTSchedule
item.Opponent = i.QuerySelectorAll("i")[k].NextSibling.NextSibling.NextSibling?.NodeType == NodeType.Text ?
i.QuerySelectorAll("i")[k].NextSibling.NextSibling.NextSibling.TextContent : "";
try { item.Cabinet = i.QuerySelectorAll("small")[k].NextSibling.TextContent.Replace("; Б22", ""); }
try { item.Cabinet = i.QuerySelectorAll("small")[k].NextSibling.TextContent.Replace(" ", "").Replace(";Б22", ""); }
catch { item.Cabinet = "СПбГУТ"; }
string rawTime = i.QuerySelectorAll("b")[k * 2 + 2].TextContent;
-3
View File
@@ -1,3 +0,0 @@
<ru-RU>
- Исправлена проблема с экспортом расписания без авторизации
</ru-RU>
@@ -1 +1 @@
- Fixed anonymous schedule loading
- Fixed and improved parser
@@ -0,0 +1 @@
- Fixed and improved parser
@@ -1 +1 @@
- Исправлена проблема с экспортом расписания без авторизации
- Исправлен и улучшен парсер расписания
@@ -0,0 +1 @@
- Исправлен и улучшен парсер расписания
+1 -1
View File
@@ -1,2 +1,2 @@
## Core
- Fixed anonymous schedule loading
- Fixed and improved parser
+6 -5
View File
@@ -8,7 +8,8 @@ trigger:
exclude:
- '*'
include:
- changelogs/android.xml
- changelogs/appMetadata/en/changelogs/*
- changelogs/appMetadata/ru/changelogs/*
pool:
vmImage: 'windows-latest'
@@ -33,6 +34,8 @@ steps:
script: |
(Get-Content AndroidManifest.xml -encoding UTF8 | Out-String) -replace '(?<=\bandroid:versionCode=")[^"]*', $(Build.BuildId) | set-content AndroidManifest.xml -encoding UTF8
(Get-Content AndroidManifest.xml -encoding UTF8 | Out-String) -replace '(?<=\bandroid:versionName=")[^"]*', $(Build.BuildNumber) | set-content AndroidManifest.xml -encoding UTF8
Rename-Item '$(Build.SourcesDirectory)/changelogs/appMetadata/en/changelogs/android-changelog.txt' '$(Build.BuildId).txt'
Rename-Item '$(Build.SourcesDirectory)/changelogs/appMetadata/ru/changelogs/android-changelog.txt' '$(Build.BuildId).txt'
workingDirectory: '$(Build.SourcesDirectory)\GUT.Schedule\GUTSchedule.Droid\Properties'
- task: XamarinAndroid@1
@@ -68,10 +71,8 @@ steps:
displayName: 'Copy changelog to output'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/changelogs'
Contents: |
android.xml
github.md
TargetFolder: '$(outputDirectory)'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/changelogs'
- task: PowerShell@2
displayName: 'Update package name'
+23 -1
View File
@@ -43,4 +43,26 @@ steps:
platform: 'x64'
solution: '**/**.UWP.csproj'
configuration: '$(buildConfiguration)'
msbuildArgs: '/p:AppxPackageSigningEnabled=false'
msbuildArgs: '/p:AppxPackageSigningEnabled=false'
- task: FileTransform@2
displayName: 'Update test credential'
inputs:
folderPath: '$(System.DefaultWorkingDirectory)'
xmlTransformationRules:
jsonTargetFiles: '**/TestCredential.json'
- task: VSBuild@1
displayName: 'Build Tests'
inputs:
solution: '**\*.Test.csproj'
- task: VSTest@2
displayName: 'Run Tests'
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: |
**\*.Test.dll
!**\*TestAdapter.dll
!**\obj\**
searchFolder: '$(System.DefaultWorkingDirectory)'
+2 -1
View File
@@ -8,7 +8,8 @@ trigger:
exclude:
- '*'
include:
- changelogs/appMetadata/*
- changelogs/appMetadata/en/baseListing/*
- changelogs/appMetadata/ru/baseListing/*
pool:
vmImage: 'windows-latest'