iotlabkit 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
iotlabkit/registry.py ADDED
@@ -0,0 +1,705 @@
1
+ """Registry of supported IoT practical programs (Programs 5 through 17).
2
+
3
+ Strictly excludes Programs 1, 2, 3, 4, and 18 as per syllabus specification.
4
+ Provides complete metadata, prerequisites, dependencies, setup instructions,
5
+ and generator linkages for each practical.
6
+ """
7
+
8
+ from dataclasses import dataclass, field
9
+ from typing import Callable, Dict, List, Optional, Tuple
10
+
11
+ SUPPORTED_PROGRAM_IDS = set(range(5, 18)) # 5 through 17 inclusive
12
+ EXCLUDED_PROGRAM_IDS = {1, 2, 3, 4, 18}
13
+
14
+
15
+ @dataclass
16
+ class ProgramInfo:
17
+ """Metadata and operational requirements for a laboratory practical."""
18
+
19
+ id: int
20
+ code: str
21
+ title: str
22
+ layer: str
23
+ description: str
24
+ objective: str
25
+ requires_internet: bool
26
+ requires_credentials: bool
27
+ requires_broker: bool
28
+ requires_dataset: bool
29
+ requires_server: bool
30
+ dependencies: List[str]
31
+ source_info: str
32
+ generated_files: List[str]
33
+ prerequisites: List[str]
34
+ setup_steps: List[str]
35
+ viva_preview: List[Tuple[str, str]]
36
+
37
+ def get_generator(self) -> Callable[[str, str], Dict[str, str]]:
38
+ """Dynamically imports and returns the program's template generator function."""
39
+ module_name = f"iotlabkit.templates.program_{self.code}"
40
+ try:
41
+ import importlib
42
+ mod = importlib.import_module(module_name)
43
+ return getattr(mod, "generate")
44
+ except (ImportError, AttributeError) as err:
45
+ raise RuntimeError(
46
+ f"Generator for Program {self.id} ({module_name}) could not be loaded: {err}"
47
+ )
48
+
49
+
50
+ # Complete specifications for Programs 5 through 17
51
+ PROGRAMS: Dict[int, ProgramInfo] = {
52
+ 5: ProgramInfo(
53
+ id=5,
54
+ code="05",
55
+ title="HTTP REST API + Cloud Database + Dashboard",
56
+ layer="Communication and Application Layer",
57
+ description=(
58
+ "Collects real environmental sensor readings (temperature and humidity), "
59
+ "transmits records via an HTTP REST API server, persists records in a cloud "
60
+ "database (Supabase PostgreSQL / Firebase), and displays real-time telemetry "
61
+ "on an interactive web dashboard."
62
+ ),
63
+ objective=(
64
+ "Understand HTTP REST principles (GET/POST/JSON) in IoT pipelines, cloud database "
65
+ "table design, and live telemetry dashboard visualization."
66
+ ),
67
+ requires_internet=True,
68
+ requires_credentials=True,
69
+ requires_broker=False,
70
+ requires_dataset=False,
71
+ requires_server=True,
72
+ dependencies=["requests", "python-dotenv", "flask", "streamlit"],
73
+ source_info="Real hardware DHT sensor or user-supplied real-time temperature/humidity measurements",
74
+ generated_files=[
75
+ "README.md",
76
+ "requirements.txt",
77
+ ".env.example",
78
+ ".gitignore",
79
+ "server.py",
80
+ "client_sender.py",
81
+ "dashboard.py",
82
+ "supabase_schema.sql",
83
+ "firebase_setup.md",
84
+ ],
85
+ prerequisites=[
86
+ "Python 3.9+",
87
+ "A free Supabase account (or Firebase project)",
88
+ "Web browser for dashboard",
89
+ ],
90
+ setup_steps=[
91
+ "Copy `.env.example` to `.env` and fill in Supabase/Firebase credentials.",
92
+ "Run `python server.py` in Terminal 1 to start the REST API.",
93
+ "Run `python client_sender.py` in Terminal 2 to stream real sensor readings.",
94
+ "Run `streamlit run dashboard.py` in Terminal 3 to view live metrics.",
95
+ ],
96
+ viva_preview=[
97
+ ("Why is HTTP REST used in IoT applications?",
98
+ "HTTP REST provides a universally supported, stateless protocol using standard methods (GET, POST) and JSON payloads, making cloud integration straightforward."),
99
+ ("What is the difference between Supabase and Firebase for IoT storage?",
100
+ "Supabase provides an open-source relational PostgreSQL database accessible via PostgREST, whereas Firebase provides a NoSQL document/realtime database (Firestore)."),
101
+ ],
102
+ ),
103
+ 6: ProgramInfo(
104
+ id=6,
105
+ code="06",
106
+ title="MQTT + Cloud Database + Dashboard",
107
+ layer="Communication and Application Layer",
108
+ description=(
109
+ "Implements an MQTT subscriber that connects to an external broker, receives "
110
+ "live sensor telemetry payloads, parses the JSON data, stores records in a "
111
+ "cloud database (Supabase / Firebase), and renders the history on a dashboard."
112
+ ),
113
+ objective=(
114
+ "Learn publish/subscribe messaging architecture, topic design, QoS levels, and "
115
+ "decoupled data persistence from MQTT into cloud storage."
116
+ ),
117
+ requires_internet=True,
118
+ requires_credentials=True,
119
+ requires_broker=True,
120
+ requires_dataset=False,
121
+ requires_server=False,
122
+ dependencies=["paho-mqtt", "requests", "python-dotenv", "streamlit"],
123
+ source_info="Real sensor readings published to configured MQTT broker topic",
124
+ generated_files=[
125
+ "README.md",
126
+ "requirements.txt",
127
+ ".env.example",
128
+ ".gitignore",
129
+ "subscriber.py",
130
+ "publisher.py",
131
+ "dashboard.py",
132
+ "supabase_schema.sql",
133
+ ],
134
+ prerequisites=[
135
+ "Python 3.9+",
136
+ "Access to an MQTT broker (e.g. HiveMQ public broker or Mosquitto)",
137
+ "Supabase or Firebase credentials",
138
+ ],
139
+ setup_steps=[
140
+ "Copy `.env.example` to `.env` and set broker address and cloud DB keys.",
141
+ "Run `python subscriber.py` to listen for MQTT messages and write to DB.",
142
+ "Run `python publisher.py` to transmit real sensor readings.",
143
+ "Run `streamlit run dashboard.py` to inspect stored telemetry.",
144
+ ],
145
+ viva_preview=[
146
+ ("How does MQTT differ from HTTP in IoT communications?",
147
+ "MQTT uses a lightweight publish/subscribe pattern over persistent TCP connections with minimal packet overhead (2-byte header), whereas HTTP uses request/response with larger headers."),
148
+ ("What are MQTT QoS levels?",
149
+ "QoS 0 is 'At most once' (fire and forget), QoS 1 is 'At least once' (acknowledged), and QoS 2 is 'Exactly once' (four-step handshake)."),
150
+ ],
151
+ ),
152
+ 7: ProgramInfo(
153
+ id=7,
154
+ code="07",
155
+ title="ThingsBoard + MQTT Telemetry",
156
+ layer="Communication and Application Layer",
157
+ description=(
158
+ "Publishes real-time IoT sensor telemetry (temperature, humidity, status) to "
159
+ "ThingsBoard IoT Platform using MQTT. Includes a comprehensive visual guide "
160
+ "for building ThingsBoard widgets, digital gauges, and time-series line charts."
161
+ ),
162
+ objective=(
163
+ "Master industry-standard IoT device management platforms, telemetry attribute "
164
+ "provisioning, access token authentication, and cloud widget visualization."
165
+ ),
166
+ requires_internet=True,
167
+ requires_credentials=True,
168
+ requires_broker=True,
169
+ requires_dataset=False,
170
+ requires_server=False,
171
+ dependencies=["paho-mqtt", "python-dotenv"],
172
+ source_info="Real sensor readings or user measurements sent to ThingsBoard",
173
+ generated_files=[
174
+ "README.md",
175
+ "requirements.txt",
176
+ ".env.example",
177
+ ".gitignore",
178
+ "main.py",
179
+ "thingsboard_dashboard_guide.md",
180
+ ],
181
+ prerequisites=[
182
+ "Python 3.9+",
183
+ "Free ThingsBoard Cloud account (thingsboard.cloud) or local ThingsBoard CE",
184
+ "ThingsBoard Device Access Token",
185
+ ],
186
+ setup_steps=[
187
+ "Create a Device on ThingsBoard and copy the Device Access Token.",
188
+ "Configure `THINGSBOARD_ACCESS_TOKEN` in `.env`.",
189
+ "Execute `python main.py` to start publishing live telemetry.",
190
+ "Follow `thingsboard_dashboard_guide.md` to add widgets to your ThingsBoard dashboard.",
191
+ ],
192
+ viva_preview=[
193
+ ("What is the standard ThingsBoard telemetry MQTT topic?",
194
+ "`v1/devices/me/telemetry` is used to send timeseries telemetry, while `v1/devices/me/attributes` is used for client-side device attributes."),
195
+ ("How does ThingsBoard authenticate MQTT devices?",
196
+ "ThingsBoard authenticates devices primarily using an Access Token passed as the MQTT client username (with an empty password)."),
197
+ ],
198
+ ),
199
+ 8: ProgramInfo(
200
+ id=8,
201
+ code="08",
202
+ title="CoAP Client and Server Environmental Monitoring",
203
+ layer="Communication and Application Layer",
204
+ description=(
205
+ "Implements a real RFC 7252 CoAP (Constrained Application Protocol) server over UDP "
206
+ "exposing environmental sensor endpoints (/temperature, /humidity, /environmental), "
207
+ "paired with a CoAP client that executes GET requests and renders readings."
208
+ ),
209
+ objective=(
210
+ "Understand UDP-based constrained IoT protocols, CoAP 4-byte header architecture, "
211
+ "Uri-Path options, Content-Format negotiation, and terminal-separated execution."
212
+ ),
213
+ requires_internet=False,
214
+ requires_credentials=False,
215
+ requires_broker=False,
216
+ requires_dataset=False,
217
+ requires_server=True,
218
+ dependencies=[], # Pure Python RFC 7252 implementation with zero mandatory dependencies
219
+ source_info="Real hardware or user-configured ambient readings exposed via CoAP resources",
220
+ generated_files=[
221
+ "README.md",
222
+ "requirements.txt",
223
+ ".env.example",
224
+ ".gitignore",
225
+ "server.py",
226
+ "client.py",
227
+ "coap_core.py",
228
+ ],
229
+ prerequisites=[
230
+ "Python 3.9+",
231
+ "Two open terminal windows (one for server, one for client)",
232
+ ],
233
+ setup_steps=[
234
+ "Open Terminal 1 and start the CoAP server: `python server.py`.",
235
+ "Open Terminal 2 and query the server: `python client.py`.",
236
+ "Optionally request specific resources: `python client.py --resource /temperature`.",
237
+ ],
238
+ viva_preview=[
239
+ ("Why is CoAP designed on top of UDP instead of TCP?",
240
+ "UDP eliminates connection setup handshakes and header overhead, making it ideal for microcontrollers with strict memory, bandwidth, and battery constraints."),
241
+ ("What is the standard UDP port for CoAP?",
242
+ "Port 5683 for unencrypted CoAP, and port 5684 for CoAP over Datagram Transport Layer Security (DTLS/coaps)."),
243
+ ],
244
+ ),
245
+ 9: ProgramInfo(
246
+ id=9,
247
+ code="09",
248
+ title="Online and Local MQTT Brokers (HiveMQ & Mosquitto)",
249
+ layer="Communication and Application Layer",
250
+ description=(
251
+ "Provides publisher and subscriber implementations capable of connecting to both "
252
+ "HiveMQ (online cloud broker) and Eclipse Mosquitto (local self-hosted broker). "
253
+ "Includes a beginner-friendly comparison of online vs. local broker trade-offs."
254
+ ),
255
+ objective=(
256
+ "Learn broker configuration, network ports (1883/8883), authentication, firewall "
257
+ "settings, and trade-offs between cloud-hosted and edge-hosted MQTT brokers."
258
+ ),
259
+ requires_internet=True, # For HiveMQ; False if testing purely local Mosquitto
260
+ requires_credentials=False,
261
+ requires_broker=True,
262
+ requires_dataset=False,
263
+ requires_server=True,
264
+ dependencies=["paho-mqtt", "python-dotenv"],
265
+ source_info="Real environmental telemetry published across local and cloud MQTT brokers",
266
+ generated_files=[
267
+ "README.md",
268
+ "requirements.txt",
269
+ ".env.example",
270
+ ".gitignore",
271
+ "publisher.py",
272
+ "subscriber.py",
273
+ "broker_comparison.md",
274
+ "mosquitto_setup_guide.md",
275
+ ],
276
+ prerequisites=[
277
+ "Python 3.9+",
278
+ "Internet access (for HiveMQ) OR Eclipse Mosquitto installed locally",
279
+ ],
280
+ setup_steps=[
281
+ "Configure `.env` to select either online (HiveMQ) or local (Mosquitto) broker.",
282
+ "To test local broker, install and launch Mosquitto (`sudo systemctl start mosquitto`).",
283
+ "Start subscriber in Terminal 1: `python subscriber.py --broker hivemq`.",
284
+ "Publish messages in Terminal 2: `python publisher.py --broker hivemq`.",
285
+ "Switch to local broker: `python subscriber.py --broker local`.",
286
+ ],
287
+ viva_preview=[
288
+ ("Compare HiveMQ Online and Local Eclipse Mosquitto.",
289
+ "HiveMQ Online requires zero installation and enables remote device access over the internet, but depends on internet connectivity. Mosquitto provides ultra-low latency on local LANs and complete data privacy, but requires local installation and port-forwarding for external access."),
290
+ ("What does the Mosquitto configuration `allow_anonymous true` mean?",
291
+ "It allows clients to connect without supplying a username and password, commonly used in development and private lab networks."),
292
+ ],
293
+ ),
294
+ 10: ProgramInfo(
295
+ id=10,
296
+ code="10",
297
+ title="CoAP Environmental Monitoring (Open-Meteo + DB + Dashboard)",
298
+ layer="Communication and Application Layer",
299
+ description=(
300
+ "Fetches live weather and air-quality telemetry from Open-Meteo official APIs for "
301
+ "configured coordinates (default: 10.0159, 76.3419), exposes the live data over a "
302
+ "CoAP server, retrieves it via a CoAP client, stores records in a cloud database "
303
+ "(Supabase / Firebase), and renders the environmental metrics on a dashboard."
304
+ ),
305
+ objective=(
306
+ "Integrate multi-source live REST APIs, bridge HTTP REST to CoAP, handle network "
307
+ "timeouts gracefully, and persist multi-variable environmental records to cloud DB."
308
+ ),
309
+ requires_internet=True,
310
+ requires_credentials=True,
311
+ requires_broker=False,
312
+ requires_dataset=False,
313
+ requires_server=True,
314
+ dependencies=["requests", "python-dotenv", "streamlit"],
315
+ source_info="Open-Meteo Forecast & Air Quality Live APIs (Lat: 10.0159, Lon: 76.3419)",
316
+ generated_files=[
317
+ "README.md",
318
+ "requirements.txt",
319
+ ".env.example",
320
+ ".gitignore",
321
+ "coap_env_server.py",
322
+ "coap_env_client.py",
323
+ "dashboard.py",
324
+ "supabase_schema.sql",
325
+ "coap_core.py",
326
+ ],
327
+ prerequisites=[
328
+ "Python 3.9+",
329
+ "Active internet connection for Open-Meteo APIs",
330
+ "Supabase or Firebase credentials",
331
+ ],
332
+ setup_steps=[
333
+ "Configure coordinates and cloud database keys in `.env`.",
334
+ "Run `python coap_env_server.py` in Terminal 1 to fetch live data and start CoAP service.",
335
+ "Run `python coap_env_client.py` in Terminal 2 to fetch readings via CoAP and write to DB.",
336
+ "Run `streamlit run dashboard.py` in Terminal 3 to monitor the environmental dashboard.",
337
+ ],
338
+ viva_preview=[
339
+ ("Why is CoAP suitable for environmental monitoring stations?",
340
+ "Environmental stations frequently operate on solar or battery power with cellular/satellite links. CoAP minimizes transmission energy and packet overhead compared to HTTP."),
341
+ ("How does the bridge architecture work in this practical?",
342
+ "The server polls the Open-Meteo REST API, formats the data into compact JSON, serves it over CoAP UDP, and the client receives and persists it into Supabase/Firebase."),
343
+ ],
344
+ ),
345
+ 11: ProgramInfo(
346
+ id=11,
347
+ code="11",
348
+ title="ISS Real-Time Location Tracking & Trajectory Analysis",
349
+ layer="Data Analytics Layer",
350
+ description=(
351
+ "Continuously collects real orbital telemetry from the International Space Station "
352
+ "at 5-second intervals (default: 100 records ≈ 8.3 minutes) using official live APIs "
353
+ "(Open-Notify with WhereTheISS HTTPS fallback). Computes orbital velocity, distance "
354
+ "traveled via Haversine formula, generates trajectory plots, and renders an analysis dashboard."
355
+ ),
356
+ objective=(
357
+ "Master real-time polling loops, CSV time-series persistence, geospatial distance "
358
+ "calculation (Haversine formula), and dynamic orbital trend visualization."
359
+ ),
360
+ requires_internet=True,
361
+ requires_credentials=False,
362
+ requires_broker=False,
363
+ requires_dataset=False,
364
+ requires_server=False,
365
+ dependencies=["requests", "pandas", "matplotlib", "streamlit"],
366
+ source_info="Live ISS Orbit APIs: Open-Notify (`api.open-notify.org`) and WhereTheISS (`api.wheretheiss.at`)",
367
+ generated_files=[
368
+ "README.md",
369
+ "requirements.txt",
370
+ ".env.example",
371
+ ".gitignore",
372
+ "collector.py",
373
+ "analyzer.py",
374
+ "dashboard.py",
375
+ "data/iss_locations.csv",
376
+ ],
377
+ prerequisites=[
378
+ "Python 3.9+",
379
+ "Active internet connection to reach ISS orbital APIs",
380
+ ],
381
+ setup_steps=[
382
+ "Run `python collector.py --count 100 --interval 5` to collect 100 live orbital records.",
383
+ "Run `python analyzer.py` to calculate orbital metrics and generate trend charts.",
384
+ "Run `streamlit run dashboard.py` to view the interactive trajectory map and stats.",
385
+ ],
386
+ viva_preview=[
387
+ ("How long does it take to collect 100 records at 5-second intervals?",
388
+ "100 samples * 5 seconds = 500 seconds, which is approximately 8.33 minutes (~8 minutes 20 seconds)."),
389
+ ("What is the Haversine formula and why is it used here?",
390
+ "The Haversine formula calculates the great-circle distance between two points on a sphere given their latitudes and longitudes, accounting for Earth's curvature."),
391
+ ],
392
+ ),
393
+ 12: ProgramInfo(
394
+ id=12,
395
+ code="12",
396
+ title="USGS Global Earthquake Statistical & Time-Series Analysis",
397
+ layer="Data Analytics Layer",
398
+ description=(
399
+ "Queries the official USGS Earthquake Hazards API for real seismic events across the "
400
+ "past seven weeks (dynamically calculated at runtime). Performs statistical analysis "
401
+ "on magnitude and focal depth, investigates tsunami triggers, generates analytical "
402
+ "charts, and provides an interactive Streamlit dashboard."
403
+ ),
404
+ objective=(
405
+ "Understand GeoJSON parsing, dynamic runtime date filtering, seismic distribution "
406
+ "statistics, depth vs. magnitude scatter analysis, and interactive data visualization."
407
+ ),
408
+ requires_internet=True,
409
+ requires_credentials=False,
410
+ requires_broker=False,
411
+ requires_dataset=False,
412
+ requires_server=False,
413
+ dependencies=["requests", "pandas", "matplotlib", "streamlit"],
414
+ source_info="USGS Earthquake API (`earthquake.usgs.gov/fdsnws/event/1/query`)",
415
+ generated_files=[
416
+ "README.md",
417
+ "requirements.txt",
418
+ ".env.example",
419
+ ".gitignore",
420
+ "fetch_data.py",
421
+ "analyzer.py",
422
+ "dashboard.py",
423
+ "data/earthquakes.csv",
424
+ ],
425
+ prerequisites=[
426
+ "Python 3.9+",
427
+ "Active internet connection for USGS API",
428
+ ],
429
+ setup_steps=[
430
+ "Run `python fetch_data.py` to fetch seismic events from the past 7 weeks.",
431
+ "Run `python analyzer.py` to produce statistical summaries and charts in `output/`.",
432
+ "Run `streamlit run dashboard.py` to launch the interactive seismic dashboard.",
433
+ ],
434
+ viva_preview=[
435
+ ("Why is the date range calculated dynamically at runtime instead of hardcoded?",
436
+ "Earthquake activity is continuous; dynamically computing `today - 49 days` ensures the practical always analyzes recent, relevant real-world seismic data rather than expired historical dates."),
437
+ ("What information does focal depth provide about an earthquake?",
438
+ "Focal depth (0-70 km shallow, 70-300 km intermediate, >300 km deep) indicates where the rupture occurred; shallow quakes typically cause greater surface destruction."),
439
+ ],
440
+ ),
441
+ 13: ProgramInfo(
442
+ id=13,
443
+ code="13",
444
+ title="Kochi Meteorological Weather Trend & Correlation Analysis",
445
+ layer="Data Analytics Layer",
446
+ description=(
447
+ "Extracts real meteorological observations for Kochi (Lat: 9.93, Lon: 76.27) from "
448
+ "Open-Meteo. Evaluates temperature, relative humidity, and wind speed trends, "
449
+ "computes Pearson correlation between temperature and humidity, and presents findings "
450
+ "via plots and a dashboard."
451
+ ),
452
+ objective=(
453
+ "Analyze weather variables, compute statistical dispersions (mean, std, range), "
454
+ "quantify inverse correlation between temperature and relative humidity, and build dashboards."
455
+ ),
456
+ requires_internet=True,
457
+ requires_credentials=False,
458
+ requires_broker=False,
459
+ requires_dataset=False,
460
+ requires_server=False,
461
+ dependencies=["requests", "pandas", "matplotlib", "streamlit"],
462
+ source_info="Open-Meteo Weather API (Kochi coordinates: 9.93°N, 76.27°E)",
463
+ generated_files=[
464
+ "README.md",
465
+ "requirements.txt",
466
+ ".env.example",
467
+ ".gitignore",
468
+ "fetch_weather.py",
469
+ "analyzer.py",
470
+ "dashboard.py",
471
+ "data/weather_kochi.csv",
472
+ ],
473
+ prerequisites=[
474
+ "Python 3.9+",
475
+ "Active internet connection for Open-Meteo API",
476
+ ],
477
+ setup_steps=[
478
+ "Execute `python fetch_weather.py` to retrieve Kochi meteorological records.",
479
+ "Execute `python analyzer.py` to compute statistical metrics and trend charts.",
480
+ "Execute `streamlit run dashboard.py` to launch the meteorological dashboard.",
481
+ ],
482
+ viva_preview=[
483
+ ("What correlation is typically observed between temperature and relative humidity?",
484
+ "An inverse (negative) correlation is standard; as temperature rises during daytime, the saturation vapor pressure of air increases, reducing relative humidity if absolute moisture is constant."),
485
+ ("Why is Open-Meteo API popular for IoT and educational projects?",
486
+ "It provides high-precision global meteorological forecasts and historical reanalysis without requiring API keys, rate-limiting hurdles, or credit card registration."),
487
+ ],
488
+ ),
489
+ 14: ProgramInfo(
490
+ id=14,
491
+ code="14",
492
+ title="Kochi Hourly Air Quality & Health Index Analysis",
493
+ layer="Data Analytics Layer",
494
+ description=(
495
+ "Retrieves authentic hourly air-quality measurements for Kochi from Open-Meteo "
496
+ "Air Quality API (PM10, PM2.5, US AQI). Performs time-series and statistical analysis, "
497
+ "distinguishes between raw API observations, missing values, and derived statistics, "
498
+ "and visualizes trends."
499
+ ),
500
+ objective=(
501
+ "Learn particulate matter dynamics ($\text{PM}_{2.5}$ vs. $\text{PM}_{10}$), US AQI health "
502
+ "hazard categorization, diurnal air quality peaks, and time-series plotting."
503
+ ),
504
+ requires_internet=True,
505
+ requires_credentials=False,
506
+ requires_broker=False,
507
+ requires_dataset=False,
508
+ requires_server=False,
509
+ dependencies=["requests", "pandas", "matplotlib", "streamlit"],
510
+ source_info="Open-Meteo Air Quality API (Lat: 9.93, Lon: 76.27)",
511
+ generated_files=[
512
+ "README.md",
513
+ "requirements.txt",
514
+ ".env.example",
515
+ ".gitignore",
516
+ "fetch_air_quality.py",
517
+ "analyzer.py",
518
+ "dashboard.py",
519
+ "data/air_quality_kochi.csv",
520
+ ],
521
+ prerequisites=[
522
+ "Python 3.9+",
523
+ "Active internet connection for Open-Meteo Air Quality API",
524
+ ],
525
+ setup_steps=[
526
+ "Run `python fetch_air_quality.py` to fetch hourly PM10, PM2.5, and US AQI data.",
527
+ "Run `python analyzer.py` to compute dispersion metrics and generate plots.",
528
+ "Run `streamlit run dashboard.py` to inspect hourly trends and health categories.",
529
+ ],
530
+ viva_preview=[
531
+ ("What is the aerodynamic difference between PM2.5 and PM10?",
532
+ r"PM10 refers to inhalable particles $\le 10\,\mu\text{m}$ (dust, pollen), while PM2.5 refers to fine inhalable particles $\le 2.5\,\mu\text{m}$ (combustion smoke) that can penetrate deep into alveoli and bloodstream."),
533
+ ("How are missing observations handled in air quality time series?",
534
+ "They must be clearly distinguished from zero readings (zero is a measurement, NaN is absence of data) and handled via forward filling, linear interpolation, or exclusion during mean computation."),
535
+ ],
536
+ ),
537
+ 15: ProgramInfo(
538
+ id=15,
539
+ code="15",
540
+ title="Decision Tree Office Room Occupancy Classification",
541
+ layer="Machine Learning Layer",
542
+ description=(
543
+ "Loads the authentic UCI Occupancy Detection dataset (CC BY 4.0). Analyzes environmental "
544
+ "features (Temperature, Humidity, Light, CO2, HumidityRatio), trains a Decision Tree "
545
+ "classifier to predict room occupancy (0 or 1), evaluates with accuracy, classification report, "
546
+ "and confusion matrix, saves model with joblib, and provides real-time single-sample inference."
547
+ ),
548
+ objective=(
549
+ "Master Decision Tree classification, feature-target separation, Gini impurity / entropy "
550
+ "splitting criteria, confusion matrix interpretation, and model persistence."
551
+ ),
552
+ requires_internet=False, # Bundled / downloaded official dataset
553
+ requires_credentials=False,
554
+ requires_broker=False,
555
+ requires_dataset=True,
556
+ requires_server=False,
557
+ dependencies=["pandas", "scikit-learn", "matplotlib", "joblib", "streamlit"],
558
+ source_info="UCI Machine Learning Repository Dataset #357 (Occupancy Detection, CC BY 4.0)",
559
+ generated_files=[
560
+ "README.md",
561
+ "requirements.txt",
562
+ ".env.example",
563
+ ".gitignore",
564
+ "train.py",
565
+ "predict.py",
566
+ "dashboard.py",
567
+ "data/datatraining.txt",
568
+ "data/download_dataset.py",
569
+ ],
570
+ prerequisites=[
571
+ "Python 3.9+",
572
+ "Scikit-learn, Pandas, Matplotlib installed",
573
+ ],
574
+ setup_steps=[
575
+ "Verify official dataset is present in `data/datatraining.txt` (or run `python data/download_dataset.py`).",
576
+ "Run `python train.py` to train the Decision Tree, evaluate metrics, and save `model/occupancy_tree.joblib`.",
577
+ "Run `python predict.py --temp 23.2 --humidity 27.2 --light 426 --co2 721` to classify a new reading.",
578
+ "Run `streamlit run dashboard.py` to test the model interactively.",
579
+ ],
580
+ viva_preview=[
581
+ ("Which feature is typically the strongest predictor of office room occupancy?",
582
+ "Light (Lux) is usually the most informative immediate feature because office lights are turned on when occupied, followed closely by CO2 levels rising from human respiration."),
583
+ ("What is the advantage of Decision Trees for IoT edge deployment?",
584
+ "Trained Decision Trees consist of simple if-else comparison thresholds, making them computationally lightweight and capable of running on low-power microcontrollers without float-heavy operations."),
585
+ ],
586
+ ),
587
+ 16: ProgramInfo(
588
+ id=16,
589
+ code="16",
590
+ title="Linear Regression Ambient Temperature Prediction",
591
+ layer="Machine Learning Layer",
592
+ description=(
593
+ "Utilizes the genuine UCI Beijing Air Quality dataset (UCI ID 501 / PRSA, CC BY 4.0). "
594
+ "Inspects actual columns, explains feature availability (PM2.5, DEWP, PRES, wind speed), "
595
+ "handles missing values, trains a Linear Regression model to predict temperature, evaluates "
596
+ "with MAE, MSE, RMSE, and R², saves model with joblib, and supports new reading predictions."
597
+ ),
598
+ objective=(
599
+ "Perform rigorous column inspection on real datasets, handle missing observations, "
600
+ "train multivariable linear regression, evaluate regression loss metrics, and explain coefficients."
601
+ ),
602
+ requires_internet=False, # Bundled / downloaded official dataset
603
+ requires_credentials=False,
604
+ requires_broker=False,
605
+ requires_dataset=True,
606
+ requires_server=False,
607
+ dependencies=["pandas", "scikit-learn", "matplotlib", "joblib"],
608
+ source_info="UCI Machine Learning Repository Dataset #501 (Beijing Multi-Site / PM2.5, CC BY 4.0)",
609
+ generated_files=[
610
+ "README.md",
611
+ "requirements.txt",
612
+ ".env.example",
613
+ ".gitignore",
614
+ "train.py",
615
+ "predict.py",
616
+ "column_inspection.py",
617
+ "data/beijing_sample.csv",
618
+ "data/download_dataset.py",
619
+ ],
620
+ prerequisites=[
621
+ "Python 3.9+",
622
+ "Scikit-learn, Pandas, Matplotlib installed",
623
+ ],
624
+ setup_steps=[
625
+ "Run `python column_inspection.py` to inspect actual dataset columns and confirm verified features.",
626
+ "Run `python train.py` to train Linear Regression, print MAE/MSE/RMSE/R², and save model.",
627
+ "Run `python predict.py --dewp 12.0 --pres 1012.0 --pm25 35.0 --wind 2.5` to predict temperature.",
628
+ ],
629
+ viva_preview=[
630
+ ("Why did we check columns in the Beijing dataset before training?",
631
+ "In the original UCI 501 Beijing PM2.5 dataset, PM10 is absent (it features pm2.5, DEWP, TEMP, PRES, Iws). Inspecting columns prevents runtime crashes and ensures feature transparency."),
632
+ ("What does an R² score of 0.85 indicate in regression?",
633
+ "It means 85% of the variance in the target variable (temperature) is explained by the input features in the linear model."),
634
+ ],
635
+ ),
636
+ 17: ProgramInfo(
637
+ id=17,
638
+ code="17",
639
+ title="Random Forest Multi-Class Air Quality Classification",
640
+ layer="Machine Learning Layer",
641
+ description=(
642
+ "Applies the genuine Kaggle 'Air Quality Data in India' dataset (`city_day.csv`, CC0). "
643
+ "Extracts pollutant features (PM2.5, PM10, NO2, CO, SO2, O3), classifies into CPCB standard "
644
+ "AQI buckets (Good, Satisfactory, Moderate, Poor, Very Poor, Severe), trains a Random Forest "
645
+ "classifier, computes feature importances, evaluates with confusion matrix, and enables inference."
646
+ ),
647
+ objective=(
648
+ "Understand ensemble learning with Random Forests, multi-class imbalance handling, "
649
+ "authoritative CPCB AQI threshold mapping, Gini-based feature ranking, and model export."
650
+ ),
651
+ requires_internet=False, # Bundled / downloaded official dataset
652
+ requires_credentials=False,
653
+ requires_broker=False,
654
+ requires_dataset=True,
655
+ requires_server=False,
656
+ dependencies=["pandas", "scikit-learn", "matplotlib", "joblib", "streamlit"],
657
+ source_info="Kaggle Indian Air Quality Dataset (Rohan Rao, CC0 Public Domain, `city_day.csv`)",
658
+ generated_files=[
659
+ "README.md",
660
+ "requirements.txt",
661
+ ".env.example",
662
+ ".gitignore",
663
+ "train.py",
664
+ "predict.py",
665
+ "dashboard.py",
666
+ "data/city_day_sample.csv",
667
+ "data/download_dataset.py",
668
+ ],
669
+ prerequisites=[
670
+ "Python 3.9+",
671
+ "Scikit-learn, Pandas, Matplotlib installed",
672
+ ],
673
+ setup_steps=[
674
+ "Verify `data/city_day_sample.csv` or run `python data/download_dataset.py` to download full dataset.",
675
+ "Run `python train.py` to train Random Forest, view feature importances, and save model.",
676
+ "Run `python predict.py --pm25 85 --pm10 140 --no2 32 --co 1.2 --so2 15 --o3 45` to classify air quality.",
677
+ "Run `streamlit run dashboard.py` to launch the interactive air quality predictor.",
678
+ ],
679
+ viva_preview=[
680
+ ("Why is Random Forest less prone to overfitting than a single Decision Tree?",
681
+ "Random Forest builds multiple decision trees on random bootstrap subsets of data and random subsets of features (bagging), averaging predictions to reduce variance."),
682
+ ("What are the official CPCB Indian AQI category ranges?",
683
+ "Good: 0-50, Satisfactory: 51-100, Moderate: 101-200, Poor: 201-300, Very Poor: 301-400, Severe: 401-500."),
684
+ ],
685
+ ),
686
+ }
687
+
688
+
689
+ def get_program(program_id: int) -> ProgramInfo:
690
+ """Retrieves program info by integer ID, strictly enforcing valid range (5-17)."""
691
+ if program_id in EXCLUDED_PROGRAM_IDS:
692
+ raise ValueError(
693
+ f"Program {program_id} is explicitly excluded from the syllabus scope. "
694
+ f"Only Programs 5 through 17 are supported."
695
+ )
696
+ if program_id not in SUPPORTED_PROGRAM_IDS:
697
+ raise ValueError(
698
+ f"Invalid Program ID {program_id}. Supported programs are 5 through 17."
699
+ )
700
+ return PROGRAMS[program_id]
701
+
702
+
703
+ def list_programs() -> List[ProgramInfo]:
704
+ """Returns a sorted list of all supported programs."""
705
+ return [PROGRAMS[pid] for pid in sorted(PROGRAMS.keys())]