Contest FLEXIBLE: mỗi học viên có đồng hồ đếm ngược riêng

Contest FLEXIBLE có một khoảng thời gian mở. Học viên bấm Start lúc nào cũng được trong khoảng đó, và đồng hồ của riêng họ bắt đầu chạy. Contest FIXED giữ nguyên như cũ.

SP-762 · 29/09/2026

Trạng thái ngày 29/09/2026

Giai đoạn
Đã phát hành. PR đã merge vào main ngày 28/09/2026 và nằm trong tag production v0.0.49 của ngày 28/09/2026. GitHub release của tag này đăng ngày 29/09/2026.
PR
#497 Cả tính năng nằm trong một PR: backend, editor của instructor, các trang của học viên, standings và bản dịch English, Vietnamese. Không có PR nào chồng lên nó.
Ticket
SP-762. Trạng thái Jira ngày 29/09/2026 là Testing.
Sau bản đầu
Trong lúc review, PR có thêm hai bản sửa. Một, trang contest bị mất đăng nhập sau khi reload hoặc mở link trực tiếp. Lỗi này cũng có trên main trước PR. Hai, các ô lịch trong editor bị lệch hàng khi một ô báo lỗi. Các ảnh bị ảnh hưởng đã chụp lại và hai phần của video đã quay lại.
Beta
Chưa có ghi nhận. Mục "Beta walkthrough" trong test plan của PR chưa được tick.
Ảnh và video
Toàn bộ ảnh và video đến từ một local stack với mock data. Tên và số liệu không có thật.

Vì sao cần tính năng này

Với contest FIXED, mọi học viên thi cùng một giờ. Contest kết thúc sau một khoảng cố định kể từ giờ bắt đầu, ví dụ 2 giờ. Ai vào trễ thì mất phần thời gian đó. Ai không rảnh đúng giờ thì không thi được.

Contest FLEXIBLE mở một khoảng thời gian dài hơn, ví dụ từ 06:00 đến 21:00. Mỗi học viên tự chọn lúc bấm Start và nhận đủ thời lượng, ví dụ 2 giờ, tính từ lúc đó.

Thay đổi chính, trong một hình

Bước 1

Chọn mode

Instructor chọn FIXED hoặc FLEXIBLE trong editor, rồi đặt lịch: giờ mở đăng ký, Starts, Ends và Duration.

Bước 2

Học viên bấm Start

Trong khoảng từ Starts đến Ends, học viên bấm Start. Trước đó, problems bị khóa.

Bước 3

Đồng hồ riêng chạy

Server lưu giờ Start. Time left giữ nguyên sau khi reload, đổi thiết bị hoặc đổi múi giờ.

Bước 4

Hết giờ cá nhân

Submit bị khóa. Standings mở khi contest đóng. Hòa điểm thì ai dùng ít thời gian hơn xếp trên.

Video demo

Khoảng 2 phút 10 giây, không có tiếng. Chấm xanh là con trỏ chuột. Video ghép từ ba lần quay, mỗi lần một tài khoản: instructor, học viên Alice và học viên Sarah.

Trong video

  1. 0:00 đến 0:07. Instructor xem danh sách contest. Chuột chỉ vào nhãn FLEXIBLE và FIXED.
  2. 0:08 đến 0:21. Instructor tạo contest mới. Ở card Schedule, mode Fixed có một dòng giải thích. Bấm Flexible thì ô Ends xuất hiện.
  3. 0:22 đến 0:31. Instructor mở contest Autumn Data Challenge 2026. Dòng "Timing is locked" hiện vì đã có học viên bấm Start.
  4. 0:33 đến 0:48. Alice xem danh sách contest có badge Flexible và Registered, rồi mở contest. Nút Start contest hiện và problems bị khóa.
  5. 0:49 đến 1:02. Alice bấm Start. Hộp thoại xác nhận hiện khoảng 3,5 giây, rồi Alice bấm Start now. Time left bắt đầu từ khoảng 01:59:5x và problems hiện ra.
  6. 1:02 đến 1:18. Alice mở problem ESC-50. Header có Time left và Submit đang bật. Khi quay lại contest, Time left là 01:59:38 ở giây thứ 76.
  7. 1:18 đến 1:29. Alice reload trang thật. Màn hình loading hiện ngắn, rồi trang trở lại như cũ. Alice vẫn đăng nhập, Time left chạy tiếp, 01:59:35 ở giây 79 và 01:59:27 ở giây 87, và problems vẫn hiện.
  8. 1:30 đến 1:38. Sarah mở danh sách contest, rồi contest Autumn Data Challenge 2026. Banner "Your time is up" hiện và chỉ còn một nút "Final standings".
  9. 1:38 đến 1:45. Sarah mở problem ESC-50. Submit bị khóa, tooltip ghi "Your time is up."
  10. 1:45 đến 2:10. Contest Summer Practice Round, trang Final standings. Chuột chỉ vào cột Time used: Linh 01:04:00 và An 01:21:00, cùng 170.00. Video dừng ở dòng giải thích cách xử lý hòa điểm.

