Skip to main content

susunjadwal-backend - Documentation

Document Status
Document OwnerProduct Engineering 2026Product Engineering 2026
Contributors
Project Linkshttps://github.com/ristekoss/susunjadwal-backendhttps://github.com/ristekoss/susunjadwal-backend
Project Linkshttps://api-ulaskelas.ristek.cs.ui.ac.id
https://api-ulaskelas-stg.ristek.cs.ui.ac.id
https://api-ulaskelas.ristek.cs.ui.ac.id
https://api-ulaskelas-stg.ristek.cs.ui.ac.id
Project Links[Monitoring Dashboard][Monitoring Dashboard]
Project Links[API Documentation][API Documentation]
TeamTheodore Kevin Himawantheodore.kevin@ristek.cs.ui.ac.id
TeamAri Darrell Muljonodarrell@ristek.cs.ui.ac.id
TeamGrace Karinagracekarin@ristek.cs.ui.ac.id
TeamYeshua Marco G. Manurungmarco@ristek.cs.ui.ac.id

πŸ”Ž Background

Overview​

Susun Jadwal backend provides authentication, course data retrieval, user schedule storage, review submission, and course scraping workflows. The service exists so UI students can sign in with SSO, fetch available courses per major and period, build schedule variants, and request fresh course data when the source data in SIAK changes.

Stakeholders​

  • Users: UI students and admins
  • Upstream Dependencies: UI CAS/SSO, SIAK academic data sources, RabbitMQ, MongoDB
  • Downstream Consumers: susunjadwal-frontend

Scope & Boundaries​

  • In Scope: Flask API, MongoDB document models, JWT auth, review moderation, uploader flow, RabbitMQ-based scraping triggers, SSE-based SIAKNG scraping
  • Out of Scope: frontend implementation, legacy data migration, and analytics pipelines

βš™οΈ Architecture & Design

System Diagram​

Architecture Decisions (ADRs)​

  • Language/Framework: Python, Flask, MongoEngine, Pydantic
  • Database: MongoDB
  • Communication: Synchronous HTTP calls

πŸ’»Technical Specifications

API Documentation​

GET /​

Response 200

{ "message": "susunjadwal is live!" }
GET /review​

Response 200

{ "message": "review feature is up" }
GET /scrape-siak-ng/status​

Response 200

{
"message": "SIAK NG scraper service is running",
"version": "1.0"
}

Authentication​

POST /auth/​

Request

{ "ticket": "string", "service_url": "string" }

Response 200

{
"user_id": "<mongo_id>",
"major_id": "<mongo_id>",
"token": "<jwt>",
"err": false
}
POST /auth/v2/​

Request

{ "ticket": "string", "service_url": "string" }

Response 200

{
"user_id": "<mongo_id>",
"major_id": "<mongo_id>",
"token": "<jwt>",
"err": false
}

Response 201

{
"user_name": "string",
"full_name": "string",
"completion_id": "<uuid>"
}
POST /auth/completion/​

Request

{
"completion_id": "<uuid>",
"npm": "string",
"kd_org": "string"
}

Response 200

{
"user_id": "<mongo_id>",
"major_id": "<mongo_id>",
"token": "<jwt>",
"err": false
}
GET /auth/me​

Response 200

{ "message": "token is valid", "token_is_valid": true }

Faculties & Majors​

GET /faculties​

Response 200

["Faculty A", "Faculty B"]
GET /faculty/&lt;faculty_idx&gt;/majors​

Response 200

{ "kd_org": "Major Name" }

Courses & Periods​

GET /majors/&lt;kd_org&gt;/courses_by_kd​

Response 200

{
"name": "2025-2",
"courses": [
{
"name": "string",
"credit": 3,
"classes": [
{
"name": "A",
"schedule_items": [
{
"day": "Mon",
"start": "08.00",
"end": "09.40"
}
]
}
]
}
]
}
GET /majors/&lt;major_id&gt;/all_courses​

Response 200

{
"courses": [
{
"name": "string",
"code": "string",
"credit": 3
}
]
}
GET /courses​

Response 200

{
"courses": [
{
"name": "string",
"code": "string",
"credit": 3
}
]
}

User Schedules​

POST /users/&lt;user_id&gt;/user_schedule​

Request

{
"schedule_items": [
{
"name": "Class A",
"day": "Mon",
"start": "08.00",
"end": "09.40"
}
]
}

Response 201

{ "id": "<user_schedule_id>" }
GET /user_schedules/​

Response 200

{
"user_schedule": {
"id": "<id>",
"name": "Schedule 1",
"schedule_items": []
}
}
GET /users/&lt;user_id&gt;/user_schedules​

Response 200

