Skip to content

Business Logic & Dataflow

Compact reference for Digital Thai MoCA / BrainDI system behavior.

Actors & Components

ActorComponentPurpose
Clinical staffBackoffice (braindi-backoffice-frontend)Create tests, review results, manual grading, PDF export
PatientPatient webapp (automoca-web-app)Take MoCA test on tablet/browser
System workersautomoca-aiservice + global-asr-serviceAuto-score sections & transcribe audio (no UI)

Shared backend: automoca-api-services (braindi-backend).

Test Lifecycle

Key Statuses

StatusMeaning
createdTest link exists; patient has not started testing.
testingPatient is currently in or has started the test flow.
predictingBackend/AI scoring is processing.
waiting for reviewTest is ready for staff review/scoring.
finishedAll required scores are complete with no review required, or staff review is finalized; the result is downloadable.

Backoffice progress label while a patient is testing

The backoffice progress label is based on the test's latest successfully submitted step. It is not a direct report of the patient webapp screen currently open. Consequently, the label intentionally lags behind the patient by one submitted item or domain boundary.

Progress is persisted in two locations by automoca-api-services:

  • MongoDB: root field lastStep on the test document. Backoffice test records and progress labels use this persisted test data.
  • Redis: JSON field last_step under key test:<testId>. Patient token validation and resume routing use this cache record. Progress updates retain the key's existing TTL.
Patient webapp screen currently in progressBackoffice status label
Patient details, terms/consent, Trail Makingรอทดสอบ
Cube, Clock, microphone check, Lion namingVisuospatial
Rhino naming, Camel naming, Memory trial 1Naming
Memory trial 2, forward digits 21854Memory
Backward digits 742, Tap-on-1 tutorial, Tap-on-1 test, serial subtraction, sentence 1Attention
Sentence 2, Thai ก-word fluency, both train/bicycle Abstraction answer screensLanguage
Both ruler/clock Abstraction answer screens, every Delayed Recall round, Orientation question 1Abstraction
Orientation questions 2–6Delayed Recall
Thank-you screen after /endประมวลผล, then รอประเมิน when review is required or เสร็จสิ้น when no review is required

Delayed Recall and Orientation have implementation-specific progress behavior:

  • While Delayed Recall is running, the backend cannot determine that its dynamic free-recall/category-hint/multiple-choice flow is fully complete. It marks lastStep = delayed_recall only when the patient submits Orientation question 1. Therefore, the backoffice still displays Abstraction during Delayed Recall and Orientation question 1.
  • The backend treats all six Orientation questions as one aggregate test. During questions 2–6, lastStep remains delayed_recall; when question 6 completes, it changes to orientation and the patient webapp immediately calls /end. Therefore, Orientation is not visible as an active testing label in the backoffice.
  • Patient resume uses the same aggregate boundary: last_step = delayed_recall resumes at the Orientation introduction/question 1, rather than at Orientation questions 2–6.

Auth Model

