Skip to main content
The journey is the student’s progress record. It holds deneme (mock exam) results, and it derives everything else: rolling averages, trends, topic performance, study-hours stats, and the streak. Three sources write into it. The client posts denemeler through this API. Lala records denemeler that the student reports in chat. The planner supplies study time — a completed block is study time, and the journey never stores a second copy.

What a deneme is

One deneme is one scored paper. A TYT paper and an AYT paper from the same exam day are two denemeler. Their nets are never summed. A deneme carries per-subject results. Each result holds dogru, yanlis, bos, and a net. The server computes the net from the counts:
  • TYT and AYT: net = dogru − yanlis / 4
  • LGS: net = dogru − yanlis / 3
A student often reports only the net (“matematik 16 net”). That is a first-class write: send net and leave the counts out. When the counts are present, the server computes the net and ignores a claimed one. A deneme can also carry per-topic rows (“Paragraf: 8 doğru, 3 yanlış, 1 boş”). Topic rows are optional. They feed the topic performance tables.

Subjects

Subject keys are canonical and exam-prefixed: tyt_matematik, ayt_tarih1, lgs_fen. On write, the API also accepts display names, aliases, and short codes — Matematik, mat, and MAT all land on the same key. An unknown subject is a 400, never a silent new subject. Each result in a response carries the key, a Turkish label (“Matematik”), and a short code (“MAT”) for dense views. On AYT, tarih and coğrafya resolve to the -1 papers (ayt_tarih1, ayt_cografya1). Name the -2 papers as tarih-2 or coğrafya-2.

Record a deneme

POST /api/denemeler records one paper.
The response is the full deneme. Subjects come back in booklet order, with the computed nets and the totalNet rollup. GET /api/denemeler lists denemeler, newest first, with an opaque cursor. Pass nextCursor back as cursor for the next older page. examType narrows to one paper type.

Correct a deneme

PATCH /api/denemeler/:id is a partial update. Header fields overwrite. subjects and topics upsert by their natural key:
Fields left off a subject patch keep their stored values. The patch above corrects dogru and keeps the known yanlis, and the server recomputes the net and totalNet. Set remove: true on a subject or topic row to delete it. examType cannot change — it would re-key every subject. Delete the deneme and record it again. DELETE /api/denemeler/:id removes the paper and every row under it. The averages recompute on the next read.

The overview

GET /api/journey returns the journey tab in one call:
avgNet is the average of the last three denemeler of that exam type. trend compares that window against the same window shifted one deneme back: up, down, or flat. The same pair exists per subject. daysTogether counts from sign-up, first day inclusive. The streak counts days with meaningful activity: a chat message, a completed study block, a recorded deneme, or a saved check-in (see Check-ins). current reads 0 once a day has been missed; longest is the record.

One subject in depth

GET /api/journey/subjects/:subject serves the per-subject screen. The path parameter is the canonical key:
The response holds the rolling average and trend, the subject’s net in each deneme of the window (series, newest first), all-time completed study minutes on the subject, and the topic table. Each topic row aggregates the window: total questions, correct, accuracy (doğru over all questions, 0–1), and consistency. Consistency is high when the per-deneme accuracy barely moves, low when it swings, and null under three denemeler of data. strongTopics and weakTopics are pre-filtered: strong is accuracy at or over 0.85, weak is under 0.6, and both need at least three questions of sample.

Study time

GET /api/journey/study?from=2026-05-16&to=2026-08-13 sums completed planner minutes over a date range. Both dates are local Istanbul dates. The range is capped at 366 days. days carries every date in the range, zeros included — a heatmap renders straight off it. bySubject is the distribution for the treemap, largest first, each with its share of the total in pct. dailyAvgMin averages over the active days, not the calendar: it answers “how much does a study day hold”. Only blocks with status: done count, and a block with no length counts zero. To make study time appear here, complete blocks through the planner (see Planner).

What Lala does with it

Lala reads the journey in every conversation: the newest denemeler, the trend, and the weakest topics. Lala also writes it — a student who reports a result in chat gets it recorded, and a correction (“matematik 18’di aslında”) lands as a patch. Records that Lala creates carry source: "chat" and createdBy: "lala", so the client can tell them apart from form entries.