{
"user_schedules": [
{
"id": "<id>",
"name": "Schedule 1"
}
]
}
PUT /users/&lt;user_id&gt;/user_schedules/​

Request

{ "schedule_items": [] }

Response 200

{
"user_schedule": {
"id": "<id>",
"name": "Schedule 1"
}
}
POST /users/&lt;user_id&gt;/user_schedules//change_name​

Request

{ "name": "New Name" }

Response 200

{ "id": "<id>", "name": "New Name" }
DELETE /users/&lt;user_id&gt;/user_schedules/​

Response 204 Scraping

POST /scrape-schedule​

Request

{ "username": "string", "password": "string" }
POST /scrape-siak-ng​

Request

{ "username": "string", "password": "string" }

Reviews​

POST /review/&lt;user_id&gt;​

Request

{ "rating": 5, "comment": "Great app" }

Response 201

{ "message": "Review saved" }

Admin​

POST /admin/login​

Request

{ "username": "string", "password": "string" }

Response 200

{ "token": "<admin_jwt>" }
GET /admin/reviews-overview​

Response 200

{
"average_rating": 4.5,
"rating_counts": { "5": 10 }
}
GET /admin/reviews/list​

Response 200

{
"reviews": [
{
"id": "<id>",
"rating": 5,
"comment": "Great"
}
]
}
PATCH /admin/review/status/​

Request

{ "reviewed": true }

Response 200

{ "id": "<id>", "reviewed": true }
DELETE /admin/review/delete/​

Response 200

{ "message": "Review deleted successfully." }

Data Model/Schema​

Project Structure​

susunjadwal-backend/
β”œβ”€β”€ .github/
β”‚ β”œβ”€β”€ issue-template.md # issue template for repository contributors
β”‚ └── workflows/
β”‚ β”œβ”€β”€ deploy-prod.yaml # production deployment pipeline
β”‚ └── deploy-staging.yaml # staging deployment pipeline
β”œβ”€β”€ .dockerignore # Docker build ignore rules
β”œβ”€β”€ .env.example # example environment variable file
β”œβ”€β”€ .gitignore # Git ignore rules
β”œβ”€β”€ app/
β”‚ β”œβ”€β”€ __init__.py # Flask app setup, config, blueprints, Mongo, CORS, Sentry, RabbitMQ init
β”‚ β”œβ”€β”€ cron.py # disabled cron blueprint and scheduled task hooks
β”‚ β”œβ”€β”€ decorators.py # JWT and owner/admin authorization decorators
β”‚ β”œβ”€β”€ exceptions/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── auth.py # authentication-related custom exceptions
β”‚ β”œβ”€β”€ jwt_utils.py # JWT encode/decode helpers
β”‚ β”œβ”€β”€ message_queue.py # RabbitMQ connection/pool helpers
β”‚ β”œβ”€β”€ utils.py # shared Flask-side utility helpers
β”‚ β”œβ”€β”€ services/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ auth/
β”‚ β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”‚ └── auth.py # auth v2 and onboarding completion logic
β”‚ β”‚ └── scrapper/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── schedule_scrapper.py # RabbitMQ-backed course refresh orchestration
β”‚ └── views/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ auth.py # auth and metadata endpoints
β”‚ β”œβ”€β”€ main.py # courses and user schedules
β”‚ β”œβ”€β”€ review.py # review submission
β”‚ β”œβ”€β”€ admin.py # admin review moderation
β”‚ └── scraper.py # SSE-based SIAK NG scraping
β”œβ”€β”€ models/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ admin.py # admin credential model
β”‚ β”œβ”€β”€ major.py # academic major document
β”‚ β”œβ”€β”€ period.py # period, course, class, and schedule embedded schema
β”‚ β”œβ”€β”€ review.py # user review document
β”‚ β”œβ”€β”€ user.py # user profile and scraping timestamps
β”‚ └── user_schedule.py # saved user schedule document
β”œβ”€β”€ scraper/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ credentials.template.json # template credential payload for scraper flows
β”‚ β”œβ”€β”€ main.py # HTML parsing and uploader ingestion helpers
β”‚ └── siak_ng_scraper.py # direct SIAK NG scraping and SSE event formatting
β”œβ”€β”€ scripts/
β”‚ β”œβ”€β”€ init-mongo.sh # initialize non-root MongoDB user
β”‚ β”œβ”€β”€ launch.sh # local Flask launch entrypoint
β”‚ β”œβ”€β”€ mongo_dump.sh # MongoDB dump helper
β”‚ └── start.sh # alternate server startup script
β”œβ”€β”€ sso/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ additional-info.json # major/faculty metadata lookup
β”‚ β”œβ”€β”€ cas.py # CAS client implementation
β”‚ β”œβ”€β”€ faculty-base-additional-info.json # faculty base metadata
β”‚ β”œβ”€β”€ faculty_exchange_route.json # RabbitMQ routing map by faculty
β”‚ └── utils.py # SSO/CAS helper functions
β”œβ”€β”€ uploader/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ decorators.py # uploader cookie auth guards
β”‚ β”œβ”€β”€ templates/
β”‚ β”‚ β”œβ”€β”€ login.html # uploader login page
β”‚ β”‚ └── upload.html # uploader upload form
β”‚ β”œβ”€β”€ utils.py # uploader auth and URL helpers
β”‚ └── views.py # uploader login, logout, and HTML upload endpoints
β”œβ”€β”€ widgets/
β”‚ β”œβ”€β”€ __init__.py
β”‚ └── flask_pika_mod.py # Flask-Pika integration wrapper
β”œβ”€β”€ cron.sh # cron/container helper script
β”œβ”€β”€ Dockerfile # application container build
β”œβ”€β”€ LICENSE.md # repository license
β”œβ”€β”€ README.md # contributor and setup guide
β”œβ”€β”€ TECH_DOC.md # technical documentation
β”œβ”€β”€ dev.docker-compose.yaml # local MongoDB and RabbitMQ stack
β”œβ”€β”€ docker-compose.yaml # full local container stack
└── requirements.txt # Python dependency list