TokenUsed byProtects
Staff bearerBackoffice/tests/*, /patients/*, /users/*, /usergroups, /web/tests/generate, /web/tests/staff-update-patient-detail
Test bearerPatient webapp/web/tests/* (submit answers, upload files, live speech)

401 on backoffice → redirect to /login.

Staff login returns the account's role and primary organization. The backend also loads the account's accessible organization IDs into a Redis session cache. Staff JWTs contain user_id and signed_time but no JWT exp claim; request validation additionally requires the Redis entry, whose repository configuration expires after 15 hours. Normal logout deletes that entry.

Patient Webapp Test Page Flow (automoca-web-app)

Fixed 22-page sequence (excludes backoffice):

After page 22, backend runs async ASR + AI scoring on submitted media.

Scored total (MoCA): 30 pts — Memory (page 10) is learning only and does not count toward total score. Only delayed recall round 1 (free recall) counts toward MoCA total (0–5 pts).


MoCA Test Domains — Detail

1. Visuospatial / Executive — 5 pts

PageTaskPatient actionAI scoringPoints
3Trail makingDraw path connecting circles in ordervisuospatial_1 — OpenCV path analysis on drawing + stroke coordinates0 or 1
4Cube drawingCopy 3D cube from referencevisuospatial_2 — Gemini multi-score + logistic regression pass/fail0 or 1
5Clock drawing (11:10)Draw clock face with handsvisuospatial_3 — Gemini evaluates 3 components1 + 1 + 1

Clock sub-scores (visuospatial_3):

  • score_1 — contour closed + circular
  • score_2 — numbers 1–12 complete
  • score_3 — hands point to 11 and 2

Photo upload fallback → AI marks unsure, staff must grade manually.


Audio check (page 6) — not scored

PageTaskPatient actionScoring
6Audio checkConfirm can hear doctor's voice (speak any words)No score — mic/ASR gate only

Occurs after visuospatial, before naming. Patient must speak so real-time ASR detects words. If nothing is detected, the patient webapp loops between mic-test and retry pages — no skip path. User cannot proceed until ASR succeeds.


2. Naming — 3 pts

PageTaskPatient actionAI scoringPoints
7สิงโต (lion)See image → speak namenaming_1 — ASR + answer match0 or 1
8แรด (rhino)See image → speak namenaming_20 or 1
9อูฐ (camel)See image → speak namenaming_30 or 1

Live WSS speech during test; batch ASR after /end for final transcript.


3. Memory — not scored

PageTrialPatient actionAPIScoring
10memory_1App plays 5 words, patient recalls all 5PUT /web/tests/memoryNo score
10memory_2Second recall of the same 5 wordsPUT /web/tests/memoryNo score

Words: หน้า, ผ้าไหม, วัด, มะลิ, สีแดง — same set reappears in Delayed Recall (page 20).

Learning phase only. Backoffice may display memory audio/transcripts for review but neither trial adds to MoCA total.


4. Attention — 6 pts

PageTaskPatient actionAI / rule scoringPoints
1121854Repeat digits forwardattention_1 — transcript match0 or 1
12742Repeat digits backwardattention_2 — transcript match0 or 1
13Tap-1 (เคาะนิ้ว)Tap when hearing "1" in digit streamRule-based tap detection (29 digits)0 or 1
14100 − 7Answer 100 − 7, then keep subtracting 7 until timeoutattention_4 — ASR + Gemini0–3 cumulative

Patient flow: User answers what 100 − 7 is (93), then continues subtracting 7 on each step until the recording times out. The patient webapp does not stop after a fixed number of answers.

Grading: Backoffice scoring uses only the first 5 answers (93 → 86 → 79 → 72 → 65). Additional answers recorded before timeout are stored but not counted toward the MoCA score.

Pages 11, 12, 14 use real-time WSS speech during the test.


5. Language — 3 pts

PageTaskPatient actionAI scoringPoints
15จอมRepeat sentence: ฉันรู้ว่าจอมเป็นคนเดียวที่มาช่วยงานวันนี้language_10 or 1
16แมวRepeat sentence: แมวมักซ่อนตัวอยู่หลังเก้าอี้เมื่อมีหมาอยู่ในห้องlanguage_20 or 1
17ก.ไก่Name words starting with ก within time limitlanguage_3 — word count + Thai prefix rules0 or 1

6. Abstraction — 2 pts

PageTaskPatient actionAI scoringPoints
18รถไฟ + จักรยานState similarity (2 audio answers)abstraction_1 — allowed-answer match0 or 1
19ไม้บรรทัด + นาฬิกาState similarity (2 audio answers)abstraction_2 — allowed-answer match0 or 1

7. Delayed Recall — 5 pts

One screen (page 20) with 3 sub-rounds. Words: หน้า, ผ้าไหม, วัด, มะลิ, สีแดง (same as page 10).

PhaseSupportPatient flowAI taskCounts toward MoCA?
1 — Free recallตอบได้เองSay all 5 words at once. All correct → skip to orientation; else → phase 2delayed_recall_1Yes
2 — Category hintใบ้หมวดหมู่Per missed word: play hint audio → wait for answer. After last missing word: all correct → skip; else → phase 3delayed_recall_2No
3 — Multiple choiceตัวเลือกPer still-wrong word: MCQ via audio → answer. Always proceeds to orientation even if words remain wrongdelayed_recall_3No

MoCA scoring (round 1 only):

  • AI scores each word 0 or 1 (score_1score_5) by matching allowed answers in the transcript.
  • Round 1: single transcript checked for all 5 words. Rounds 2/3: per-word transcripts.
  • MoCA total uses delayed_recall_1 only (sum of 5 word scores, 0–5 pts). Backend score.Total and backoffice grader both use round 1.
  • Rounds 2 and 3 are stored for staff review but do not change MoCA total.

Siriraj auxiliary metric (MisTotal): 3×DR1 + 2×DR2 + DR3 — exported separately, not used in MoCA total. DR2/DR3 scores cascade: only words still wrong from the prior round are counted.


8. Orientation — 6 pts

#QuestionPatient actionAI taskPoints
1วันนี้วันที่เท่าไรSpeak answerorientation_10 or 1
2เดือนนี้เดือนอะไรSpeak answerorientation_20 or 1
3ปีนี้ปีพ.ศ.อะไรSpeak answerorientation_30 or 1
4วันนี้วันอะไรSpeak answerorientation_40 or 1
5ที่นี่ที่ไหนSpeak answerorientation_50 or 1
6จังหวัดอะไรSpeak answerorientation_60 or 1

Ground truth from test end timestamp + patient geolocation.


MoCA score summary

DomainMax ptsCounts toward total?
Visuospatial5Yes
Naming3Yes
MemoryNo (learning only)
Attention6Yes
Language3Yes
Abstraction2Yes
Delayed Recall5Yes (round 1 free recall only)
Orientation6Yes
Total30

Patient Webapp Test Flow (automoca-web-app)

AI Scoring Tasks

automoca-aiservice routes Kafka messages by type:

GroupTask types
Visuospatialvisuospatial_1, visuospatial_2, visuospatial_3
Namingnaming_1, naming_2, naming_3
Attentionattention_1, attention_2, attention_4
Languagelanguage_1, language_2, language_3
Abstractionabstraction_1, abstraction_2
Delayed recalldelayed_recall_1, delayed_recall_2, delayed_recall_3
Orientationorientation_1orientation_6

Each task reads media from S3, runs scorer (rules / Gemini / ResNet), returns score payload via Kafka.

Staff Backoffice Flow (braindi-backoffice-frontend)

Backoffice grading modules: attention, naming, memory, visuospatial, language, abstraction, delayed-recall, orientation.

Staff can override AI scores per section before finalizing the report.

Backoffice Roles & Access Control

The system uses three technical role values. The backoffice displays different labels for two of them:

Backoffice labelTechnical rolePrimary scope
CreatoruserTests created by that account in its primary organization
AdminadminTests and users in the Admin's assigned organizations
OrganizersuperuserAll organizations

Each staff account has one primary usergroupId. An Admin may also have accessibleUsergroupIds for additional organizations. The primary organization is always included in the effective access list. Admin accounts without additional IDs, including legacy accounts, fall back to their primary organization.

Organization selection

  • Creator: no organization selector; the dashboard and generated tests use the account's primary organization.
  • Admin with one organization: no organization selector; the primary organization is used.
  • Admin with multiple organizations: the organization selector lists only assigned organizations.
  • Organizer: the selector lists all organizations and is always shown.
  • The backoffice dashboard displays one organization at a time. Admin requests without an organization default to the primary organization. The backend permits Organizer test search without an organization filter, but the backoffice waits for an organization selection before loading records.
  • Only Organizer can replace an Admin's additional organization access through the API. That operation preserves the primary organization and refreshes an active Redis session immediately.

Test and backoffice capabilities

Unless a row says otherwise, “own” means a test whose stored creator user ID matches the logged-in account, and “assigned” means the test's organization is in the Admin's effective access list.

CapabilityCreator (user)Admin (admin)Organizer (superuser)
Log in to backofficeYesYesYes
List organizationsPrimary onlyAssigned organizationsAll organizations
Generate a test linkPrimary organizationSelected assigned organizationSelected organization
Search dashboard testsOwn onlySelected assigned organizationSelected organization in UI; any/all through API
Open a resultOwn onlyAssigned organizationsAny
Download test mediaOwn onlyAssigned organizationsAny
Review and override scoresOwn onlyAssigned organizationsAny
Edit patient demographicsOwn onlyAssigned organizationsAny
Edit result-page note in UINoYes, except when the Admin's primary organization is SirirajYes
Update note through APIOwn onlyAssigned organizationsAny
Generate/download PDFOwn onlyAssigned organizationsAny
Delete testNoAssigned organizations except SirirajAny, including Siriraj

Creator ownership remains the access boundary throughout the test lifecycle. A Creator cannot open or modify another Creator's test even when both accounts have the same primary organization. Admin access is organization-based and is not limited to tests created by that Admin.

The result action is available only when the test is waiting for review or finished. This status rule is the same for every role.

Siriraj behavior

Siriraj is the only organization with explicit role/action exceptions in the current implementation:

  • The backoffice hides manual test-link generation whenever Siriraj is selected, for every role. The test-creation API has no equivalent Siriraj-specific check and otherwise applies the normal role scope.
  • Creator cannot delete any test. Admin cannot delete a Siriraj test, enforced by the backend even when Siriraj is assigned to that Admin. Organizer can delete a Siriraj test.
  • Result-page note editing checks the Admin's primary organization, not the result's organization. A primary-Siriraj Admin cannot edit notes in the UI. A multi-organization Admin whose primary organization is not Siriraj can edit a Siriraj note in the UI. The backend note-update endpoint applies normal assigned-organization access and has no Siriraj-specific note rule.
  • Other patient fields remain editable for a role that can access the result.
  • Scoring a Siriraj test automatically generates and submits its result to the Siriraj integration. Changing education on an already finished Siriraj test also triggers resubmission.

User and organization management APIs

There is no user-management screen in braindi-backoffice-frontend. The following routes are active in automoca-api-services and require a staff bearer token:

Method and runtime pathCurrent authorization
POST /usersCreator denied. Admin may create only Creator accounts in assigned organizations. Organizer may create Creator or Admin accounts, but not Organizer accounts. Additional accessible organizations are accepted only when the new account is an Admin.
GET /users/:idCreator may read self. Admin may read users in assigned organizations. Organizer may read any user.
PUT /usersCreator may target self. Admin may target users whose primary organization matches the Admin's primary organization. Organizer may target any user. Accepted mutable fields are username, password, first name, last name, and phone number.
GET /users/usergroup/:usergroup_idCreator receives only self for the primary organization. Admin may list users in an assigned organization. Organizer may list users in any organization.
PUT /users/:id/accessible-usergroupsOrganizer only; target must be an Admin. Replaces additional organization access while retaining the Admin's primary organization.
POST /usergroupsOrganizer only; creates an organization/usergroup.
GET /usergroupsCreator receives the primary organization, Admin receives assigned organizations, and Organizer receives all organizations.

Current operations update staff information for every role directly in MongoDB rather than through a backoffice screen. Staff offboarding is also a manual operations/database process.

The active management APIs do not provide operations to:

  • create another Organizer account;
  • delete, deactivate, reactivate, or lock a staff account;
  • change an account's role, email, or primary organization;
  • administratively revoke a staff session;
  • rename or delete an organization.

Manual database changes do not automatically refresh or delete an existing Redis session. The cached role and organization access remain in use until the cache expires, the user logs out, or operations remove/update the Redis entry.

Authorization implementation facts

  • Backoffice route components do not have role-specific route guards. UI controls are conditionally displayed, while staff API endpoints apply their own backend authorization checks.
  • PUT /patients and GET /patients/tests/:id require a valid staff bearer token but do not apply role, creator, or organization checks in the patient service.
  • The registered staff route POST /tests/files has its test ownership/organization permission check commented out. Its controller obtains test-token context rather than applying the normal staff role matrix.
  • Tests retain the creating user ID and username, but the system has no structured audit history recording which staff account later changed scores or patient data, generated a PDF, or deleted a test.

Core Data Entities

EntityDescription
UsergroupClinic / organization; scopes staff access
UserStaff account with a role, one primary usergroup, and optional additional accessible usergroups for Admin
TestOne MoCA session; linked to patient + test token
PatientDemographics (education level affects interpretation)
Section scorePer-domain result from AI, rules, or manual grade

Score Interpretation

Backoffice maps total score to cognitive level:

LevelMeaning
ADAlzheimer's disease range
MCIMild cognitive impairment
NORMALNormal range

Education level (Thai labels in UI) adjusts clinical context for staff review.

Async vs Real-Time Processing

WhenWhat runsWhy
During testWebSocket ASRImmediate feedback for patient UX
After /endKafka ASR + AIFull scoring pipeline on all media

Cron job monitors delayed AI predictions → SNS alarm if threshold exceeded.

Multi-Brand Behavior

Same backend serves multiple frontends (DTM, BrainDI, Siriraj) via configured frontend URLs. Business logic is shared; branding and domain differ per deployment.