Quỹ Hoa Mặt Trời · Kế hoạch kỹ thuật nội bộ

Nâng cấp Trung tâm điều hành

Thiết kế & giao diện · tiếng Việt toàn phần · trình bày tên & liên kết · phân quyền 3 mô-đun · tốc độ & chi phí · quản lý học sinh có cấu trúc · báo cáo chạy nền.
Lập ngày 11/06/2026 · nhánh claude/website-redesign · Trạng thái: Đợt 1–4 + 5 PR (#35–#39) đã dựng & xanh còn lại: merge → deploy → bật phân quyền → backfill (đều cần bạn) (cập nhật 11/06/2026 — xem mục Tình trạng)
Phạm vi: ứng dụng hub (app/hub.py, app/main.py, app/templates/, app/static/hmt.css) + sổ đăng ký chăm sóc + lớp báo cáo. Không đụng Giai đoạn A (mọi bài vẫn cần duyệt tay).

Tám hạng mục bạn nêu chia làm hai khối. Khối A — giao diện & trải nghiệm (mục 1, 2, 3, 6 và toàn bộ phản hồi trực tiếp tối nay): chủ yếu là sửa template + CSS, làm được nhanh, ít rủi ro. Khối B — nền tảng (mục 4 phân quyền, mục 5 + 8 tốc độ, mục 7 cấu trúc học sinh): đụng dữ liệu và hạ tầng, cần làm cẩn thận theo bước.

Khuyến nghị thứ tự (chi tiết ở cuối trang)

  1. Đợt 1 — Giao diện & dọn chữ (1–2 ngày): nhẹ hoá tiêu đề, dọn hết chữ Anh, ẩn mã kỹ thuật, sửa breadcrumb & tên thẻ bài, bỏ ô tìm kiếm trống. làm trước
  2. Đợt 2 — Tốc độ (1 ngày): giữ máy chủ luôn ấm, cache đọc Sheet, đưa việc tạo báo cáo ra chạy nền. làm trước
  3. Đợt 3 — Phân quyền 3 mô-đun (2–3 ngày): bảng người dùng + vai trò + chặn theo khu vực.
  4. Đợt 4 — Cấu trúc học sinh (4–6 ngày): mã ứng viên/học sinh, hai chương trình, hồ sơ tài liệu, OCR, nhật ký nguồn. nền tảng

Tình trạng triển khai · cập nhật 11/06/2026

Toàn bộ mã của Đợt 1–4 đã DỰNG, MERGE và XANH trên nhánh claude/website-redesign (575 bài kiểm thử đạt; đã rà soát bảo mật & tường lửa). Năm việc xây-thêm hôm nay cũng xong dưới dạng 5 PR nháp (#35–#39). Phần còn lại CHỈ là các bước vận hành thật cần bạn quyết: merge PR → deploy → bật phân quyền (cần danh sách người dùng) → nạp dữ liệu thật (có giám sát).

Hạng mụcViệcTình trạng
Đợt 1 · Giao diện & tiếng ViệtNhẹ hoá thiết kế, dọn chữ Anh, ẩn mã kỹ thuật, tên thẻ người-đọc, bỏ tìm kiếm, breadcrumb, nhãn kế hoạch — 11/06: sửa thêm bẫy phông SemiBold (chữ đậm toàn trang), bỏ breadcrumb + kicker, sửa thead che dòngđã chạy thật · 11/06
Đợt 2 · Tốc độ + máy chủCache đọc Sheet, báo cáo chạy nền (report_index), 2 worker + giữ máy ấmđã chạy thật · 11/06
Đợt 3 · Phân quyềnBảng người dùng, chặn theo mô-đun (fail-closed, đã kiểm), trang quản trị người dùngđã bật · 11/06 (admin@ + finance@, fail-closed đã kiểm trên prod)
Đợt 4 · Mã, chương trình, OCR, nguồnUV/HS, ghi_danh, CETB→NBTL, tách trường, khớp danh bạ, dòng thời gian nguồnđã chạy thật · 11/06
Đợt 4 · Báo cáo + thứ tự học bạ + báo cáo trường/lứaSắp học bạ (Cả năm trên, năm mới trên cùng), thẻ học bạ theo năm, sinh báo cáo theo trường & theo lứa (đã hoà giải)đã merge & deploy · 11/06
Đợt 4 · Sổ tay (Google Doc)Bộ dựng sổ tay + bộ nối Google Drive thật (md → .docx → Google Doc gốc)đã merge & deploy · 11/06
Đợt 4 · Đổi tên band DriveCARE_ROOT2_Hoc-sinh (giữ nguyên bucket mirror); cập nhật quy ước đặt tênđã merge & deploy · 11/06
Mục 7 · Danh sách ~1000 emNhóm gập được (Trường/Chương trình/Lứa/Trạng thái) + phân trang phía máy chủđã merge & deploy · 11/06
Đợt 4 · Backfill ~142 emCấp mã, ghi_danh, sinh sổ tay hàng loạt — chạy thử (dry-run) xongđã chạy thật · 11/06 (142 mã HS + 142 ghi danh + 142 sổ tay Google Doc; sao lưu kép)
11/06 · Báo cáo = Google DocTạo báo cáo (cá nhân/trường/lứa) thành Google Doc gốc trên Drive, tên có ngày, cùng ngày tạo lại → "bản 2/3…"; bỏ chọn đối tượng đọc; chạy nềnđã chạy thật · 11/06

Còn lại — chỉ là các bước vận hành (cần bạn)

  1. Merge 5 PR (#35–#39) vào website-redesign. Đều xanh; gộp lại để sẵn sàng deploy. (Việc git, đảo ngược được.)
  2. Đưa lên máy chủ (deploy). Một lần deploy Cloud Run để toàn bộ phần đã merge hoạt động thật. Cần bạn cho phép theo từng lần (workspace-ethics §2).
  3. Bật phân quyền. Khung đã có & đã kiểm fail-closed; cần danh sách người dùng (tên + mô-đun care/content/finance mỗi người) để tạo + bật.
  4. Nạp dữ liệu thật (có giám sát). Chạy backfill --apply cho ~142 em (cấp mã, ghi_danh, sinh sổ tay) + xoá dữ liệu demo. Sao lưu kho chăm sóc trước, làm từng bước — đây là bản sao duy nhất của dữ liệu trẻ em.
  5. Theo sau (cần phối hợp): đổi SURVEY_ROOT (11_→2_Hoc-sinh/Khao-sat, phải đổi cùng Apps Script khảo sát), band nhà tài trợ 08_/02_, và build_drive_tree.py.

1Thiết kế, bố cục, phông & cỡ chữ

Mục tiêu: gọn, thoáng, bớt chữ in đậm, trông chuyên nghiệp như một tổ chức quốc tế. Hub hiện dùng phông serif Playfair Display cỡ lớn và in đậm cho mọi tiêu đề, cộng nhiều chữ đậm trong thân — tạo cảm giác "nặng" và "căng".

Vấn đề quan sát được

Đề xuất (chỉ sửa hmt.css + base.html, không đổi logic)

Hệ phông & thang cỡ

  • Serif chỉ dùng cho H1/H2, hạ font-weight từ 700 → 600, giảm cỡ H1 ~34px → 26–28px.
  • Thân bài: phông sans hệ thống, cỡ 15–16px, line-height 1.6.
  • Thang cỡ rõ ràng: 28 / 20 / 16 / 14 / 12.5 — bỏ các cỡ trung gian tuỳ tiện.
  • Chữ đậm chỉ cho nhãn & con số, không cho câu văn.

Khoảng cách & thành phần

  • Tăng khoảng trắng quanh khối; đường kẻ bảng mảnh hơn (1px #ececec).
  • Chip trạng thái nhỏ lại (11–12px), bớt nền đậm.
  • Tiêu đề trang chỉ xuất hiện một lần; breadcrumb mảnh, không lặp.
  • Lưới nội dung tối đa ~1040px để dòng không quá dài trên màn rộng.
Đây là "đợt làm trước" — toàn bộ là CSS & template. Cần xem-bằng-mắt sau khi sửa (theo ghi nhớ design-system-source: CSS đã có, việc còn lại là markup + render rồi soi).

2Giao diện tiếng Việt toàn phần

Rà toàn bộ template, dọn hết chữ Anh còn sót, dùng tiếng Việt chuẩn. Danh sách dưới đây là đầy đủ các điểm tìm thấy (kèm vị trí tệp), để thi công một lượt.

Vị tríHiện tạiSửa thành
cohort.html (trạng thái)withdrawn / paused / graduatedĐã rời chương trình / Tạm dừng / Đã hoàn thành
cohort.html, child.htmlcung-em-tien-buocCùng em tiến bước
review/queue.html"Khi pipeline đẩy bài tới""Khi hệ thống đẩy bài tới"
hub/plan.html"…tiến trình soạn bài (pipeline)""…tiến trình soạn bài"
hub/plan.htmlagent content-strategist(bỏ — không nêu tên agent)
hub/trip_detail.html"(cull → chỉnh ảnh → viết caption → QC)""(chọn ảnh → chỉnh ảnh → viết bài → kiểm chất lượng)"
hub/trip_detail.html"…đưa vào prompt khi soạn caption""…được dùng khi soạn bài"
child.html"(PDF)""(tệp PDF)"
báo cáo / thẻ trạng tháidraft / in-review / approvedBản nháp / Đang xem / Đã duyệt

Cách làm bền vững: tập trung mọi nhãn trạng thái & tên chương trình vào một bộ ánh xạ tiếng Việt (một dict trong report_common.py đã có sẵn nhãn petal/status — mở rộng nó), để template gọi chung thay vì rải chuỗi khắp nơi. Khi đó "dọn tiếng Anh" không bị tái diễn mỗi lần thêm trang.

3Trình bày tên & liên kết — ẩn vận hành kỹ thuật

Nguyên tắc: người dùng không bao giờ thấy chi tiết kỹ thuật — không tên tệp/script, không slug, không mã nội bộ, không thuật ngữ giai đoạn. Trang đọc-để-hiểu, không phải log hệ thống.

Các rò rỉ kỹ thuật phải bỏ (đã xác định vị trí)

Vị tríĐang lộThay bằng
hub/survey_detail.html"Quyết định xét chọn (admit/reject) là thao tác CLI thủ công (survey_intake.py)… (Giai đoạn A)""Việc xét chọn được thực hiện bởi quản trị viên. Trang này để xem hồ sơ."
survey, childjourney-0021 hiện trên mànMã học sinh thân thiện (mục 7) — hoặc ẩn hẳn
hub/survey.html"Chưa cấu hình sổ khảo sát (so-khao-sat.sqlite)""Chưa có dữ liệu khảo sát."
hub/presence.htmlposted_id, registry.post, event_slugTên hoạt động dễ đọc; bỏ tham chiếu schema
trip_detail.htmlbrief.yaml, ingest-watch, /approve"phiếu thông tin", "hệ thống tự tạo", "cần duyệt trước khi đăng"
review/queue.html"Nguồn dữ liệu: {{ posts_root }}" (đường dẫn ổ đĩa)(bỏ dòng này)
nhiều nơi"Giai đoạn A" / "Stream-A/B"(bỏ — đây là thuật ngữ nội bộ)

Tên thẻ bài đợi duyệt — phản hồi tối nay

Hiện tại

Thẻ bài hiển thị slug máy làm tiêu đề:

2026-05-26__khac-ghi-ro-o-phan-ghi-chu-thcs-tan-khanh

Chip "G8" khó hiểu.

Đề xuất

Tiêu đề người-đọc dựng từ phiếu thông tin:

"Ghi chú lớp học — THCS Tân Khánh" · 26/05/2026 · 5 ảnh

Slug thu nhỏ, mờ, ở dòng phụ. Chip cánh-hoa ghi rõ "Cánh 8 · Lan toả yêu thương".

Khi phiếu thông tin chưa có tiêu đề, dùng quy tắc dựng tên: tên sự kiện (nếu có) → hoặc "Hoạt động tại [trường]" → ngày. Không bao giờ phơi slug làm tiêu đề.

4Phân quyền theo 3 mô-đun

Hiện tại đăng nhập là một mật khẩu dùng chung, được tất-cả-hoặc-không (care_auth.py: PBKDF2 + cookie ký HMAC; chưa có bảng người dùng, chưa có vai trò). Định tuyến đã chia sẵn theo tiền tố đường dẫn, nên việc chặn theo mô-đun là khả thi, gọn.

Ba mô-đun đề xuất

Mô-đunBao gồm (tiền tố)Nội dung
Chăm sóc & khảo sát/care/*, /surveyHồ sơ học sinh, ứng viên, phúc lợi, báo cáo, an toàn
Hoạt động & nội dung/trips, /plan, /presence, /review/*Hoạt động, kế hoạch nội dung, bài đợi duyệt
Tài chính & nhà tài trợ/finance/*Nhà tài trợ, đóng góp, hiện vật, báo cáo tháng

Mô hình thiết kế (giữ tối giản — đúng kỷ luật "không trừu tượng hoá sớm")

Lưu ý an toàn: middleware phân quyền phải fail-closed — không có mô-đun nào ⇒ chặn, không phải mở. (Ghi nhớ review-depth-auth-failpath: lần trước cổng auth từng fail-OPEN; phải thử đường thất bại, không tin "đã đóng" trên giấy.)

5Vì sao chậm — nguyên nhân, lựa chọn & chi phí

Nguyên nhân (theo mức tác động)

#Nguyên nhânTác động
1Một worker duy nhất (uvicorn --workers 1 trong Dockerfile). Mọi yêu cầu xếp hàng nối đuôi.Một việc nặng làm cả hub đứng với mọi người
2Gọi Opus đồng bộ ngay trong handler — tạo báo cáo 3–5s, trích học bạ 2–5s — khoá worker suốt thời gian chờ.Đây là lý do "tạo báo cáo làm hub treo" (≈ chạm mốc 600s)
3Đọc Google Sheet mỗi lần vào //trips (0.5–2s, qua mạng, không cache).Trang chủ & hoạt động chậm rõ
4Khởi động nguội (min-instances=0) → yêu cầu đầu 5–10s."Lần đầu vào rất lâu"
5gcsfuse — DB nằm trên bucket gắn qua gcsfuse, mỗi thao tác tệp +10–50ms, nhân với nhiều truy vấn/trang.Cộng dồn, không lớn
6Mở kết nối SQLite + PRAGMA mỗi yêu cầu; danh sách báo cáo quét đĩa thay vì đọc chỉ mục.Nhỏ

Lựa chọn & chi phí

Giải phápSửa gìHiệu quảChi phí thêm/tháng
Giữ máy luôn ấmmin-instances=1, CPU cấp-thường-trựcHết khởi động nguội (#4)≈ 8–15 USD
Tăng worker/đồng thời--workers 2 + nhỉnh RAM/CPUHết "cả hub đứng" (#1)≈ 3–6 USD
Báo cáo chạy nền (mục 8)Đưa việc tạo báo cáo ra Cloud Run JobWorker không bị khoá (#2)≈ vài cent/lần
Cache đọc SheetCache trong-tiến-trình TTL 60s, hoặc đọc từ sổ đăng ký nội bộTrang chủ nhanh (#3)0
Chỉ mục báo cáoLiệt kê từ một bảng nhỏ, chỉ tạo lại khi dữ liệu đổiTrang báo cáo nhẹ (#6)0

Gói khuyến nghị (rẻ → tác động lớn)

  1. min-instances=1 + CPU thường trực + --workers 2~12–18 USD/tháng, xoá hai cảm giác chậm rõ nhất (nguội + đứng).
  2. Báo cáo chạy nền (mục 8) — gần như miễn phí, hub không bao giờ treo vì báo cáo.
  3. Cache đọc Sheet 60s + chỉ mục báo cáo — miễn phí.

Cảnh báo kỹ thuật: nhiều worker + SQLite ghi qua gcsfuse có rủi ro tranh-ghi (ghi nhớ gcsfuse-db-write-race). Hub chủ yếu đọc nên 2 worker đọc thì an toàn; mọi ghi (thêm ghi chú, phúc lợi) cần đi qua một đường ghi có khoá/chốt-WAL như hiện nay.

6Tìm kiếm — có cần không?

Ô tìm kiếm trên thanh đầu (topbar__search) hiện bị tắt, ghi "sắp có" — một tính năng chưa xây, đang chiếm chỗ và lộ ý "đang dở".

Khuyến nghị: bỏ hẳn ô tìm kiếm toàn cục. Lý do:

Nếu sau này muốn tìm-nhanh-một-học-sinh xuyên mô-đun, sẽ làm như một hộp "Tra cứu học sinh" trong mô-đun Chăm sóc (theo mã hoặc tên) — không phải ô toàn cục trên thanh đầu.

7Quản lý học sinh có cấu trúc

Đây là phần nền tảng và là gốc của các "lệch dữ liệu" trong báo cáo. Hiện mọi thứ móc vào journey-NNNN (cấp theo block từng trường, vô nghĩa với người đọc, lộ trên màn). Đề xuất một mô hình danh tính rõ ràng, ổn định, truy nguồn được.

Hình dung tổng thể: mỗi em một "Sổ tay học sinh"

Mỗi học sinh có một cuốn sổ tay mở ra từ lúc khảo sát và theo em suốt đời tham gia. Mọi việc về em đều thành một trang có ngày tháng trong sổ, và mỗi trang ghi rõ lấy từ đâu: thông tin khảo sát, học bạ & điểm, bài viết "Tôi đã thay đổi", đánh giá cảm xúc-xã hội, biên bản gặp gỡ 3 bên (mỗi năm), giải thưởng, và các ghi chú khác. Khi tạo báo cáo, hệ thống đọc toàn bộ cuốn sổ (mọi năm) chứ không chỉ một lát cắt; vì mỗi dữ kiện đều có trang nguồn nên báo cáo kiểm chứng được. "Sổ tay" chính là hồ sơ học sinh ở 7.1–7.5; bản Google Doc của nó nằm trên Drive (7.3), mở trên trình duyệt để xem ngay, không cần tải về.

7.1 · Hai loại mã, hai vòng đời

Mã ứng viên — UVYYYYNNNN

Ví dụ UV2026-0001 = Ứng viên · năm 2026 · số 0001. Cấp khi nộp phiếu khảo sát. Mọi thông tin & tài liệu của ứng viên được gom vào hồ sơ này.

Thay cho ung-vien-NNNN hiện nay (đã có sổ khảo sát riêng — chỉ đổi cách cấp mã + thêm hồ sơ tài liệu).

Mã học sinh — HSYYYYNNNN

Ví dụ HS2026-0001 = Học sinh · lứa vào Quỹ 2026 · số 0001. Cấp một lần khi ứng viên được nhận, và theo em suốt đời tham gia — không đổi khi chuyển trường, lên cấp, hay chuyển chương trình.

Đã chốt: năm trong mã = lứa vào Quỹ (= lứa của lượt CETB đầu tiên). Mã người giữ nguyên năm này mãi; lượt NBTL về sau mang lứa riêng nhưng tại thời điểm vào Quỹ hai con số trùng nhau.

Đây là mã thay journey-NNNN trên giao diện. (Kỹ thuật: giữ journey-NNNN làm khoá nội bộ bất biến, thêm cột student_code hiển thị — không phải di trú phá vỡ.)

7.2 · Hai chương trình, tách rõ "người" và "lượt tham gia"

Mấu chốt để dữ liệu nhất quán: mã học sinh là cấp-người (ổn định), còn chương trình là một "lượt ghi danh" (có thể nhiều lượt theo thời gian).

Chương trìnhGiai đoạn
Cùng em tiến bước (CETB)Bậc phổ thông (THCS + THPT, lớp 6–12)cung-em-tien-buoc → hiện "Cùng em tiến bước"
Nâng bước tương lai (NBTL)Sau phổ thông (nghề / đại học)nang-buoc-tuong-lai → hiện "Nâng bước tương lai"

Bảng ghi_danh (enrollment) — mới

student_code, chuong_trinh ∈ {CETB, NBTL}, truong, lua (cohort), bat_dau, ket_thuc, trang_thai ∈ {đang-học, hoàn-thành, tạm-dừng, đã-rời, chuyển-tiếp}, ly_do_roi

  • Một học sinh có ≥1 lượt ghi danh. "Lứa" gắn theo lượt, không phải theo người.
  • Em học CETB xong lớp 12 và học tiếp nghề/đại học ⇒ mở lượt NBTL mới, cùng mã học sinh (đáp ứng 7d).
  • Em nghỉ học / chuyển sang trường ngoài chương trình ⇒ lượt đó đã-rời/chuyển-tiếp; mã học sinh giữ nguyên để nếu quay lại thì lịch sử còn nguyên vẹn.
Vì sao tách: hiện programme_slugs là chuỗi nối bằng dấu phẩy trên hàng child + lịch sử nằm trong YAML hồ sơ — khó truy vấn, dễ lệch. Tách thành bảng ghi_danh cho phép báo cáo theo chương trình, theo lứa, theo trường chính xác và xử lý đúng việc chuyển CETB→NBTL.

Hành động chuyển giai đoạn — dựng riêng cho từng bước (phản hồi tối nay)

Form "Chuyển giai đoạn / tốt nghiệp" hiện là một dropdown chung, các ô không đổi theo hành động. Đề xuất các hành động chuyên biệt, mỗi hành động chỉ hỏi đúng các ô cần thiết và làm đúng việc nền:

Hành độngHỏi gìHệ thống làm gì
Tốt nghiệp CETB → học tiếp (NBTL)Ngày tốt nghiệp · Nơi học tiếp (ĐH/CĐ/nghề) · Bậc mớiĐóng lượt CETB (hoàn-thành) + tự mở lượt NBTL mới cùng mã học sinh
Tốt nghiệp — kết thúcNgày tốt nghiệpĐóng lượt (hoàn-thành), không mở lượt mới
Nghỉ họcNgày · Lý doLượt → đã-rời, ghi lý do; giữ mã & lịch sử
Chuyển trường ngoài chương trìnhNgày · Trường chuyển đếnLượt → chuyển-tiếp; giữ mã
Tạm dừng / Quay lạiNgày · Lý doLượt → tạm-dừngđang-học

Sửa kèm: định dạng ngày mm/dd/yyyydd/mm/yyyy; ô "Người ghi" mặc định coordinator → tên người dùng đang đăng nhập (không để chữ Anh thô).

7.3 · Sổ tay trên Drive (7f) — bản Google Doc + tài liệu nguồn có tổ chức

Cập nhật cấu trúc Drive (06/2026): Shared Drive đã được tinh gọn — bỏ tiền tố 10_/11_. Band học sinh nay là 2_Hoc-sinh/ (cùng cấp: 1_Hoat-dong-truyen-thong, 3_Nha-tai-tro, 4_Tai-chinh, 5_Quan-tri-thuong-hieu, 9_Tam). Kế hoạch này dùng cây mới. Việc kỹ thuật trong Đợt 4: code & tài liệu nội bộ còn trỏ tên cũ (CARE_ROOT="10_Ho-so-thu-huong", naming-convention) — cần cập nhật sang band mới.

Thư mục của mỗi em đặt tên theo mã kèm tên em — vd. HS2026-0001 — Nguyễn Văn A: mã đứng trước để sắp xếp & tra cứu ổn định, tên theo sau cho dễ tìm bằng mắt trên Drive. Mã là khoá tra cứu; nếu tên đổi thì đổi tên thư mục, mã giữ nguyên. Khi ứng viên được nhận, tài liệu khảo sát (ở Khao-sat/<mã ứng viên> — Tên/) chuyển tiếp vào sổ tay học sinh để cuốn sổ liền mạch từ lúc khảo sát. Điểm vào là cuốn sổ tay:

2_Hoc-sinh/

  • Ho-so/<mã> — <Tên>/ (vd. HS2026-0001 — Nguyễn Văn A/) — chứa 📄 Sổ tay học sinh — Nguyễn Văn A (HS2026-0001) (Google Doc, mở trên trình duyệt xem ngay) + hồ sơ hợp nhất của em. Đây là bản review chính.
  • Tai-lieu-vao/ — bản gốc/quét đưa vào (gắn mã học sinh). Chỉ-đọc sau khi nạp.
  • Tai-lieu-so-hoa-OCR/ — bản OCR đọc được (Google Doc, gắn mã học sinh).
  • Bao-cao/ — báo cáo theo dịp (đặt tên tiếng Việt, xem 7.6).
  • Khao-sat/<mã ứng viên>/ — hồ sơ + tài liệu ứng viên (thay 11_Khao-sat cũ).
  • So-phuc-loi/, Danh-sach-an-toan/, Chi-muc/ — sẵn có trong band.

Tài liệu quét (vd. bài viết của em) — 3 bản liên kết với nhau

  1. Bản máy đọc (nội bộ): một tệp .txt/.md văn bản OCR để hệ thống lưu; văn bản này đồng thời vào thẳng dòng nhật ký của em để báo cáo dùng được.
  2. Trên Drive — Google Doc đã chuyển + ảnh quét gốc, để cạnh nhau: người review đọc Doc và liếc bản gốc để đối chiếu. Bản gốc chỉ-đọc.
  3. Một dòng nhật ký nối ba thứ (hệ thống ↔ Google Doc ↔ ảnh gốc) để truy nguồn một-bấm.

Nguyên tắc: chép đúng nguyên văn (nhất là bài viết của em — lời của chính em), không diễn giải; OCR độ tin thấp thì đánh dấu "cần người kiểm", không tin bản quét mờ. ("Máy đọc được", không phải dịch ngôn ngữ.)

Cách dựng & cập nhật sổ tay (Google Doc)

  • Định dạng đã chốt: Google Doc (không Word/PDF) — mở ngay trên trình duyệt, không tải về, dễ xem & ghi chú, luôn truy cập được với người có quyền vào thư mục.
  • Giữ diện mạo có thương hiệu (logo, mục đánh số, bảng điểm): hệ thống dựng tài liệu có style rồi tải lên kèm chuyển đổi sang Google Doc gốc (mimetype application/vnd.google-apps.document), nên Doc giữ được bố cục — không phải văn bản trơn.
  • Nút "Cập nhật sổ tay" dựng lại Doc với dữ liệu mới nhất và ghi đè đúng tệp cũ (idempotent theo mã học sinh); chạy ở nền (việc nền ở Đợt 2) để hub không bị treo.
  • Bản review, không phải bản gốc: dữ liệu sửa trong hệ thống; Doc là ảnh chụp để đọc/chia sẻ — không sửa Doc rồi nạp ngược lại (tránh lệch hai bản). Đúng tinh thần "Drive là bản ghi, hệ thống là nguồn gốc".
  • Tận dụng sẵn: hệ thống đã ghi Google Doc gốc vào band học sinh trên Shared Drive (các tài liệu OCR đã lưu kiểu này) — chỉ thêm tệp sổ tay cho từng em. Người review cần quyền vào thư mục hồ sơ (quyền chặt hơn vì là dữ liệu trẻ em).

7.4 · Công cụ OCR (7g) — quét → máy đọc được

Tận dụng đúng khuôn đã có: school_doc_extract.py đã dùng Opus-thị-giác đọc PDF học bạ/sổ điểm và trả dữ liệu có cấu trúc (Pydantic, messages.parse). Mở rộng thành công cụ chung tools/ocr_intake.py:

Khi nạp học bạ / phiếu điểm — làm gì & lưu ở đâu

  1. Tải lên bản quét học bạ/phiếu điểm (PDF hoặc ảnh) trên trang của em.
  2. Trích bằng Opus-thị-giác → các trường tách bạch: năm học · kỳ (HK I/II) hay cả năm · lớp · điểm từng môn · ĐTB · hạnh kiểm · chuyên cần. Không dồn cục.
  3. Màn rà soát (đã có): trường trích hiện cạnh bản quét gốc; người dùng xác nhận/sửa; ô độ-tin-thấp được đánh dấu.
  4. Khi xác nhận, lưu vào 4 nơi, nối với nhau:
    • Điểm số → hệ thống (bảng school_report) — thứ báo cáo đọc.
    • Một trang nhật ký → sổ tay của em (profile_entry), ghi nguồn.
    • Bản quét gốc → Drive 2_Hoc-sinh/Tai-lieu-vao/<mã>/ (chỉ-đọc).
    • Google Doc đọc được → Drive 2_Hoc-sinh/Tai-lieu-so-hoa-OCR/<mã>/.
  5. Hiện ra ở mục học tập trong sổ tay + trang của em, sắp theo 7.8 (Cả năm trên, học kỳ dưới; năm mới trên cùng).
  6. Phiếu điểm cả lớp (nhiều em): khớp danh bạ → tách → định tuyến dòng của từng em về đúng sổ tay (xem 7.5).

Phần lớn luồng này đã có (route school-doc + màn rà soát); việc mới ở Đợt 4 là tách-trường chặt hơn, lưu bản gốc + Google Doc về band 2_Hoc-sinh/ theo mã, và nối nguồn một-bấm.

Tách trường dữ liệu — KHÔNG dồn cục (phản hồi tối nay)

Vấn đề bạn chỉ: nhiều dữ kiện bị dồn vào một ô — tên, số điện thoại, quan hệ của người chăm sóc nằm chung một chuỗi khi một báo cáo học sinh được nạp. Đây là một gốc của "lệch dữ liệu".

Nguyên tắc trích: engine phải suy luận và tách mỗi dữ kiện về đúng trường riêng, không bê nguyên cụm:

  • "Nguyễn Thị B · 09xx xxx xxx · mẹ" (ví dụ minh hoạ) ⇒ ten_nguoi_cham_soc, sdt, quan_he — ba trường tách bạch.
  • Mỗi trường có kiểu & ràng buộc (SĐT là chuỗi số, ngày là ISO, quan hệ trong tập giá trị biết trước).
  • Khi engine không chắc, đánh dấu "cần xác nhận" thay vì đoán bừa rồi dồn cục — đưa vào màn rà soát song song (đã có cho học bạ) để người sửa trước khi ghi.
  • Dữ liệu thô gốc vẫn lưu (trong payload + tài liệu nguồn) để truy nguồn, nhưng các trường đã tách mới là thứ báo cáo dùng.

7.5 · Tài liệu cho nhiều học sinh (7h)

Engine trích đã hỗ trợ trả danh sách bản ghi (mỗi em một bản, đè journey_id riêng). Bổ sung bước khớp tên với danh bạ: nâng "bộ khớp tên kép" từ script backfill thành tools/roster_match.py dùng lại được. Một tài liệu phủ nhiều em ⇒ tách, định tuyến phần dữ liệu liên quan về đúng hồ sơ từng em.

7.5 · Nhật ký & truy nguồn (7i) — phần quan trọng nhất cho "hết lệch dữ liệu"

Bảng profile_entry đã là nhật ký theo em: có entry_date, source, by_person, petal, summary_line, detail, raw_link. Cần siết và phơi ra:

Đây chính là cơ chế trả lời "chi tiết X trong báo cáo lấy từ đâu" — không cần đoán, bấm là thấy nguồn.

7.6 · Đặt tên báo cáo dễ hiểu bằng tiếng Việt (7j)

Hiện tại

2026-06-10__child__journey-0006.md

Máy đọc, lộ mã nội bộ.

Đề xuất — tiêu đề hiển thị tiếng Việt

"Báo cáo cá nhân — Nguyễn Văn A (HS2026-0001) — 11/06/2026"

"Báo cáo theo trường — THCS Tam Thanh — 11/06/2026"

"Báo cáo chương trình — Cùng em tiến bước · Lứa 2023"

(Tên tệp vẫn ASCII theo quy ước; tiêu đề hiển thị mới là thứ người dùng thấy.)

7.7 · Thiết kế & thương hiệu cho báo cáo (phản hồi tối nay)

Áp dụng cho cả ba loại báo cáo (cá nhân · theo trường · theo chương trình), ở cả bản xem trong hub (HTML) và bản tải về (.docx):

Đầu trang & thương hiệu

  • Logo Hoa Mặt Trời + "Quỹ Hoa Mặt Trời" ở đầu mỗi báo cáo.
  • Khối tiêu đề: tên báo cáo, đối tượng (tên + mã), kỳ báo cáo, ngày lập.
  • Chân trang: số trang, "Tài liệu nội bộ", ngày tạo.

Bố cục & kiểu chữ

  • Đánh số mục rõ ràng: 1, 2, 3… với phân cấp 1.1, 1.2.
  • Phông & màu thương hiệu tiết chế (serif cho tiêu đề mục, sans cho thân).
  • Danh sách gạch đầu dòng / đánh số gọn; bảng cho điểm số, phúc lợi, mốc thời gian.
  • Khoảng trắng đều, không "tường chữ".

Cách làm: một mẫu báo cáo dùng chung — bộ style .docx có sẵn header/footer + logo cho markdown_to_docx, và một CSS báo cáo dùng chung cho bản HTML — để cả ba loại báo cáo đồng bộ một diện mạo, sửa một nơi áp dụng mọi nơi.

7.8 · Thứ tự hiển thị học bạ & tiến triển điểm (phản hồi tối nay)

Trang hồ sơ hiện sắp xếp học bạ chưa nhất quán (học kỳ I đang nằm trên kết quả cả năm của cùng năm học). Quy tắc đề xuất:

  • Trong một năm học: "Cả năm" hiện trên, các "Học kỳ" (HK I, HK II) hiện dưới.
  • Giữa các năm: năm mới nhất trên cùng, năm cũ nhất dưới cùng (mới → cũ) cho các thẻ chi tiết.
  • Bảng tiến triển ĐTB/điểm: sắp theo dòng thời gian xa → gần (cũ trên, mới dưới) để đọc được "đường đi lên" qua các năm — đây là khung nhìn để xem tiến bộ của em, tách khỏi các thẻ chi tiết.

Đã có cột "Năm học" trong bảng xu hướng học bạ (commit gần đây); việc còn lại là chốt thứ tự sắp xếp cả-năm-trên-học-kỳ và đảo chiều cho hai khung nhìn.

7.9 · Backfill (7a, 7e) — đưa dữ liệu cũ vào khuôn mới

  1. Cấp HSYYYYNNNN cho ~142 em hiện có (từ năm vào Quỹ + số thứ tự), giữ journey-NNNN nội bộ.
  2. Chuyển programme_slugs + trạng thái hiện tại → mỗi em một (hoặc nhiều) dòng ghi_danh.
  3. Tạo hồi tố hồ sơ ứng viên UV… cho các em có dữ liệu khảo sát; phần thiếu đánh dấu "không rõ giai đoạn ứng viên".
  4. Gắn tài liệu OCR đã có (đang ở Tai-lieu-so-hoa-OCR/) về đúng thư mục từng em, kèm dòng nhật ký nguồn.
  5. Tạo sổ tay cho tất cả ~142 em hiện có (không chỉ em mới): tạo thư mục 2_Hoc-sinh/Ho-so/<mã>/ cho từng em rồi dựng Google Doc sổ tay từ dữ liệu đã có (hồ sơ + nhật ký + điểm + phúc lợi). Chạy hàng loạt một lần, sau đó nút "Cập nhật sổ tay" giữ cho mới.

Lưu ý vận hành: mọi nạp dữ liệu vào bucket chăm sóc phải chạy một lần một, chốt WAL trước khi tải lên, và làm yên hub trước (ghi nhớ gcsfuse-db-write-race + va chạm đa-agent). Dữ liệu thật là bản sao duy nhất — sao lưu trước mỗi lần đụng.

8Báo cáo chạy nhanh / chạy nền

Hiện tạo báo cáo là đồng bộ trong handler ⇒ khoá worker 3–5s (đôi khi 2 lần gọi Opus), hub đứng với mọi người.

Đề xuất: tách việc tạo báo cáo ra chạy nền

Kết hợp với gói tốc độ ở mục 5 (giữ máy ấm + 2 worker), trải nghiệm sẽ "tức thì khi xem, không đứng khi tạo".

Danh sách lỗi giao diện từ buổi soi tối nay

Tổng hợp các điểm bạn chỉ trực tiếp, ánh xạ về hạng mục & đợt thi công.

Quan sátSửaThuộc
Breadcrumb "… › Bài đợi duyệt › Hàng chờ" trùng nghĩaGộp còn "Trung tâm điều hành › Bài đợi duyệt"Đợt 1 · mục 1
Thẻ bài lấy slug làm tiêu đề (2026-05-26__khac-ghi-…)Tiêu đề người-đọc; slug mờ/ẩn; chip "G8" → "Cánh 8"Đợt 1 · mục 3
Danh sách học sinh: withdrawn, cung-em-tien-buoc, journey-0021 lộ raViệt hoá trạng thái; tên chương trình đầy đủ; mã học sinh mới/ẩnĐợt 1 + Đợt 4 · mục 2,3,7
"Danh sách em" lặp 3 lần trên trangTiêu đề chỉ một lần; breadcrumb mảnhĐợt 1 · mục 1
140 học sinh đã cuộn dài; sắp tới ~1000Nhóm gập được theo trường/chương trình/lứa + chọn cách nhóm + phân trang phía máy chủ (xem dưới)Đợt 4 · mục 7
"Chốt ô" khó hiểu; cột "B"/số trống nghĩa; lộ "Giai đoạn A"/"Stream-B"/slugĐổi nhãn quy trình + đặt tiêu đề cột (xem dưới)Đợt 1 · mục 1,3
Trang hồ sơ em: dòng "Chương trình: cung-em-tien-buoc, lứa 2025. Vào chương trình: 2025-09-05" dồn cục, slug thô, ngày thôTách dòng: "Cùng em tiến bước" · "Lứa 2025" · "Vào chương trình: 05/09/2025"; mã journey-0043 → mã học sinh mới/ẩnĐợt 1 + Đợt 4 · mục 3,7
Báo cáo nạp vào dồn tên + SĐT + quan hệ chung một ôEngine suy luận & tách từng trường; ô nghi ngờ "cần xác nhận" (xem 7.5)Đợt 4 · mục 7
Ô tìm kiếm toàn cục "sắp có" (đang tắt)Bỏ hẳnĐợt 1 · mục 6
Tiêu đề serif quá to/đậm khắp nơiHạ cỡ & độ đậm; bớt chữ đậm thân bàiĐợt 1 · mục 1

Danh sách học sinh quy mô ~1000 (phản hồi tối nay)

  • Bộ chọn cách nhóm: Trường · Chương trình · Lứa · Trạng thái.
  • Nhóm gập được, mặc định gập, hiện số đếm mỗi nhóm (vd. "THCS Tam Thanh · 38 em").
  • Phân trang phía máy chủ (LIMIT/keyset) để trang nhẹ bất kể danh bạ lớn.
  • Lọc hiện có (tên/trường/trạng thái/chương trình/lớp) giữ nguyên, chạy cùng nhóm.

Quy trình kế hoạch nội dung — đặt lại nhãn

Hiện tại

Ý tưởng → Chốt ô → Yêu cầu viết bài

Cột: "B", số trống nghĩa; lộ slug + "Giai đoạn A".

Đề xuất

Ý tưởng → Đưa vào kế hoạchSoạn bài

Cột rõ: "Luồng", "Tháng", "Chủ đề"; bỏ slug + thuật ngữ giai đoạn.

"Đưa vào kế hoạch" = xác nhận ý tưởng đủ chín để soạn; ô chưa xác nhận thì chưa soạn được. Giữ ý nghĩa, bỏ tiếng lóng.

Thứ tự thi công & ước lượng

ĐợtNội dungĐụng tớiƯớc lượngRủi ro
1 đã xongGiao diện + dọn tiếng Anh + ẩn kỹ thuật + tên thẻ/breadcrumb + bỏ tìm kiếm + nhãn kế hoạchhmt.css, các template1–2 ngàyThấp (chỉ trình bày)
2 code xongMáy luôn ấm + 2 worker + cache Sheet + báo cáo chạy nềnDockerfile, deploy.ps1, handler báo cáo1 ngàyCòn: deploy
3 code xongPhân quyền 3 mô-đun + trang quản lý người dùngcare_auth.py, middleware hub.py, template nav2–3 ngàyCòn: tạo người dùng + bật
4 code xongMã UV/HS, bảng ghi_danh, hồ sơ Drive, OCR chung, khớp danh bạ, nhật ký nguồn, đặt tên báo cáo, backfillSổ đăng ký chăm sóc, survey_intake.py, tool mới, template care4–6 ngàyCòn: Drive thật + nạp dữ liệu thật

Hai hạng mục trong kế hoạch chưa thành task riêng & vẫn còn: nhóm/gập danh sách ~1000 em (bộ chọn nhóm + phân trang) và backfill hồ sơ ứng viên UV hồi tố. Báo cáo theo trường/lứa đã có (đang ở PR #35, chờ hoà giải).

Quyết định của quản trị viên (chốt 11/06/2026)

  • đã chốt Định dạng mã: UV2026-0001 / HS2026-0001.
  • đã chốt Năm trong mã học sinh = lứa vào Quỹ (trùng lứa của lượt CETB đầu tiên; mã người giữ nguyên năm này về sau).
  • đã chốt Xoá dữ liệu demo trong bucket thật trước khi cấp mã hàng loạt.
  • để sau Phân quyền: danh sách người dùng & mô-đun sẽ bổ sung sau (cứ dựng khung phân quyền, người dùng cụ thể thêm sau).

Bước tiếp theo cho quản trị viên: (1) duyệt hướng đi tổng thể; (2) trả lời 4 câu quyết định ở trên; (3) cho phép bắt đầu Đợt 1 (giao diện) ngay — đây là phần an toàn nhất và đem lại cảm giác "mới" rõ nhất. Mọi thay đổi nằm trong nhánh claude/website-redesign; không đụng Giai đoạn A (mọi bài vẫn cần duyệt tay, không đăng tự động). · HMT · 11/06/2026