Skip to content

Add a new data source

How to add new data the school serves publicly (course lists, menus, other public data). Sources are independent and share only the common layer (common: errors, HTTP transport, version). Authentication (auth) is one source just like public data, so you can build yours without touching the others.

  1. Propose it in an issue. Use the 새 정보 출처 제안 (new data source proposal) issue template. Describe where it is served, whether login is needed, the request form (JSON or HTML), the fields, and whether it contains personal data. Information that exists only as HTML is not used as a primary source.
  2. Write the contract first. Put the address, request keys and fixed values under publicData in spec/contract.json (each language keeps its own constants and its contract test compares them), then add the method, request and field mapping to spec/PUBLIC_DATA.md, one line each, with the date checked and the account type you used. Add the same method to the agent-facing method list spec/api-index.json too (parameters, return model, Java signatures). python3 scripts/check-api-index.py fails if it drifts from the three implementations.
  3. Create synthetic fixtures and expected results. Add them to FIXTURES and the per-source map_*() functions (merged by map_all()) in scripts/generate-public-data-fixtures.py and run it. Never copy a real response, and never put in values that identify a person, such as a staff name.
  4. Implement Java, Python and Node in the same PR. Put the request building and mapping in one file per source (<source>_source.py, <source>Source.ts, <source>Source.java) and add only a one-line public method to SejongPublicData. Reuse the shared file (fetcher) for cache and failure isolation, and follow the contract’s value rules (empty string when missing, extra).
  5. Write only the tests you need. One test that the fixture result equals the expected result is enough. Do not add tests that call the school’s servers.
  6. Add a shape check. Add a row to SOURCES in scripts/probe-public-data.py. It runs without an account.
  7. Go through the feature exposure checklist.

For a public-data source. One method maps to three files.

Language Method and request Result model Test
Python python/src/sejongpeia/public_data/<source>_source.py (request, mapping), the public method line in client.py public_data/models.py python/tests/test_public_data.py
Node node/src/publicData/<source>Source.ts (request, mapping), the public method line in client.ts, argument objects in queries.ts publicData/models.ts node/test/publicData.test.mjs
Java publicdata/<source>Source.java (request, mapping), the public methods in SejongPublicData.java publicdata/model/ SejongPublicDataTest.java
  • Method names are snake_case in Python and camelCase in Node and Java. School fields you do not map go into extra.
  • Login-required sources are not covered by this procedure. Sources cannot import each other, so how a login session would be shared needs a design; agree on the direction in an issue first.
  • When you add or change a public method or result field, check that the “Public API” list in API stability and the integration examples are still correct.

The rule has three layers. Common (common) imports no source, and each source (auth, public_data) imports only common and never another source. The top-level entry points (__init__.py, index.ts, Spring config) only assemble, so they may import every layer. Tests enforce it.

Language Rule test
Python python/tests/test_dependency_rules.py
Node node/test/dependencyRules.test.mjs
Java DependencyRulesTest

Inside public data, source files (*_source) never import each other or the entry point (client); they use only the shared files (fetcher, row_reader, models). If a rule fails, move what needs sharing into common.

Folder layout (same shape in all three languages)

Layer Python python/src/sejongpeia/ Node node/src/ Java java/src/main/java/kr/suhsaechan/sejongpeia/
Common common/ (errors, transport, version) common/ (errors, transport, version) common/ (exception, util, http, config)
Authentication auth/ (client, parsers, portal_main_parser, models) auth/ (client, parsers, portalMainParser, models) auth/ (client, parser, model, service)
Public data public_data/ (client, fetcher, row_reader, models, *_source) publicData/ (client, fetcher, rowReader, queries, models, *Source) publicdata/ (SejongPublicData, *Source, PublicDataFetcher, model)
Assembly __init__.py index.ts config/ (Spring auto-configuration, TLS setup)

The contract between the school’s services and the three implementations lives in spec/. When the school changes something, change spec/ first, then all three implementations in the same PR.

Kind Location Rule
Request and response contract, field meanings, limits spec/<source>.md One document per source. Write only what was verified, and say what was not
Values shared by the implementations (URLs, error messages) spec/contract.json The single source. Each language’s contract test fails if its constants drift
Where the school serves what, dead ends, what was not checked spec/REFERENCE_DATA_SURVEY.md Read it before searching again. Update it when you look somewhere new
Sample responses for tests spec/fixtures/ Synthetic only. Never real responses, personal data, session values or staff names
Shape drift detection scripts/probe-*.py Runs without an account, one entry per source
  • Return the school’s raw strings. No value lists or code tables in the library, no guessing a code from a name.
  • A failure in one source never blocks another, authentication in particular. Return an empty value or the last cached value.
  • Do not log or return internal server details (stack traces, session identifiers, IP addresses, password-state flags).
  • Be gentle with public pages and endpoints: cache, space requests out, and bound the number of requests per refresh.

See also Failure isolation and cache and Common rules.