Lưu ý. Số trên đồng hồ trong video và trong ảnh khác nhau, vì chụp ở các lần khác nhau. Alice bấm Start trong video lúc 01:43 sáng 28/09/2026 giờ VN. Ảnh trước Start, hộp thoại, sau Start và trang problem đến từ lần chụp đầu, khi Alice bấm Start lúc khoảng 01:17 đến 01:19. Hai ảnh sau reload và mở link trực tiếp, cùng phần 2 và 3 của video, đến từ lần chụp sau bản sửa đăng nhập. Phần editor của video, từ 0:00 đến 0:31, đến từ lần chụp đầu. Nó không có trạng thái lỗi, nên không cần quay lại sau bản sửa lịch.

Cách dùng

Dành cho instructor

  1. Chọn Timing khi tạo hoặc sửa contest

    Mở card Schedule. Chọn Fixed hoặc Flexible. Mỗi mode có một dòng giải thích ngay dưới tên.

    Card Schedule của contest mới, Fixed đang chọn, Duration 120 phút, kèm lời giải thích của hai mode
    Card Schedule khi tạo contest mới. Fixed đang chọn và Duration là 120 phút. Fixed: contest kết thúc 2h sau giờ Starts. Flexible: mỗi học viên có 2h kể từ lúc bấm Start.
  2. Đặt lịch cho FLEXIBLE

    Với Flexible, editor có thêm ô Ends. Điền Registration opens, Starts, Ends và Duration. Quy tắc: Registration opens không sau Starts, Starts trước Ends, và Duration phải nằm gọn trong khoảng từ Starts đến Ends. Khoảng đó gọi là window.

    Contest nháp Winter Warm-up ở mode Flexible, Starts 05/10/2026 08:00 AM, Ends 12:00 PM, Duration 90
    Contest nháp Winter Warm-up. Registration opens 01/10/2026 09:00 AM. Window từ 05/10/2026 08:00 AM đến 12:00 PM, Duration 90 phút.
  3. Sửa nếu editor báo lỗi

    Nếu Duration dài hơn window, editor báo lỗi ngay dưới ô Duration và không lưu. Các ô khác vẫn nằm trên cùng một hàng.

    Duration 300 trong window 4 giờ, lỗi đỏ dưới ô Duration
    Winter Warm-up, window 4h từ 08:00 AM đến 12:00 PM, Duration nhập 300 phút. Lỗi đỏ: "The duration must fit between the start and end times."
  4. Xem mode trong danh sách contest

    Trong Contest Management, mode nằm dưới cột Window.

    Bảng Contest Management có 5 contest, mỗi contest ghi FLEXIBLE hoặc FIXED dưới cột Window
    Năm contest. Winter Warm-up là DRAFT và FLEXIBLE. Weekly Sprint #14 là FIXED. Autumn Data Challenge 2026 là FLEXIBLE và có 5 người đăng ký. Summer Practice Round là FLEXIBLE và có 8 người đăng ký. SP-574 QA Private Contest là contest cũ, FIXED.
  5. Theo dõi ai đã bấm Start

    Mở contest, chọn tab Registered. Cột Started at cho biết giờ mỗi học viên bấm Start. Ô hiện "–" nghĩa là chưa Start.

    Tab Registered với cột Started at: Sarah 27/09/2026 10:53 PM, Bob 28/09/2026 12:43 AM, Minh 28/09/2026 01:08 AM, Alice và Linh chưa Start
    Autumn Data Challenge 2026, 5 người đăng ký. Sarah Start lúc 27/09/2026 10:53 PM, Bob lúc 28/09/2026 12:43 AM, Minh lúc 28/09/2026 01:08 AM. Alice và Linh hiện "–", nghĩa là chưa Start.
  6. Lịch bị khóa khi đã có học viên Start

    Sau khi có học viên bấm Start, editor khóa mode, Starts, Ends và Duration, và hiện một dòng nhắc. Registration opens vẫn sửa được.

    Editor của Autumn Data Challenge 2026 với dòng vàng báo lịch bị khóa, các ô lịch bị mờ, Registration opens vẫn sửa được
    Dòng vàng: "Timing is locked because students have already started. You can still change when registration opens." Mode, Starts 27/09/2026 08:00 PM, Ends 28/09/2026 08:00 PM và Duration 120 bị mờ. Registration opens 25/09/2026 09:00 AM vẫn sửa được.