Components​

LayerPathsResponsibility
App Bootstrapapp/init.pyInitializes Flask configuration, loads SSO metadata files, registers all blueprints, enables CORS, connects to MongoDB, initializes RabbitMQ, and starts background scraping consumer threads.
Route Layerapp/views/*.py, uploader/views.pyExposes HTTP endpoints under /susunjadwal/api, organized by domain including authentication, course catalog, user schedules, reviews, admin moderation, scraping, and uploader workflows.
Auth Servicesapp/services/auth/auth.py, app/jwt_utils.py, app/decorators.py, uploader/decorators.pyHandles CAS-based authentication, JWT generation and validation, onboarding completion flow, role-based access control (user/admin), and uploader cookie authentication.
Domain Modelsmodels/*.pyDefines MongoEngine document schemas for admins, users, majors, periods, courses, reviews, and user schedules, including embedded structures for nested data.
Scraping Services for SIAKNGapp/services/scrapper/schedule_scrapper.py, scraper/main.py, scraper/siak_ng_scraper.pyManages course data scraping via RabbitMQ jobs, parses uploaded HTML files, performs SIAK NG scraping, streams progress using SSE, and stores results into Period documents.
Messaging and Integrationapp/message_queue.py, sso/cas.py, sso/utils.pyProvides RabbitMQ connection handling, CAS client implementation, and SSO utility functions used across services.
Review and Admin Workflowapp/views/review.py, app/views/admin.py, models/review.py, models/admin.pyHandles user review submission, computes aggregated ratings, supports paginated review listing, and enables admin moderation including status updates and deletion.
Uploader Workflowuploader/views.py, uploader/utils.py, scraper/main.pySupports uploader login with whitelist validation, processes HTML uploads, validates period and major, extracts course data, and stores detailed course information for the active period.

☁️ Operational Playbook

Infrastructure​

Environment Variables​

KeyDescriptionDefault (Dev)Sensitive?
ACTIVE_PERIODActive academic period used for filtering courses (e.g. semester identifier)2025-2No
ADMIN_CREDENTIAL_VERIFICATIONMethod or flag used to verify admin privilegesissuperuserNo
MONGODB_DBName of the MongoDB database used by the backendbackendNo
MONGODB_HOSTHostname or IP address of the MongoDB serverβ€”Yes
MONGODB_PASSWORDPassword for MongoDB authenticationβ€”Yes
MONGODB_PORTPort used to connect to MongoDB27017No
MONGODB_USERNAMEUsername for MongoDB authenticationβ€”Yes
RABBITMQ_ERLANG_COOKIEShared secret used by RabbitMQ nodes for clusteringβ€”Yes
RABBIT_HOSTHostname of the RabbitMQ serverβ€”Yes
RABBIT_PASSWORDPassword for RabbitMQ authenticationβ€”Yes
RABBIT_USERNAMEUsername for RabbitMQ authenticationβ€”Yes
SECRET_KEYSecret key used for JWT signing and application securityβ€”Yes
SENTRY_DSNData Source Name for Sentry error tracking serviceβ€”Yes
SSO_UI_FORCE_HTTPSFlag to enforce HTTPS in SSO redirectsTRUENo
UPDATE_COURSE_LIST_EXCHANGE_NAMEName of RabbitMQ exchange for course list updatesupdate_courseNo

πŸ™‹Questions

πŸ—’ List of frequently asked questions or question that need to be answered that is related to this initiative ..