Update wire protocol to version 6 and enhance timetable functionality
ci / server (push) Failing after 3m53s
ci / client (push) Successful in 15s

- Bumped the wire protocol version to 6, reflecting changes in the communication structure.
- Expanded the timetable API with new endpoints for fetching and managing lesson schedules, including `GET /api/schools/{id}/timetable` and `POST /api/schools/{id}/timetable/pin`.
- Updated the protocol documentation to include detailed descriptions of the new timetable features and message structures.
- Enhanced the client-side implementation to support the new timetable functionalities, including lesson pinning and unpinning.
- Revised server-side logic to handle timetable operations and ensure proper integration with existing school management features.
- Added tests to validate the new timetable functionalities and ensure robustness in handling lesson data.
This commit is contained in:
Leonid Pershin
2026-08-19 10:05:05 +03:00
parent 2011d12b1d
commit cad3068bac
30 changed files with 1621 additions and 43 deletions
+37 -1
View File
@@ -1,6 +1,7 @@
using System.Text.Json;
using HSchool.Content;
using HSchool.People;
using HSchool.Schedule;
using HSchool.Simulation;
using Microsoft.Extensions.Options;
@@ -104,7 +105,8 @@ internal sealed class SchoolStore
{
var fileName = Path.GetFileName(path);
if (string.Equals(fileName, IndexFileName, StringComparison.OrdinalIgnoreCase)
|| fileName.EndsWith(".people.json", StringComparison.OrdinalIgnoreCase))
|| fileName.EndsWith(".people.json", StringComparison.OrdinalIgnoreCase)
|| fileName.EndsWith(".timetable.json", StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -188,6 +190,12 @@ internal sealed class SchoolStore
{
File.Delete(people);
}
var timetable = TimetablePath(id);
if (File.Exists(timetable))
{
File.Delete(timetable);
}
}
public RosterDocument? TryReadPeople(int id)
@@ -215,10 +223,38 @@ internal sealed class SchoolStore
WriteAtomic(PeoplePath(id), document, RosterJson.Options);
}
public Timetable? TryReadTimetable(int id)
{
var path = TimetablePath(id);
if (!File.Exists(path))
{
return null;
}
try
{
var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<Timetable>(json, Json);
}
catch (Exception ex)
{
throw new SchoolContentUnavailableException(
$"School {id} timetable file could not be read.",
ex);
}
}
public void SaveTimetable(int id, Timetable table)
{
WriteAtomic(TimetablePath(id), table);
}
private string SchoolPath(int id) => Path.Combine(DirectoryPath, $"{id}.json");
private string PeoplePath(int id) => Path.Combine(DirectoryPath, $"{id}.people.json");
private string TimetablePath(int id) => Path.Combine(DirectoryPath, $"{id}.timetable.json");
private string IndexPath() => Path.Combine(DirectoryPath, IndexFileName);
private static void WriteAtomic<T>(string path, T value, JsonSerializerOptions? options = null)