Dành cho học viên

  1. Tìm contest trong danh sách

    Contest FLEXIBLE có badge Flexible và một dòng "Any time between ..." thay cho giờ bắt đầu cố định.

    Danh sách contest của học viên: Autumn Data Challenge 2026 Flexible, Running, Closes in 18:17:00; Weekly Sprint #14 FIXED, 04:17:00 left; Summer Practice Round Flexible, Ended
    Autumn Data Challenge 2026 có badge Flexible và Registered, thời lượng 2h, trạng thái Running, "Closes in 18:17:00". Weekly Sprint #14 không có badge Flexible vì là contest FIXED, còn "04:17:00 left". Summer Practice Round là Flexible, 1h 30m, đã Ended.
  2. Mở contest và chưa bấm Start

    Trang contest có nút Start contest. Problems bị khóa cho đến khi học viên bấm Start.

    Trang contest của Alice trước khi Start: nút Start contest, Closes in 18:42:03, Your time 2h, Problems are locked
    Alice trước khi Start. Nút Start contest ở góc phải, "Closes in 18:42:03", bốn ô ngày giờ, trong đó Your time là 2h. Khung Problems ghi "Problems are locked" và "Problems appear after you press Start."
  3. Bấm Start và xác nhận

    Hộp thoại nhắc rằng đồng hồ không dừng lại được. Đồng hồ cũng dừng khi contest đóng. Bấm Not yet để quay lại, hoặc Start now để bắt đầu.

    Hộp thoại Start the contest now? với nút Not yet và Start now
    Hộp thoại "Start the contest now?": "Your countdown of 2h starts now and cannot be paused. It also stops when the contest closes at 28/09/2026, 08:00 PM."
  4. Làm bài với Time left

    Sau khi Start, Time left đếm ngược và problems hiện ra. Nút Start contest biến mất.

    Trang contest của Alice ngay sau Start: Time left 01:59:55 và hai problems
    Ngay sau khi Start. Time left 01:59:55, danh sách 2 problems: ESC-50 và Capital Bike Share Hourly Demand.
  5. Khi hết giờ cá nhân

    Trang contest báo hết giờ, dù contest vẫn Running. Học viên vẫn xem được problems nhưng không nộp được nữa. Standings chỉ mở khi contest đóng.

    Trang contest của Sarah với banner Your time is up khi contest vẫn Running
    Sarah, khi contest vẫn Running. Banner "Your time is up": "The final standings appear after the contest closes at 28/09/2026, 08:00 PM." Danh sách problems vẫn hiện.
  6. Xem standings khi contest đóng

    Standings của contest FLEXIBLE có thêm cột Time used. Khi hai học viên hòa điểm, ai dùng ít thời gian hơn xếp trên.

    Standings của Summer Practice Round: Linh Tran 170.00 dùng 01:04:00 hạng 2, An Le 170.00 dùng 01:21:00 hạng 3
    Summer Practice Round. Dòng ghi chú: "Ties are broken by the time each student used, counted from when they pressed Start." Linh Tran và An Le cùng 170.00. Linh dùng 01:04:00 nên hạng 2, An dùng 01:21:00 nên hạng 3. Dòng của Sarah được tô màu và có nhãn YOU.

