mirror of
https://github.com/XFox111/bonch-calendar.git
synced 2026-06-30 10:52:41 +03:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b03a05b89f | |||
| d765ab0269 | |||
| 462dab9e3e | |||
| b03ff5c61c | |||
| e33acd4fc4 | |||
| 916c7bcb22 | |||
| 882e196ea8 | |||
| fa39e8d26c | |||
| f4d1d4e983 | |||
| c0e6ced376 | |||
| 452e6d51b2 | |||
| 9b74bb63c5 | |||
| ea6dbf2d8f | |||
| c6d91d7020 | |||
| 610b7909cd | |||
| 2560f124f1 | |||
| f1324ce1d1 | |||
| 97751cf20b | |||
| 14f1b4e7b5 | |||
| b932b49b65 | |||
| 52d980534e | |||
| 9d0f6a31d8 |
@@ -6,7 +6,7 @@
|
||||
"image": "mcr.microsoft.com/devcontainers/dotnet:1-10.0",
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"ghcr.io/devcontainers/features/node:2": {
|
||||
"version": "lts",
|
||||
"pnpmVersion": "none",
|
||||
"nvmVersion": "latest"
|
||||
|
||||
+16
-4
@@ -48,7 +48,10 @@ updates:
|
||||
schedule:
|
||||
interval: monthly
|
||||
rebase-strategy: disabled
|
||||
open-pull-requests-limit: 20
|
||||
groups:
|
||||
all:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
@@ -58,7 +61,10 @@ updates:
|
||||
schedule:
|
||||
interval: monthly
|
||||
rebase-strategy: disabled
|
||||
open-pull-requests-limit: 20
|
||||
groups:
|
||||
all:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "devcontainers"
|
||||
directory: "/"
|
||||
@@ -68,7 +74,10 @@ updates:
|
||||
schedule:
|
||||
interval: monthly
|
||||
rebase-strategy: disabled
|
||||
open-pull-requests-limit: 20
|
||||
groups:
|
||||
all:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directories:
|
||||
@@ -80,4 +89,7 @@ updates:
|
||||
schedule:
|
||||
interval: monthly
|
||||
rebase-strategy: disabled
|
||||
open-pull-requests-limit: 20
|
||||
groups:
|
||||
all:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
@@ -31,16 +31,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
- uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: ./api
|
||||
tags: ${{ github.repository }}-api:ci
|
||||
|
||||
- run: docker save ${{ github.repository }}:ci | gzip > api_image.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: api-image
|
||||
path: api_image.tar.gz
|
||||
@@ -49,16 +49,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
- uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: ./app
|
||||
tags: ${{ github.repository }}-app:ci
|
||||
|
||||
- run: docker save ${{ github.repository }}:ci | gzip > app_image.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: app-image
|
||||
path: app_image.tar.gz
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
container: node:latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- run: npm install
|
||||
working-directory: ./app
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
- run: npm audit --audit-level=moderate --json > audit_report.json
|
||||
working-directory: ./app
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: app-audit-report
|
||||
path: ./app/audit_report.json
|
||||
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: docker/metadata-action@v5
|
||||
- uses: docker/metadata-action@v6
|
||||
id: meta
|
||||
with:
|
||||
images: |
|
||||
@@ -26,19 +26,19 @@ jobs:
|
||||
${{ github.ref_name }}
|
||||
|
||||
- name: "Login to Docker Hub"
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: "Login to GitHub Container Registry"
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
- uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: ./api
|
||||
push: true
|
||||
@@ -48,9 +48,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: docker/metadata-action@v5
|
||||
- uses: docker/metadata-action@v6
|
||||
id: meta
|
||||
with:
|
||||
images: |
|
||||
@@ -61,19 +61,19 @@ jobs:
|
||||
${{ github.ref_name }}
|
||||
|
||||
- name: "Login to Docker Hub"
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: "Login to GitHub Container Registry"
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
- uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: ./app
|
||||
push: true
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- run: npm install
|
||||
working-directory: ./app
|
||||
@@ -104,12 +104,12 @@ jobs:
|
||||
working-directory: ./app
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
uses: actions/configure-pages@v6
|
||||
|
||||
- uses: actions/upload-pages-artifact@v4
|
||||
- uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: "./app/dist"
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v5
|
||||
|
||||
@@ -14,7 +14,7 @@ This is my farewell gift to the university.
|
||||
|
||||
## Demo
|
||||
|
||||
<!-- TODO: put demo here -->
|
||||

|
||||
|
||||
## Q&A
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.4.0" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
||||
<PackageReference Include="Ical.Net" Version="5.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
|
||||
<PackageReference Include="Ical.Net" Version="5.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BonchCalendar", "BonchCalendar.csproj", "{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{811C13A0-E5FC-452C-8628-AD36B9A8A7E2}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="BonchCalendar.csproj" />
|
||||
</Solution>
|
||||
+30
-22
@@ -97,30 +97,38 @@ app.MapGet("/timetable/{facultyId}/{groupId}", async (
|
||||
}
|
||||
}
|
||||
|
||||
DateTime semesterStartDate = await apiService.GetSemesterStartDateAsync(groupId);
|
||||
string groupName = (await apiService.GetGroupsListAsync(facultyId, 0))[groupId];
|
||||
|
||||
string classesRaw = await apiService.GetScheduleDocumentAsync(groupId, TimetableType.Classes);
|
||||
List<CalendarEvent> timetable = [.. parsingService.ParseGeneralTimetable(classesRaw, semesterStartDate, groupName)];
|
||||
|
||||
TimetableType[] types = [TimetableType.Attestations, TimetableType.Exams, TimetableType.ExamsForExtramural];
|
||||
foreach (TimetableType type in types)
|
||||
try
|
||||
{
|
||||
classesRaw = await apiService.GetScheduleDocumentAsync(groupId, type);
|
||||
timetable.AddRange(parsingService.ParseExamTimetable(classesRaw, groupName));
|
||||
DateTime semesterStartDate = await apiService.GetSemesterStartDateAsync(groupId);
|
||||
string groupName = (await apiService.GetGroupsListAsync(facultyId, 0))[groupId];
|
||||
|
||||
string classesRaw = await apiService.GetScheduleDocumentAsync(groupId, TimetableType.Classes);
|
||||
List<CalendarEvent> timetable = [.. parsingService.ParseGeneralTimetable(classesRaw, semesterStartDate, groupName)];
|
||||
|
||||
TimetableType[] types = [TimetableType.Attestations, TimetableType.Exams, TimetableType.ExamsForExtramural];
|
||||
foreach (TimetableType type in types)
|
||||
{
|
||||
classesRaw = await apiService.GetScheduleDocumentAsync(groupId, type);
|
||||
timetable.AddRange(parsingService.ParseExamTimetable(classesRaw, groupName));
|
||||
}
|
||||
|
||||
Calendar calendar = new();
|
||||
calendar.Properties.Add(new CalendarProperty("X-WR-CALNAME", groupName));
|
||||
calendar.Properties.Add(new CalendarProperty("X-WR-TIMEZONE", "Europe/Moscow"));
|
||||
calendar.Properties.Add(new CalendarProperty("REFRESH-INTERVAL;VALUE=DURATION", "PT6H"));
|
||||
calendar.Events.AddRange(timetable);
|
||||
calendar.AddTimeZone(new VTimeZone("Europe/Moscow"));
|
||||
string serialized = new CalendarSerializer().SerializeToString(calendar)!;
|
||||
|
||||
await File.WriteAllTextAsync(cacheFile, serialized);
|
||||
logger.LogInformation("Cached timetable for group {GroupId} to {CacheFile}.", groupId, cacheFile);
|
||||
return Results.Text(serialized, contentType: "text/calendar");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to generate timetable for group {GroupId} of faculty {FacultyId}.", groupId, facultyId);
|
||||
throw;
|
||||
}
|
||||
|
||||
Calendar calendar = new();
|
||||
calendar.Properties.Add(new CalendarProperty("X-WR-CALNAME", groupName));
|
||||
calendar.Properties.Add(new CalendarProperty("X-WR-TIMEZONE", "Europe/Moscow"));
|
||||
calendar.Properties.Add(new CalendarProperty("REFRESH-INTERVAL;VALUE=DURATION", "PT6H"));
|
||||
calendar.Events.AddRange(timetable);
|
||||
calendar.AddTimeZone(new VTimeZone("Europe/Moscow"));
|
||||
string serialized = new CalendarSerializer().SerializeToString(calendar)!;
|
||||
|
||||
await File.WriteAllTextAsync(cacheFile, serialized);
|
||||
logger.LogInformation("Cached timetable for group {GroupId} to {CacheFile}.", groupId, cacheFile);
|
||||
return Results.Text(serialized, contentType: "text/calendar");
|
||||
})
|
||||
.WithName("GetTimetable")
|
||||
.WithDescription("Gets the iCal timetable for the specified group.")
|
||||
|
||||
@@ -123,15 +123,15 @@ public partial class ParsingService
|
||||
|
||||
private static (string className, string classType, string[] professors, string auditorium) ParseBaseInfo(IElement classElement)
|
||||
{
|
||||
string className = classElement.QuerySelector(".subect")!.TextContent;
|
||||
string classType = classElement.QuerySelector(".type")!.TextContent
|
||||
.Replace("(", string.Empty).Replace(")", string.Empty).Trim();
|
||||
string className = classElement.QuerySelector(".subect")?.TextContent ?? string.Empty;
|
||||
string classType = classElement.QuerySelector(".type")?.TextContent
|
||||
.Replace("(", string.Empty).Replace(")", string.Empty).Trim() ?? string.Empty;
|
||||
|
||||
string[] professors = classElement.QuerySelector(".teacher[title]")!.GetAttribute("title")
|
||||
!.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
string[] professors = classElement.QuerySelector(".teacher[title]")?.GetAttribute("title")
|
||||
?.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? [];
|
||||
|
||||
string auditorium = classElement.QuerySelector(".aud")!.TextContent
|
||||
.Replace("ауд.:", string.Empty).Replace(';', ',').Trim();
|
||||
string auditorium = classElement.QuerySelector(".aud")?.TextContent
|
||||
.Replace("ауд.:", string.Empty).Replace(';', ',').Trim() ?? string.Empty;
|
||||
|
||||
return (className, classType, professors, auditorium);
|
||||
}
|
||||
|
||||
Generated
+1465
-2263
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -10,25 +10,25 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui/react-components": "^9.72.7",
|
||||
"@fluentui/react-icons": "^2.0.315",
|
||||
"@fluentui/react-motion-components-preview": "^0.14.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"@fluentui/react-components": "^9.73.8",
|
||||
"@fluentui/react-icons": "^2.0.326",
|
||||
"@fluentui/react-motion-components-preview": "^0.15.4",
|
||||
"react": "^19.2.6",
|
||||
"react-dom": "^19.2.6",
|
||||
"react-localization": "^2.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "^19.2.6",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/node": "^25.8.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.24",
|
||||
"globals": "^16.5.0",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.47.0",
|
||||
"vite": "^7.2.2"
|
||||
"@vitejs/plugin-react": "^6.0.2",
|
||||
"eslint": "^10.4.0",
|
||||
"eslint-plugin-react-hooks": "^7.1.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"globals": "^17.6.0",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.59.3",
|
||||
"vite": "^8.0.13"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
Reference in New Issue
Block a user