Implement people management features by adding API endpoints for retrieving school rosters and individual person cards. Enhance the UI to support a people browser with filtering and pagination capabilities. Update localization strings for improved user experience and ensure robust handling of person data. Revise documentation to reflect new API functionalities and update tests to validate the new features.
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Threading.Channels;
|
||||
using HSchool.Content;
|
||||
using HSchool.People;
|
||||
using HSchool.Protocol;
|
||||
using HSchool.Server.Api;
|
||||
using HSchool.Server.Net;
|
||||
using HSchool.Simulation;
|
||||
|
||||
@@ -41,6 +42,7 @@ internal sealed class SchoolWorker
|
||||
|
||||
private SchoolState _snapshot;
|
||||
private Roster? _rosterSnapshot;
|
||||
private DefCatalog? _catalogSnapshot;
|
||||
private School? _school;
|
||||
private Task? _run;
|
||||
private bool _persistOnStop = true;
|
||||
@@ -94,6 +96,9 @@ internal sealed class SchoolWorker
|
||||
/// <summary>Last roster composition. Published like <see cref="Snapshot"/>; needs live on entities.</summary>
|
||||
public Roster? RosterSnapshot => Volatile.Read(ref _rosterSnapshot);
|
||||
|
||||
/// <summary>Frozen catalog for this school. Safe to read from HTTP; it never mutates after load.</summary>
|
||||
public DefCatalog? CatalogSnapshot => Volatile.Read(ref _catalogSnapshot);
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_run = Task.Factory.StartNew(
|
||||
@@ -103,12 +108,15 @@ internal sealed class SchoolWorker
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
|
||||
public void Post(WorkerCommand command)
|
||||
public bool Post(WorkerCommand command)
|
||||
{
|
||||
if (!_mailbox.Writer.TryWrite(command))
|
||||
if (_mailbox.Writer.TryWrite(command))
|
||||
{
|
||||
_logger.LogDebug("Dropped a command for school {SchoolId}: the mailbox is closed.", _id);
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Dropped a command for school {SchoolId}: the mailbox is closed.", _id);
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task StopAsync(bool persist)
|
||||
@@ -185,6 +193,7 @@ internal sealed class SchoolWorker
|
||||
}
|
||||
|
||||
var catalog = _mods.LoadCatalog(packIds, _logger);
|
||||
Volatile.Write(ref _catalogSnapshot, catalog);
|
||||
var map = _mods.LoadMap(packIds, _savedMap);
|
||||
try
|
||||
{
|
||||
@@ -320,6 +329,14 @@ internal sealed class SchoolWorker
|
||||
var school = _school;
|
||||
if (school is null)
|
||||
{
|
||||
while (_mailbox.Reader.TryRead(out var orphan))
|
||||
{
|
||||
if (orphan is WorkerCommand.GetPerson getPerson)
|
||||
{
|
||||
getPerson.Result.TrySetResult(new PersonCardResult(null, PersonLookupError.UnknownSchool));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -357,10 +374,23 @@ internal sealed class SchoolWorker
|
||||
school.Clock.SpeedIndex = setSpeed.SpeedIndex;
|
||||
dirty = true;
|
||||
break;
|
||||
|
||||
case WorkerCommand.GetPerson getPerson:
|
||||
var card = PersonCardReader.Read(school, getPerson.PersonId, getPerson.Locale);
|
||||
getPerson.Result.TrySetResult(
|
||||
card is null
|
||||
? new PersonCardResult(null, PersonLookupError.UnknownPerson)
|
||||
: new PersonCardResult(card, PersonLookupError.None));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (command is WorkerCommand.GetPerson failed)
|
||||
{
|
||||
failed.Result.TrySetException(ex);
|
||||
}
|
||||
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Command {Command} failed for school {SchoolId}; the school keeps running.",
|
||||
|
||||
Reference in New Issue
Block a user