Đọc màn hình

Chỗ trên màn hìnhÝ nghĩaVí dụ
Badge Running, EndedContest đang mở hoặc đã đóng.Running
Badge FlexibleContest thuộc mode FLEXIBLE. Contest FIXED không có badge này.Flexible
Closes inThời gian còn lại đến khi contest đóng. Hiện trước khi học viên bấm Start.18:42:03
Time leftThời gian còn lại của riêng học viên. Hiện sau khi Start.01:59:55
Start from, ClosesĐầu và cuối của window. Học viên chỉ bấm Start được trong khoảng này.27/09/2026, 08:00 PM đến 28/09/2026, 08:00 PM
Your timeThời lượng mỗi học viên nhận sau khi bấm Start.2h
Started atCột trong tab Registered của instructor. Giờ học viên bấm Start. Ô hiện "–" là chưa Start.27/09/2026, 10:53 PM
Time usedCột trong standings. Thời gian học viên đã dùng, tính từ lúc Start.01:04:00

Hạn chót cá nhân. Hạn chót của một học viên là mốc nào đến trước: giờ Start cộng thời lượng, hoặc giờ contest đóng. Ví dụ, nếu contest đóng lúc 08:00 PM, học viên bấm Start lúc 07:30 PM chỉ có 30 phút, không phải 2 giờ. Ví dụ này theo quy tắc trong PR và dòng trong hộp thoại Start, không có trong ảnh chụp.

Submit khi còn giờ và khi hết giờ

Trang problem ESC-50, Running, Time left 01:59:16, nút Submit đang bật
Còn giờ. Problem ESC-50 của Alice: Running, Time left 01:59:16 ở header, Submit bật. Bấm vào ảnh để mở cỡ đầy đủ.
Trang problem ESC-50 của Sarah, nút Submit bị mờ, tooltip Your time is up, header không có đồng hồ
Hết giờ. Cùng problem ESC-50, tài khoản Sarah: Submit bị mờ, tooltip "Your time is up.", header không còn đồng hồ. Bấm vào ảnh để mở cỡ đầy đủ.

Badge: Running Ended Flexible Registered

Giao diện tiếng Việt

Trang contest của Alice bằng tiếng Việt: Đang diễn ra, Linh hoạt, Thời gian còn lại 01:57:51, Đề bài 2 bài
Alice đang làm bài, giao diện tiếng Việt. Các nhãn là "Đang diễn ra", "Linh hoạt" và "Thời gian còn lại 01:57:51". Giờ hiển thị theo dạng 24 giờ. Dòng mô tả contest vẫn bằng tiếng Anh vì đó là nội dung của contest, không phải bản dịch. Bản dịch cần người bản ngữ đọc lại.

Vì sao tin được

Kiểm tra bằng tay. Sarah bấm Start lúc 22:53 ngày 27/09/2026. Thời lượng 2h, nên 22:53 cộng 2h là 00:53 ngày 28/09/2026. Contest đóng lúc 20:00 ngày 28/09, muộn hơn, nên hạn chót là 00:53. Bảng database ghi đúng 00:53:00. Lúc chụp là 01:23:20, đã quá hạn, nên trạng thái là "time is up". Với standings: Linh dùng 3840 giây, chia 60 được 64 phút, tức 01:04:00. An dùng 4860 giây, tức 81 phút, hay 01:21:00. Cả hai khớp với cột Time used.

Đồng hồ sau khi reload và mở link trực tiếp

Trang contest của Alice sau khi reload: vẫn đăng nhập, Time left 01:59:05, hai problems
Alice mở contest bằng URL rồi reload. Vẫn đăng nhập, Time left 01:59:05. Alice bấm Start khoảng 55 giây trước đó.
Trang problem ESC-50 mở thẳng bằng link: Running, Time left 01:58:59, Submit bật
Trang problem ESC-50 mở thẳng bằng link, không đi qua ứng dụng. Running, Time left 01:58:59, Submit bật. Bấm vào ảnh để mở cỡ đầy đủ.

