Split the person card into overview, clothes, carry and now tabs.
Today's history is a paged HTTP log on the worker, not on the card or in people.json. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
using Arch.Core;
|
||||
using HSchool.Ai;
|
||||
using HSchool.Content;
|
||||
using HSchool.People;
|
||||
|
||||
namespace HSchool.Simulation.Tests;
|
||||
|
||||
public class PersonDayLogTests
|
||||
{
|
||||
private static readonly DateTime TuesdayFive = new(2012, 4, 3, 5, 0, 0, DateTimeKind.Utc);
|
||||
private static readonly DateTime TuesdayNoon = new(2012, 4, 3, 12, 0, 0, DateTimeKind.Utc);
|
||||
private static readonly DateTime TuesdayEvening = new(2012, 4, 3, 22, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
[Fact]
|
||||
public void TryStartAndFinish_WriteStartAndEndRows()
|
||||
{
|
||||
using var school = OpenCafeteria(TuesdayNoon);
|
||||
Spawn(school, "a", hunger: 0.3f);
|
||||
|
||||
Assert.True(school.TryStartAction("a", "EatLunch"));
|
||||
Assert.Contains(
|
||||
school.DayLog,
|
||||
row => row.PersonId == "a"
|
||||
&& row.Type == PersonLogTypes.ActionStarted
|
||||
&& row.ThingDef == "EatLunch");
|
||||
|
||||
TickMinutes(school, 16);
|
||||
|
||||
Assert.Contains(
|
||||
school.DayLog,
|
||||
row => row.PersonId == "a"
|
||||
&& row.Type == PersonLogTypes.ActionEnded
|
||||
&& row.ThingDef == "EatLunch");
|
||||
Assert.Equal(
|
||||
"начал: Обед",
|
||||
school.DayLog.First(row => row.Type == PersonLogTypes.ActionStarted).Caption(school.Catalog!, "ru"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AfterSix_YesterdaysRowsAreGone()
|
||||
{
|
||||
using var school = OpenCafeteria(TuesdayFive);
|
||||
Spawn(school, "a", hunger: 0.3f);
|
||||
Assert.True(school.TryStartAction("a", "EatLunch"));
|
||||
Assert.NotEmpty(school.DayLog);
|
||||
|
||||
TickMinutes(school, 61);
|
||||
|
||||
Assert.True(school.Clock.Time >= new DateTime(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc));
|
||||
Assert.Empty(school.DayLog);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipEmpty_ClearsYesterdaysRows()
|
||||
{
|
||||
using var school = OpenEmpty(TuesdayEvening);
|
||||
school.AppendDayLog(new PersonLogEvent("f0.c0", TuesdayEvening, PersonLogTypes.ActionStarted, "EatLunch"));
|
||||
Assert.NotEmpty(school.DayLog);
|
||||
|
||||
var result = school.TrySkipEmpty();
|
||||
|
||||
Assert.True(result.Succeeded);
|
||||
Assert.Empty(school.DayLog);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pages_DoNotOverlap()
|
||||
{
|
||||
var time = TuesdayNoon;
|
||||
var log = new List<PersonLogEvent>();
|
||||
for (var i = 0; i < 25; i++)
|
||||
{
|
||||
log.Add(new PersonLogEvent("p", time.AddMinutes(i), PersonLogTypes.ActionStarted, "EatLunch"));
|
||||
}
|
||||
|
||||
var first = PersonLogBrowser.Apply(log, "p", new PersonLogQuery(null, Descending: false, Page: 1, PageSize: 10), catalog: null, "ru");
|
||||
var second = PersonLogBrowser.Apply(log, "p", new PersonLogQuery(null, Descending: false, Page: 2, PageSize: 10), catalog: null, "ru");
|
||||
|
||||
Assert.Equal(25, first.Total);
|
||||
Assert.Equal(10, first.Entries.Count);
|
||||
Assert.Equal(10, second.Entries.Count);
|
||||
Assert.Empty(first.Entries.Select(row => row.Time).Intersect(second.Entries.Select(row => row.Time)));
|
||||
Assert.Equal(time, first.Entries[0].Time);
|
||||
Assert.Equal(time.AddMinutes(10), second.Entries[0].Time);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Search_FiltersByCaption()
|
||||
{
|
||||
var log = new[]
|
||||
{
|
||||
new PersonLogEvent("p", TuesdayNoon, PersonLogTypes.ActionStarted, "EatLunch"),
|
||||
new PersonLogEvent("p", TuesdayNoon.AddMinutes(1), PersonLogTypes.ActionStarted, "UseToilet"),
|
||||
};
|
||||
|
||||
var page = PersonLogBrowser.Apply(log, "p", new PersonLogQuery("toilet", Descending: false, Page: 1, PageSize: 20), catalog: null, "en");
|
||||
Assert.Equal(1, page.Total);
|
||||
Assert.Equal("action-started", page.Entries[0].Type);
|
||||
Assert.Equal("UseToilet", page.Entries[0].ThingDef);
|
||||
}
|
||||
|
||||
private static void TickMinutes(School school, int minutes)
|
||||
{
|
||||
for (var i = 0; i < minutes; i++)
|
||||
{
|
||||
school.Tick(0.2d, 5d);
|
||||
}
|
||||
}
|
||||
|
||||
private static School OpenCafeteria(DateTime start)
|
||||
{
|
||||
var catalog = new CatalogLoader().Load(
|
||||
[CatalogLoader.CorePackId],
|
||||
[
|
||||
new ContentDocument("core", "defs/actions/sit.jsonc", """{ "defName": "Sit", "abstract": true }"""),
|
||||
new ContentDocument(
|
||||
"core",
|
||||
"defs/actions/eat.jsonc",
|
||||
"""
|
||||
{
|
||||
"defName": "EatLunch",
|
||||
"room": "Cafeteria",
|
||||
"thing": "Chair",
|
||||
"minutes": 15,
|
||||
"need": "Hunger",
|
||||
"needGain": 0.5,
|
||||
"roles": ["student"]
|
||||
}
|
||||
"""),
|
||||
new ContentDocument(
|
||||
"core",
|
||||
"defs/needs/hunger.jsonc",
|
||||
"""{ "defName": "Hunger", "initial": 1, "decayPerHour": 0, "min": 0, "max": 1 }"""),
|
||||
new ContentDocument("core", "defs/things/chair.jsonc", """{ "defName": "Chair", "actions": ["Sit"] }"""),
|
||||
new ContentDocument("core", "defs/territories/yard.jsonc", """{ "defName": "Yard", "travelMinutes": 1 }"""),
|
||||
new ContentDocument("core", "defs/buildings/main.jsonc", """{ "defName": "Main" }"""),
|
||||
new ContentDocument("core", "defs/floors/floor.jsonc", """{ "defName": "Floor" }"""),
|
||||
new ContentDocument(
|
||||
"core",
|
||||
"defs/rooms/cafeteria.jsonc",
|
||||
"""
|
||||
{
|
||||
"defName": "Cafeteria",
|
||||
"slots": [{ "key": "seats", "thing": "Chair" }],
|
||||
"travelMinutes": 1
|
||||
}
|
||||
"""),
|
||||
new ContentDocument("core", "localizations/ru.jsonc", """{ "EatLunch": "Обед", "ActionStarted": "начал: {0}", "ActionEnded": "закончил: {0}" }"""),
|
||||
]);
|
||||
var map = new MapLayout
|
||||
{
|
||||
Territory = new TerritoryNode { Id = "yard", Def = "Yard" },
|
||||
Buildings = [new BuildingNode { Id = "main", Def = "Main" }],
|
||||
Floors = [new FloorNode { Id = "floor-1", Def = "Floor", Building = "main" }],
|
||||
Rooms =
|
||||
[
|
||||
new RoomNode
|
||||
{
|
||||
Id = "cafe",
|
||||
Def = "Cafeteria",
|
||||
Building = "main",
|
||||
Floor = "floor-1",
|
||||
Slots = [new SlotFill { Key = "seats", Thing = "Chair", Count = 8 }],
|
||||
},
|
||||
],
|
||||
Links = [new MapLink { A = "yard", B = "cafe" }],
|
||||
};
|
||||
MapValidator.Validate(map, catalog);
|
||||
return School.Create(1, "Столовая", start, catalog, map);
|
||||
}
|
||||
|
||||
private static void Spawn(School school, string id, float hunger)
|
||||
{
|
||||
school.World.Create(
|
||||
new PersonIdentity(id, "f", false, TuesdayNoon, DummyName()),
|
||||
new PersonRoles(true, false, false, null, null, null),
|
||||
new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal) { ["Hunger"] = hunger }),
|
||||
new Presence("cafe", 0f, "cafe", false, []),
|
||||
PersonActivity.Idle);
|
||||
}
|
||||
|
||||
private static School OpenEmpty(DateTime start)
|
||||
{
|
||||
var (catalog, map) = Vanilla();
|
||||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Russia", start);
|
||||
var pool = ApplicantPool.Create(catalog, roster, schoolSeed: 1, "Russia", start);
|
||||
var school = School.Create(1, "Пустая", start, catalog, map);
|
||||
school.InstallPeople(roster, seed: 1, "Russia", pool);
|
||||
school.ConfigurePresence(weekDays: 5, maxDecisionsPerTick: 10_000);
|
||||
return school;
|
||||
}
|
||||
|
||||
private static (DefCatalog Catalog, MapLayout Map) Vanilla()
|
||||
{
|
||||
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||||
var documents = new List<ContentDocument>();
|
||||
foreach (var path in Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
|
||||
&& !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var relative = Path.GetRelativePath(root, path).Replace('\\', '/');
|
||||
documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
|
||||
}
|
||||
|
||||
var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
|
||||
var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
|
||||
Assert.NotNull(map);
|
||||
return (catalog, map);
|
||||
}
|
||||
|
||||
private static PersonName DummyName()
|
||||
{
|
||||
var cases = new CaseTable
|
||||
{
|
||||
Nom = "А",
|
||||
Gen = "А",
|
||||
Dat = "А",
|
||||
Acc = "А",
|
||||
Ins = "А",
|
||||
Pre = "А",
|
||||
};
|
||||
return new PersonName("А", "А", "А", cases, cases, cases);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user