feat: read "Занятия в Полоцке" as a day's town, show it in all clients
- Parser 14 (server/places.js): "Занятия в Полоцке/Новополоцке/г. Новополоцк" over a day's first row is no longer a lesson but a note of the day for its column (`places`); lessons of that day get `place`. A lesson below the note in the same cell keeps its own subject.
- A town on its own line ("Новополоцк", "г.Новополоцк") or after a room ("246(Новополоцк)") becomes the lesson's `place` and leaves the subject and room. "История Полоцка" is not a town.
- API 14: `place` on lessons and `places` on days of /api/timetable, `place` in the directory. The day notes are shared by the server and the PWA (shared/places.js); the same note in every subgroup reads as the whole group's.
- Android, PWA and iOS: a "Занятия в Полоцке" card above the day's lessons (day pages, calendar, overview, week view, desk stand), "Полоцк" under the date in the week grid, "249Ж · Полоцк" on lesson cards, a "Город" row in details, the town in widgets, tile, reminders and change lists.
- Snapshot lines show the town and the day notes; tests for notes, towns in cells and the API.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// third-party clients use it instead of re-implementing the white/green week rules.
|
||||
import { semesterView, addDays, weekday, weekIndexOf } from "../shared/weeks.js";
|
||||
import { lessonKey, subgroupsOf } from "../shared/lessons.js";
|
||||
import { placesOn } from "../shared/places.js";
|
||||
import { occurrence, peopleAndRooms, roomsOn, teachersOn, movedOn } from "./directory.js";
|
||||
import { apiError } from "./errors.js";
|
||||
|
||||
@@ -24,6 +25,8 @@ function lessonOut(lesson, group, people, date, status, calendar, week = null) {
|
||||
teachers: date ? teachersOn(people, week) : people.teachers,
|
||||
rooms,
|
||||
roomMoved: Boolean(date && movedOn(people, date)),
|
||||
// "Полоцк": the town, when the cell or the day's note names one.
|
||||
place: lesson.place || null,
|
||||
distance: people.distance,
|
||||
condition: lesson.condition,
|
||||
variant: lesson.variant,
|
||||
@@ -58,6 +61,7 @@ export function timetable({ file, schedule, calendar, group, from, days, range,
|
||||
}
|
||||
let count = Math.min(Math.max(Number(days) || 7, 1), MAX_DAYS);
|
||||
const lessons = schedule.lessons.filter((l) => l.groups.includes(group));
|
||||
const labels = subgroupsOf(schedule, group);
|
||||
// A session schedule printed with a date on every day needs no week table: its own dates
|
||||
// say when each lesson takes place, even in a year the table no longer covers.
|
||||
const sessionDates = lessons.every((l) => l.date || l.dates?.length)
|
||||
@@ -112,11 +116,14 @@ export function timetable({ file, schedule, calendar, group, from, days, range,
|
||||
if (entry.lesson.date === date || entry.lesson.dates?.includes(date))
|
||||
dayLessons.push(lessonOut(entry.lesson, group, entry, date, "yes", calendar));
|
||||
dayLessons.sort((a, b) => a.start.localeCompare(b.start));
|
||||
// A day without lessons (a holiday, weeks of practice) is in no town.
|
||||
const places = dayLessons.length ? placesOn(schedule.places, group, labels, weekday(date), date) : [];
|
||||
out.push({
|
||||
date,
|
||||
weekday: weekday(date),
|
||||
week: week ? { n: week.n, color: week.color } : null,
|
||||
lessons: dayLessons,
|
||||
...(places.length && { places }),
|
||||
});
|
||||
}
|
||||
const weekly = [1, 2, 3, 4, 5, 6, 7]
|
||||
@@ -127,7 +134,11 @@ export function timetable({ file, schedule, calendar, group, from, days, range,
|
||||
.map((e) => lessonOut(e.lesson, group, e, null, "yes", calendar))
|
||||
.sort((a, b) => a.start.localeCompare(b.start)),
|
||||
}))
|
||||
.filter((d) => d.lessons.length);
|
||||
.filter((d) => d.lessons.length)
|
||||
.map((d) => {
|
||||
const places = placesOn(schedule.places, group, labels, d.day);
|
||||
return places.length ? { ...d, places } : d;
|
||||
});
|
||||
return {
|
||||
group,
|
||||
document: {
|
||||
|
||||
Reference in New Issue
Block a user