Dữ liệu lưu và phản hồi của API

Bảng database: giờ Start và hạn chót cá nhân của 5 học viên, Sarah time is up, Bob và Minh in progress, Alice và Linh not started
Bảng registrations của Autumn Data Challenge 2026, đọc từ database lúc 01:23:20 ngày 28/09/2026 giờ VN. Sarah Start 22:53, hạn chót 00:53, time is up. Bob Start 00:43, hạn chót 02:43. Minh Start 01:08, hạn chót 03:08. Cả hai đang làm. Alice và Linh chưa Start.
Bảng database standings của Summer Practice Round: Linh 3840 giây, An 4860 giây, cùng 170.00
Bảng standings của Summer Practice Round. Cột elapsed_s là số giây dùng: Linh 3840, An 4860, cùng 170.00. Cột last_best_vn là giờ trên đồng hồ của lần nộp tốt nhất: Linh 15:09, An 09:51. Nếu xếp theo giờ trên đồng hồ, An sẽ trên Linh. Xếp theo thời gian đã dùng, Linh trên An.
Bốn lệnh gọi API: ba lệnh trả 409 vì hết giờ, chưa Start và lịch bị khóa, và một lệnh trả 200
Bốn lệnh gọi API. Ba lệnh trả 409: nộp bài khi hết giờ ("Your contest time has ended"), nộp bài trước Start ("Start the contest first") và sửa Ends khi đã có học viên Start ("Contest timing is locked because students have already started"). Lệnh thứ tư sửa Registration opens và trả 200, timing_locked là true. Bấm vào ảnh để mở cỡ đầy đủ.

Giới hạn đã biết

Các quyết định

Câu hỏiLựa chọn
Có những mode nào?Hai mode: FIXED và FLEXIBLE. Mặc định là FIXED, và contest FIXED chạy đúng như trước.
Hạn chót cá nhân tính thế nào?Mốc nào đến trước: giờ Start cộng thời lượng, hoặc giờ contest đóng.
Nộp bài trước Start hoặc sau hạn chót?Server từ chối và trả 409.
Khi nào học viên thấy problems?Sau khi bấm Start. Khi window kết thúc, mọi thứ mở cho tất cả.
Điểm private?Vẫn ẩn đến giờ contest đóng, kể cả với học viên xong sớm.
Hòa điểm trong contest FLEXIBLE?Ai dùng ít thời gian hơn, tính từ lúc Start, xếp trên. Standings có thêm cột Time used.
Sửa lịch sau khi có học viên Start?Không. Starts, Ends, Duration và mode bị khóa. Registration opens vẫn sửa được.
Đồng hồ theo giờ nào?Giờ server, không phải giờ trên máy học viên.
Bấm Start trên contest đang ẩn, nháp hoặc đã archive?Trả 404, giống các endpoint khác của học viên, để không lộ rằng contest có tồn tại.
Ngôn ngữ?English và Vietnamese.
Cập nhật database?Thêm cột mới với giá trị mặc định FIXED. Theo PR, bản app cũ vẫn chạy được.

Việc cần làm sau phát hành

  1. Đã xong. PR #497 merge vào main ngày 28/09/2026. PR #491 (SP-763) cũng thêm migration và sửa cùng khu vực contest. PR đó merge sau #497, và cả hai cùng nằm trong v0.0.49.
  2. Xác nhận migration của PR đã chạy trên production. Tôi chưa kiểm tra bước này.
  3. Chạy thử một contest FLEXIBLE thật: instructor tạo contest, một học viên bấm Start rồi reload, và một học viên hết giờ. Đây là mục cuối chưa tick trong test plan của PR.
  4. Cập nhật hướng dẫn "How to participate" cho FLEXIBLE. Nhờ một người bản ngữ đọc lại bản tiếng Việt.
  5. Đưa SP-762 từ Testing sang Done. Code đã nằm trong tag production v0.0.49.