Introduction to Software Engineering · Chapter 02
Lab-02: Types of Applications
Your team classifies five applications you use every day, decides which type of application ITC Club Hub should be for its three user groups, turns the important qualities into six measurable scenarios, draws a context diagram and a three-tier sketch, and then separates the C++ core from its command-line interface with an Event class and a list-events command. The challenge designs offline-first check-in for a venue with poor Wi-Fi.
git log.Setup
15 min- Tools. Git; CMake 3.28 or later; a C++20 compiler (GCC 13, Clang 17 or MSVC in Visual Studio 2022, or newer); an editor with Mermaid preview (VS Code with the extension Markdown Preview Mermaid Support, or the online editor at
mermaid.liveto export PNG); draw.io is optional for Task 4. Open your team repository from Lab-01 and create the branch for this lab:git --version cmake --version # 3.28 or later g++ --version # or: clang++ --version, or cl in a VS prompt cd itc-club-hub # your team repository from Lab-01 git switch main && git pull git switch -c lab-02 mkdir -p lab02 include/clubhub src/cli data - Inputs from earlier labs. This lab reuses what you wrote in Lab-01. If something is missing or weak, use the fallback in the table and move on; do not stop to repair Lab-01.
Input File What you need from it Fallback if missing Problem statement lab01/team-charter.md(or your separate problem-statement file)Who has the problem, what it costs them today, what Club Hub must change "Club events at ITC are announced in scattered chat groups; students miss events, organisers cannot tell who will come or who attended, and the student affairs office cannot see club activity." Stakeholder list lab01/team-charter.mdUser groups and external parties, with their main interest Students, club leaders, event organisers, administrators (student affairs office), the ITC IT office (student directory), the course instructor as client. Team roles and decision rules lab01/team-charter.md,lab01/decisions.mdWho is Product Owner (leads Task 2), how the team decides Product Owner = the member listed first in the README; decisions by consensus, else majority; record them in lab01/decisions.md.Repository skeleton CMakeLists.txt,src/,include/clubhub/A place for the C++ code of Task 5 Use the CMakeLists.txtgiven in Task 5; it builds from an empty repository. - Case-study brief. Everything this lab needs to know about the product is in this box.
Case-study brief: ITC Club Hub
- Purpose
- An application for student clubs at the Institute of Technology of Cambodia. Club leaders register a club, which an administrator approves. Approved clubs publish events (title, date and time, location, capacity, description). Students browse events, register, cancel up to 24 hours before the start and receive a reminder. Organisers check registered students in at the door and see attendance. Administrators see a monthly activity report across clubs.
- User groups
- Students: about 3,000, almost all on Android or iPhone, often on mobile data, use it a few times a week. Organisers (club leaders and helpers): one or two phones at the door of a room or hall where the Wi-Fi is weak or overloaded. Administrators (student affairs office): 2 or 3 staff on desktop PCs in an office, monthly reports and club approvals.
- External systems
- E-mail service: sends registration confirmations and reminders. ITC student directory (run by the IT office): confirms that a student ID exists and gives name and e-mail; Club Hub never stores passwords itself.
- Business rules
- Capacity cannot be exceeded (a waiting list is a Should feature) · a student cannot register twice for the same event · cancellation closes 24 hours before the start · events in the past cannot be registered for · only approved clubs can publish events.
- Key numbers
- About 40 clubs, 5 to 15 events a week, 20 to 200 participants per event. Peak at the door: 200 arrivals in 10 minutes at one entrance. Registrations open on Friday evenings, when traffic is highest. The semester has 15 weeks.
- Personal data
- Student ID, name, e-mail, phone (optional), club membership, attendance records.
- In this course
- Teams analyse and model Club Hub as a web and mobile application and implement its core in C++20: domain model, business rules and a command-line front end, with CSV or JSON files for storage. Canonical names: namespace
clubhub; classesClub,Event,Student,Registration,RegistrationService(registerStudent,cancel,checkIn); enumsClubStatus,RegistrationStatus(Registered,Cancelled,CheckedIn,Waitlisted). Layout:CMakeLists.txt,src/,include/clubhub/,tests/.
- Deliverables of this lab. Create the files as you go; every task fills some of them.
itc-club-hub/ ├── lab01/ # Lab-01, read-only here ├── lab02/ │ ├── app-classification.md # Task 1 │ ├── app-quadrant.mmd # Task 1 (Mermaid quadrant chart) │ ├── adr-001-application-type.md # Task 2 │ ├── quality-scenarios.md # Task 3 │ ├── context-diagram.mmd (+ .png) # Task 4 │ ├── architecture.mmd (+ .png) # Task 4 │ ├── README.md # Task 5 (record what changed) │ └── challenge/offline-checkin.md # Challenge ├── include/clubhub/event.hpp # Task 5 ├── include/clubhub/event_csv.hpp # Task 5 ├── include/clubhub/queued_checkin.hpp # Challenge ├── src/event.cpp, src/event_csv.cpp # Task 5 ├── src/cli/main.cpp # Task 5 ├── data/events.csv # Task 5 └── CMakeLists.txt # Task 5 - Conventions. Every Markdown file starts with a title, a version and a date. Scenario ids are
QAS-01..QAS-06, decision recordsADR-001... Commit after each task with a message such asLab-02 task 3: quality attribute scenarios.
Classify five applications you use every day
Easy 30 minGoal
Practise the vocabulary of the chapter on real software before applying it to Club Hub: for each application name its type, where its code runs, its architecture and the quality attributes that matter most.
Steps
- Copy the starter into
lab02/app-classification.md. The five applications are fixed: a mobile banking app one of you uses, Telegram, a car's ABS (anti-lock braking) controller, Moodle as used at ITC, and a game one of you plays (write its name). - Split the rows: each team member fills at least one row and commits it with their own Git account (
git log --format='%an' -- lab02/app-classification.mdmust show at least four names). - Column Kind: system or application software. Column Type(s): use the chapter's terms (desktop, web MPA or SPA, mobile native, cross-platform or PWA, enterprise, embedded real-time, IoT, cloud SaaS, data-intensive or AI-enabled, game). Several types per row are normal.
- Column Where the code runs: name every place (phone, browser, server, ECU in the car) and how the software reaches it (app store, URL, installed at the factory, over-the-air update).
- Column Architecture: standalone, client-server, three-tier, embedded control loop, or a combination; one line of evidence (for example "works offline and syncs later").
- Column Top 3 qualities: choose from performance, availability, security, usability, portability, safety, and justify each with one concrete consequence of failure ("if Moodle is down during an online quiz...").
- Place the five applications on a Mermaid
quadrantChart(timing demand against availability demand, as on the chapter slide) inlab02/app-quadrant.mmd, and write two sentences below the table on the biggest difference you found.
Starter
# Lab-02 · Classification of five applications
Version 1.0 · 2026-__-__ · Team: __
| # | Application | Kind | Type(s) | Where the code runs, how it is delivered | Architecture | Top 3 qualities (why) | Filled by |
|---|-------------|------|---------|------------------------------------------|--------------|-----------------------|-----------|
| 1 | Mobile banking app: __ | | | | | | |
| 2 | Telegram | | | | | | |
| 3 | ABS controller in a car | | | | | | |
| 4 | Moodle at ITC | | | | | | |
| 5 | Game: __ | | | | | | |
Biggest difference we found: __
quadrantChart
title Five applications: timing vs availability
x-axis Low timing demand --> High timing demand
y-axis Low availability demand --> High availability demand
quadrant-1 Critical and fast
quadrant-2 Always on
quadrant-3 Best effort
quadrant-4 Fast but tolerant
Telegram: [0.50, 0.50]
%% TODO: move Telegram, then add the other four (values 0 to 1)
Expected output (one row, for an application that is not in your list)
| 0 | Google Maps | Application | Mobile native + web SPA; data-intensive
| | | | (traffic from millions of phones)
| Where: phone app (store), browser SPA, Google servers; map tiles cached
| on the phone, so the last route still shows offline
| Architecture: client-server; phone and browser clients call the same
| back-end APIs
| Top 3: availability (drivers depend on it on the road), performance
| (route in < 2 s or users switch app), usability (used while
| driving: large buttons, voice) | Filled by: Dara
- The ABS controller is the only row where a missed deadline can hurt someone: think about safety and hard real-time, and about who updates its software (a garage, not the driver).
- Telegram has several clients (phone, desktop, web) talking to the same servers. List them all under "where the code runs".
- Moodle is a web application hosted by the institute; ask yourself what happens to it in exam week.
- A banking app is a mobile client of an enterprise system (the bank's core banking system). Security is obvious; what is the second quality?
Acceptance checklist
Decide the application type(s) for ITC Club Hub
Medium 45 min · led by the Product OwnerGoal
Choose how each user group (students on phones, organisers at the door, administrators on desktops) will use Club Hub, compare the options with a weighted matrix, and record the decision in an architecture decision record that a new team member could understand.
Steps
- In
lab02/adr-001-application-type.md, fill the User groups table: for students, organisers and administrators write device, location, connectivity, how often they use Club Hub and the one thing that must never fail for them. Take the facts from the case-study brief and your Lab-01 stakeholder list. - List at least four options from: native apps (Android + iOS), cross-platform app, PWA, responsive web app, desktop application. Combinations are allowed (for example "PWA for students and organisers + the same web app for administrators").
- Agree on at least five criteria and their weights (1 to 3) with the Product Owner. Include cost for your team in one semester, reach, offline check-in at the door, reminders, and one criterion that matters to administrators. Write one line explaining each weight.
- Score every option from 1 to 5 per criterion, multiply by the weight and add up. Show the arithmetic (
3 + 9 + 10 = 22), not only the totals. - Check the winner against the deal-breakers (for example "check-in must work without Wi-Fi"). If it fails one, say what you add or change.
- Write the decision part of ADR-001: Status, Context, Decision, Consequences (at least two + and two -), Rejected options with one reason each. State how the front ends will reach the C++ core you build in this course (for example "the core would sit behind an HTTP API; in this course the CLI calls it directly").
- The Product Owner presents the ADR to the teaching assistant or instructor (your client) in 3 minutes; set Status: Accepted with the date, and add a line to
lab01/decisions.mdpointing to ADR-001.
Starter
# ADR-001 · Application types for ITC Club Hub
Version 1.0 · 2026-__-__ · Status: Proposed | Accepted (date) · Deciders: __
## User groups
| Group | Device | Where | Connectivity | How often | Must never fail |
|----------------|--------|-------|--------------|-----------|-----------------|
| Students | | | | | |
| Organisers | | | | | |
| Administrators | | | | | |
## Options
A. __ B. __ C. __ D. __
## Weighted comparison (score 1-5 x weight)
| Criterion (weight) | why this weight | A | B | C | D |
|--------------------|-----------------|---|---|---|---|
| Cost for our team in one semester (_) | | | | | |
| Reach (_) | | | | | |
| Offline check-in at the door (_) | | | | | |
| Reminders (_) | | | | | |
| __ for administrators (_) | | | | | |
| **Total** | | = | = | = | = |
Deal-breaker check: __
## Decision
## Consequences
+ __
+ __
- __
- __
## Rejected options
## How the front ends reach the C++ core
Expected output (excerpt; your options, weights and scores will differ)
| Criterion (weight) | why this weight | A native | B PWA | C web |
| Admin report printing (1) | 2-3 users, once a month | 1 x 1=1 | 4x1=4 | 5x1=5 |
...
Deal-breaker check: C (responsive web) fails "check-in without Wi-Fi";
we keep B and add an offline check-in queue (see the challenge).
Consequences
+ one codebase for students, organisers and administrators
- iPhone users receive reminders by push only after installing the PWA;
e-mail reminders remain the fallback
Rejected options
A native apps: three front ends for a 5-person team in one semester
- Decide per user group first, then look for one option that serves several groups. Administrators on desktops rarely need an app.
- Weights are the Product Owner's call, but they must be written down before scoring, otherwise the matrix only confirms what you already wanted.
- The chapter slide "Club Hub as a native app, a PWA or a responsive web app" is an example, not the answer: your weights, your fifth criterion and your deal-breakers must be your own.
- A consequence with no cost ("-") means you have not looked hard enough: think about iOS, store review, offline storage limits, team skills.
Acceptance checklist
Write six quality attribute scenarios
Medium 45 minGoal
Turn "Club Hub must be fast, reliable and secure" into six testable statements, one per quality attribute, each with a source, stimulus, environment, artifact, response and a measurable response measure.
Steps
- Create
lab02/quality-scenarios.mdfrom the starter. Write exactly six scenarios:QAS-01availability,QAS-02performance at check-in,QAS-03usability,QAS-04security,QAS-05modifiability,QAS-06portability. - Each member writes at least one scenario and commits it; the Scrum Master reviews all six against the checklist below before the task is ticked.
- Make every Environment concrete (time, load, network, device) using the key numbers in the case-study brief, for example "Friday 18:00, registration opens" or "200 arrivals in 10 minutes".
- Make every Response measure a number with a unit that someone could test: seconds, percent, minutes of downtime, number of taps, files changed, days of work. Show any calculation (for availability, the downtime per month it allows).
- For QAS-04 (security) choose a realistic threat for this application: another student reading attendance records, a guessed organiser account, a scraped list of phone numbers. Name the personal data at risk.
- For QAS-06 (portability) use the decision from Task 2: which browsers, phones or operating systems must work, and how you will check them.
- End the file with a priority table: each scenario rated for business importance (H/M/L) and technical risk (H/M/L), and the user group it protects. Mark the two scenarios the architecture must satisfy first.
Starter
# Lab-02 · Quality attribute scenarios for ITC Club Hub
Version 1.0 · 2026-__-__ · Reviewed by (Scrum Master): __
## QAS-01 · Availability · written by __
| Part | Content |
|------------------|---------|
| Source | |
| Stimulus | |
| Environment | |
| Artifact | |
| Response | |
| Response measure | |
## QAS-02 · Performance of check-in at the door · written by __
...
## QAS-06 · Portability · written by __
## Priorities
| Scenario | Business importance | Technical risk | Protects | First? |
|----------|---------------------|----------------|----------|--------|
| QAS-01 | | | | |
Expected output (one scenario that the chapter did not show)
## QAS-05 · Modifiability · written by Sokha
| Source | a developer of the team |
| Stimulus | adds the waiting list (a Should feature): a full |
| | event puts new registrations in status Waitlisted |
| Environment | design time, after Sprint 1, core already tested |
| Artifact | RegistrationService, Registration (include/clubhub/) |
| Response | change made in the core only; the CLI gains one |
| | column; existing behaviour unchanged |
| Response measure | done in <= 2 person-days; <= 4 files changed under |
| | include/ and src/; 0 changes in src/cli/ other than |
| | display; all existing tests still pass |
- Availability arithmetic: a 30-day month has 720 hours, so 99 % allows 7.2 h of downtime and 99.9 % allows about 43 minutes. Pick a target a student club can really pay for.
- Usability measures can be counted: "a first-time student registers for an event in at most 3 taps and 30 seconds, without help".
- Security responses are actions: deny, log, alert, lock after N attempts. Measures: "0 attendance records visible to other students", "account locked after 5 failed logins within 10 minutes".
- A scenario without a number is not finished. Read each response measure aloud and ask: "how would a tester prove this is false?"
Acceptance checklist
Draw a context diagram and a three-tier sketch
Medium 40 minGoal
Show Club Hub as one black box with everything around it (users and external systems) and the data that flows between them; then open the box one level and show its three tiers, marking which part your C++ code implements this semester.
Steps
- In
lab02/context-diagram.mmd(Mermaidflowchart LR, or draw.io exported to PNG) draw Club Hub as a single node in the centre. Do not draw anything inside it. - Add the four human roles as nodes:
Student,Club leader,Organiser,Administrator, and the two external systems:E-mail serviceandITC student directory. Use a different shape for external systems (for example[[ ]]). - Label every arrow with the data that flows, in the direction it flows: "registration, cancellation", "confirmation, reminder", "student ID lookup", "name and e-mail", "monthly report"... Aim for 10 to 14 labelled arrows.
- In
lab02/architecture.mmddraw three subgraphsPresentation,Logic,Data. Presentation holds the front ends from your ADR-001 plus theC++ CLI; Logic holdsRegistrationServiceandEventService; Data holds the CSV files (and "database later"). Draw the external systems where Logic calls them. - Mark with a style or a note which nodes your team implements in C++ this semester (the core and the CLI) and which are only modelled (the PWA or web front ends). Write the matching folder next to each implemented node:
include/clubhub/,src/,src/cli/,data/. - Export both diagrams to PNG next to the
.mmdfiles, and embed them inlab02/README.mdlater (Task 5) so they show on GitHub.
Starter
flowchart LR
S([Student]) -- browse, register, cancel --> CH[ITC Club Hub]
CH -- confirmation, reminder request --> MAIL[[E-mail service]]
MAIL -- e-mail --> S
%% TODO: Club leader, Organiser, Administrator
%% TODO: ITC student directory (what does Club Hub ask, what comes back?)
%% TODO: label every arrow with the data that flows
flowchart TB
subgraph P[Presentation]
CLI[C++ CLI · src/cli/]
%% TODO: the front ends from ADR-001
end
subgraph L[Logic]
RS[RegistrationService · include/clubhub/, src/]
end
subgraph D[Data]
CSV[(CSV files · data/)]
end
CLI --> RS
RS --> CSV
%% TODO: external systems, and mark implemented vs modelled nodes
Expected output (the context diagram started; yours has all six neighbours)
flowchart LR S([Student]) -- browse, register, cancel --> CH[ITC Club Hub] CH -- event list, registration status --> S O([Organiser]) -- check-in scans --> CH CH -- attendance list --> O CH -- confirmation, reminder request --> MAIL[[E-mail service]] MAIL -- e-mail --> S
- A context diagram answers "what is outside and what crosses the border?" If you draw a database or a class, it is no longer a context diagram.
- The student directory is read by Club Hub: the arrow to it carries a request (student ID), the arrow back carries data (name, e-mail, exists or not).
- Club leaders and administrators exchange different data: club registration goes one way, approval or rejection comes back.
- Mermaid styles:
classDef built fill:#d1e7dd,stroke:#198754andclass RS,CLI builtmark implemented nodes; or add anote-like node "implemented in C++".
Acceptance checklist
C++: separate the core from the interface
Hard 75 minGoal
Build the first piece of the Club Hub core, an Event class with its start time as a std::chrono time point, and a CSV loader, in a library that never prints and never opens a file by name. Then add a thin command-line front end with a list-events command. The same core could later serve a web or mobile front end through an API, exactly as in your three-tier sketch.
Steps
- Create
include/clubhub/event.hppandinclude/clubhub/event_csv.hppexactly as in the starter (they are the agreed interface; do not add<iostream>to them). - In
src/event.cppcomplete theEventconstructor: throwstd::invalid_argumentwhen the id or title is empty or the capacity is not positive. Then finishparseTimePointso that"2026-10-14 14:00"parses and"2026-02-30 10:00","2026-10-14"and"14:00 2026-10-14"throw. - In
src/event_csv.cppimplementloadEventsCsv: skip the header, split each line at commas, require 5 fields, build anEvent, and put every bad row intoerrorswith its line number instead of stopping. - In
src/cli/main.cppfinishlistEvents: sort by start time, print an aligned table, the count, and the skipped rows onstd::cerr. All console output lives in this file only. - Create
data/events.csvfrom the starter (keep the invalid row E004: it proves the error path), add theCMakeLists.txttargetsclubhub_core(library) andclubhub(executable), then build and run:cmake -S . -B build cmake --build build ./build/clubhub list-events data/events.csv # Windows: build\Debug\clubhub.exe grep -rn "iostream\|cout\|ifstream" include/ src/event*.cpp # must print nothing - Split the work so that at least three members commit: one the header and
event.cpp, one the CSV loader, one the CLI andCMakeLists.txt. Review each other's code in the pull request. - Record what changed: create
lab02/README.mdwith (a) a table of every Lab-02 file (documents, diagrams, code) with a one-line description and its main author, (b) a table Inputs reused listing the Lab-01 files you used (lab01/team-charter.md,lab01/decisions.md) with their version or commit hash (git log -1 --format=%h -- lab01/team-charter.md), (c) the two PNG diagrams embedded, and (d) a change-log row (date, version 1.0, what changed, by whom). Commit asLab-02 task 5: event core, list-events CLI, READMEand push the branch.
Starter: the core interface (include/clubhub/event.hpp, include/clubhub/event_csv.hpp)
// include/clubhub/event.hpp
#pragma once
#include <chrono>
#include <string>
namespace clubhub {
// Minute precision is enough for club events. Times are ITC local
// time (Asia/Phnom_Penh); this lab does no time-zone conversion.
using TimePoint = std::chrono::sys_time<std::chrono::minutes>;
class Event {
public:
// Throws std::invalid_argument when a field breaks a rule.
Event(std::string id, std::string title, TimePoint start,
std::string location, int capacity);
const std::string& id() const noexcept { return id_; }
const std::string& title() const noexcept { return title_; }
TimePoint start() const noexcept { return start_; }
const std::string& location() const noexcept { return location_; }
int capacity() const noexcept { return capacity_; }
// Business rule: events in the past cannot be registered for.
bool hasStarted(TimePoint now) const noexcept { return start_ <= now; }
private:
std::string id_;
std::string title_;
TimePoint start_;
std::string location_;
int capacity_;
};
// "2026-10-14 14:00" -> TimePoint; throws std::invalid_argument
TimePoint parseTimePoint(const std::string& text);
// TimePoint -> "2026-10-14 14:00"
std::string formatTimePoint(TimePoint t);
} // namespace clubhub
// include/clubhub/event_csv.hpp
#pragma once
#include <istream>
#include <string>
#include <vector>
#include "clubhub/event.hpp"
namespace clubhub {
struct CsvError {
int line;
std::string message;
};
struct EventLoadResult {
std::vector<Event> events;
std::vector<CsvError> errors;
};
// Reads "id,title,start,location,capacity" rows after one header line.
// A bad row goes to errors; the good rows are still returned.
EventLoadResult loadEventsCsv(std::istream& in);
} // namespace clubhub
Starter: src/event.cpp (complete the TODOs)
#include "clubhub/event.hpp"
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <utility>
namespace clubhub {
Event::Event(std::string id, std::string title, TimePoint start,
std::string location, int capacity)
: id_{std::move(id)}, title_{std::move(title)}, start_{start},
location_{std::move(location)}, capacity_{capacity} {
// TODO: throw std::invalid_argument when id_ or title_ is empty
// TODO: throw std::invalid_argument when capacity_ <= 0
}
TimePoint parseTimePoint(const std::string& text) {
using namespace std::chrono;
std::istringstream in{text};
int y{}, mo{}, d{}, h{}, mi{};
char dash1{}, dash2{}, colon{};
in >> y >> dash1 >> mo >> dash2 >> d >> h >> colon >> mi;
const year_month_day date{year{y}, month{static_cast<unsigned>(mo)},
day{static_cast<unsigned>(d)}};
// TODO: throw std::invalid_argument{"bad date-time '" + text + "'"}
// unless the stream is good, the separators are '-', '-', ':',
// nothing follows, date.ok(), 0 <= h <= 23 and 0 <= mi <= 59
return sys_days{date} + hours{h} + minutes{mi};
}
// Given: TimePoint -> "2026-10-14 14:00"
std::string formatTimePoint(TimePoint t) {
using namespace std::chrono;
const auto midnight = floor<days>(t);
const year_month_day date{midnight};
const hh_mm_ss time{t - midnight};
std::ostringstream out;
out << std::setfill('0') << static_cast<int>(date.year()) << '-'
<< std::setw(2) << static_cast<unsigned>(date.month()) << '-'
<< std::setw(2) << static_cast<unsigned>(date.day()) << ' '
<< std::setw(2) << time.hours().count() << ':'
<< std::setw(2) << time.minutes().count();
return out.str();
}
} // namespace clubhub
Starter: src/event_csv.cpp, src/cli/main.cpp, CMakeLists.txt, data/events.csv
// src/event_csv.cpp
#include "clubhub/event_csv.hpp"
#include <sstream>
#include <stdexcept>
namespace clubhub {
EventLoadResult loadEventsCsv(std::istream& in) {
EventLoadResult result;
std::string line;
int lineNo = 1;
std::getline(in, line); // skip the header
while (std::getline(in, line)) {
++lineNo;
// TODO: strip a trailing '\r' (files edited on Windows)
// TODO: split the line at ',' into fields; expect exactly 5
// TODO: build an Event (parseTimePoint for the start, an int for
// the capacity) and add it to result.events; on
// std::invalid_argument add {lineNo, e.what()} to result.errors
}
return result;
}
} // namespace clubhub
// src/cli/main.cpp: the only place that talks to the user
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include "clubhub/event.hpp"
#include "clubhub/event_csv.hpp"
namespace {
int listEvents(const std::string& path) {
std::ifstream file{path};
if (!file) {
std::cerr << "error: cannot open " << path << '\n';
return 2;
}
auto [events, errors] = clubhub::loadEventsCsv(file);
// TODO: sort events by start time (std::ranges::sort, projection
// &clubhub::Event::start)
// TODO: print a header line and one aligned row per event
// (std::left, std::setw; formatTimePoint for the start)
// TODO: print "<n> event(s)", then every error to std::cerr as
// <path>:<line>: skipped: <message>
return errors.empty() ? 0 : 1;
}
void printUsage() {
std::cerr << "usage: clubhub list-events [events.csv]\n";
}
} // namespace
int main(int argc, char* argv[]) {
if (argc < 2) {
printUsage();
return 2;
}
const std::string command{argv[1]};
if (command == "list-events") {
return listEvents(argc > 2 ? argv[2] : "data/events.csv");
}
std::cerr << "unknown command: " << command << '\n';
printUsage();
return 2;
}
cmake_minimum_required(VERSION 3.28)
project(clubhub LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Core: domain model and rules. No console, no file names.
add_library(clubhub_core src/event.cpp src/event_csv.cpp)
target_include_directories(clubhub_core PUBLIC include)
# Interface: the command-line front end. A web or mobile front end
# would be another target that links the same core.
add_executable(clubhub src/cli/main.cpp)
target_link_libraries(clubhub PRIVATE clubhub_core)
id,title,start,location,capacity
E001,Robotics Workshop,2026-10-14 14:00,Lab F-301,30
E002,Debate Night,2026-10-16 18:00,Amphi A,120
E003,Intro to Git,2026-10-09 15:30,Room I-204,40
E004,Charity Run Briefing,2026-10-21 17:00,Sports Hall,0
E005,Photo Walk,2026-10-18 07:00,Main Gate,25
Expected output
$ ./build/clubhub list-events data/events.csv
START TITLE LOCATION CAPACITY
2026-10-09 15:30 Intro to Git Room I-204 40
2026-10-14 14:00 Robotics Workshop Lab F-301 30
2026-10-16 18:00 Debate Night Amphi A 120
2026-10-18 07:00 Photo Walk Main Gate 25
4 event(s)
data/events.csv:5: skipped: capacity must be positive
$ echo $?
1
$ ./build/clubhub
usage: clubhub list-events [events.csv]
- Splitting at commas:
std::istringstream in{line}; while (std::getline(in, field, ',')) fields.push_back(field);. A line ending in a comma needs one extra empty field. - Parsing the capacity:
std::from_chars(header<charconv>) reports an error instead of throwing; convert that into your ownstd::invalid_argumentwith a clear message such asbad number 'abc'. - "Nothing follows" in
parseTimePoint:(in >> std::ws).eof()is true only when the rest of the text was blank. - Sorting with a projection:
std::ranges::sort(events, {}, &clubhub::Event::start);compares the start times of two events. - If the final
grepfindscoutin the core, move that printing intosrc/cli/main.cppand return data instead.
Acceptance checklist
Challenge: offline-first check-in for a PWA
Hard 90–120 min · optional, extra creditScenario
The Debate Night takes place in Amphi A. The Wi-Fi there drops for minutes at a time and 120 students arrive in 10 minutes through two doors, each staffed by an organiser with a phone running the Club Hub PWA. Check-in must never block on the network: each phone records check-ins locally, queues them, and synchronises with the server when it can. Two doors, one flaky network and a student who shows the same QR code twice will produce conflicts. Design how the system resolves them.
Requirements
- In
lab02/challenge/offline-checkin.mddraw a MermaidsequenceDiagramwith participantsOrganiser,PWA,ServiceWorker,IndexedDB queueandClub Hub API, showing: a scan while offline, the local green tick, the queued record, the network coming back, the batch upload and the server's per-item answer (accepted or rejected with a reason). - Write a conflict rules table with at least five cases: the same student scanned at both doors; a scan for a registration cancelled on the server meanwhile; a student not registered at all; a retry of an upload that the server already applied; a phone whose clock is 7 minutes wrong. For each: who wins, what the organiser sees, what is stored.
- Define the C++ data structure for a queued check-in in
include/clubhub/queued_checkin.hpp: astruct QueuedCheckInand aclass CheckInQueue(enqueue, pending, markAccepted, markRejected). Explain in the Markdown file which field makes a retried upload idempotent (applied at most once). - Link the design to your quality scenarios: which of QAS-01..06 does offline-first improve, and which does it make harder? Write one new scenario for "check-in during a 5-minute network outage" with a measurable response.
Skeleton
// include/clubhub/queued_checkin.hpp
#pragma once
#include <chrono>
#include <optional>
#include <string>
#include <vector>
namespace clubhub {
enum class SyncState { Pending, Sent, Accepted, Rejected };
// One check-in recorded on an organiser's phone while offline.
struct QueuedCheckIn {
std::string localId; // TODO: why a UUID made on the device?
std::string deviceId;
std::string eventId;
std::string studentId;
std::chrono::sys_seconds scannedAt; // device clock: trust it?
// TODO: attempts, state, the server's reject reason (optional)
};
class CheckInQueue {
public:
// false if this student is already queued for this event
bool enqueue(QueuedCheckIn item);
// oldest first; what the next sync attempt uploads
std::vector<QueuedCheckIn> pending() const;
void markAccepted(const std::string& localId);
void markRejected(const std::string& localId, std::string reason);
private:
std::vector<QueuedCheckIn> items_;
};
} // namespace clubhub
## Conflict rules
| # | Case | Winner | Organiser sees | Stored on the server |
|---|------|--------|----------------|----------------------|
| 1 | same student scanned at door 1 and door 2 | | | |
| 2 | registration cancelled on the server before the scan synced | | | |
| 3 | student not registered | | | |
| 4 | upload retried after a timeout, server had applied it | | | |
| 5 | phone clock 7 minutes wrong | | | |
Expected output (the start of the sequence diagram and one rule)
sequenceDiagram actor O as Organiser participant P as PWA participant Q as IndexedDB queue participant A as Club Hub API Note over P,A: Wi-Fi down O->>P: scan QR of student e20230117 P->>Q: store check-in, state Pending P-->>O: green tick, marked offline Note over P,A: Wi-Fi back P->>A: POST batch of pending check-ins A-->>P: per item: accepted or rejected with reason
| 4 | upload retried after a timeout | server (first copy) | nothing new |
| | the server keeps a set of applied localIds; a second POST with the |
| | same localId returns "accepted" again without a second record |
- The server is the single source of truth for registrations; the phone is the source of truth for "this student stood at my door at 18:02".
- Check-in is naturally idempotent for the business ("checked in" twice is still checked in), so duplicate scans can be merged: keep the earliest scan and remember both devices.
- Never order events across phones by device clocks alone; store the server's
receivedAtas well, and use device time only for ordering within one phone. - Think about what the organiser can do at the door when the server later rejects a check-in: the rejected list must reach the organiser's screen, not only a log file.
Acceptance checklist
Grading rubric
| Component | Points | Full marks when… |
|---|---|---|
| Task 1 · Classify five applications | 10 | Five complete rows in chapter vocabulary, justified qualities, quadrant chart renders. |
| Task 2 · Application type decision | 15 | Three user groups analysed, four options, five weighted criteria with visible arithmetic, deal-breaker check, ADR-001 accepted with rejected options. |
| Task 3 · Quality attribute scenarios | 20 | Six complete six-part scenarios, every response measure testable, priorities set, reviewed. |
| Task 4 · Context and three-tier diagrams | 20 | Black-box context with all roles and both external systems, every flow labelled; three tiers with implemented parts mapped to folders. |
| Task 5 · C++ core vs interface + README | 25 | Builds cleanly, list-events output as expected including the error path, no I/O in the core, std::chrono start time, complete README. |
| Quality of documents, diagrams and code | 10 | Headers with version and date, consistent ids, readable diagrams, modern C++ (const, RAII, no raw new), contributions of every member visible in Git. |
| ★ Challenge (extra credit) | +20 | Sequence diagram, five or more conflict rules, compiling data structure with an idempotency key, new outage scenario and trade-offs. |
| Total | 100 (+20) |
src/event*.cpp use std::cout or open files by name · -10 if the code does not build from a clean checkout · -5 if lab02/README.md is missing · -5 if one member has no commit in this lab without a note explaining why.Submission
- Repository layout after this lab:
itc-club-hub/ ├── CMakeLists.txt ├── README.md ├── data/events.csv ├── include/clubhub/event.hpp, event_csv.hpp (, queued_checkin.hpp) ├── src/event.cpp, src/event_csv.cpp, src/cli/main.cpp ├── lab01/ ... └── lab02/ ├── README.md ├── app-classification.md, app-quadrant.mmd ├── adr-001-application-type.md ├── quality-scenarios.md ├── context-diagram.mmd + .png, architecture.mmd + .png └── challenge/offline-checkin.md (optional) - Self-check from a clean clone before you open the pull request (there are no tests yet;
ctestsimply reports none):cmake -S . -B build && cmake --build build && ctest --test-dir build ./build/clubhub list-events data/events.csv git log --format='%an' main..lab-02 | sort | uniq -c # every member listed - Branch
lab-02, pull request titledLab-02 – <team name>, reviewed by at least one team member other than the author, and the teaching assistant added as reviewer.