Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "datagsm-openapi-sdk"
version = "1.1.1"
version = "1.2.0"
description = "Python SDK for DataGSM OpenAPI"
readme = "README.md"
requires-python = ">=3.9"
Expand Down Expand Up @@ -93,8 +93,6 @@ select = [
"RUF", # Ruff-specific rules
]
ignore = [
"ANN101", # Missing type annotation for self
"ANN102", # Missing type annotation for cls
"ANN401", # Dynamically typed expressions (Any) - needed for *args in context managers
"N818", # Exception naming - DataGsmException is the intended name
"UP035", # typing.Type vs type - Python 3.9 compatibility
Expand Down
4 changes: 3 additions & 1 deletion src/datagsm_openapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
ScheduleRequest,
StudentApi,
StudentRequest,
TimetableRequest,
)
from .client import DataGsmClient
from .exceptions import (
Expand All @@ -36,7 +37,7 @@
ValidationException,
)

__version__ = "1.1.1"
__version__ = "1.2.0"

__all__ = [
"BadRequestException",
Expand All @@ -56,6 +57,7 @@
"ServerErrorException",
"StudentApi",
"StudentRequest",
"TimetableRequest",
"UnauthorizedException",
"ValidationException",
]
3 changes: 2 additions & 1 deletion src/datagsm_openapi/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""API modules for DataGSM OpenAPI SDK."""

from .club import ClubApi, ClubRequest
from .neis import MealRequest, NeisApi, ScheduleRequest
from .neis import MealRequest, NeisApi, ScheduleRequest, TimetableRequest
from .project import ProjectApi, ProjectRequest
from .student import StudentApi, StudentRequest

Expand All @@ -15,4 +15,5 @@
"ScheduleRequest",
"StudentApi",
"StudentRequest",
"TimetableRequest",
]
85 changes: 80 additions & 5 deletions src/datagsm_openapi/api/neis.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import date as Date
from typing import Optional

from ..models import Meal, Schedule
from ..models import Meal, MealResponse, Schedule, ScheduleResponse, Timetable, TimetableResponse
from ._base import BaseApi


Expand Down Expand Up @@ -68,10 +68,47 @@ def to_params(self) -> dict[str, Optional[object]]:
return params


@dataclass
class TimetableRequest:
"""시간표 조회 요청 파라미터 (Timetable Query Parameters).

Use either 'date' for a single day or 'start_date' and 'end_date' for a date range.
Either 'date' or both 'start_date' and 'end_date' must be provided.

Attributes:
grade: Grade to query (1-3)
class_num: Class number to query (1-4)
date: Single date to query
start_date: Start date for range query
end_date: End date for range query
"""

grade: int = 0
class_num: int = 0
date: Optional[Date] = None
start_date: Optional[Date] = None
end_date: Optional[Date] = None

def to_params(self) -> dict[str, Optional[object]]:
"""Convert to query parameters dictionary.

Returns:
Dictionary of query parameters
"""
params: dict[str, Optional[object]] = {
"grade": self.grade,
"classNum": self.class_num,
"date": self.date,
"startDate": self.start_date,
"endDate": self.end_date,
}
return params


class NeisApi(BaseApi):
"""NEIS 데이터 API (NEIS Data API).

Provides methods for querying school meal and schedule information from NEIS.
Provides methods for querying school meal, schedule, and timetable information from NEIS.
"""

def get_meals(self, request: Optional[MealRequest] = None) -> list[Meal]:
Expand Down Expand Up @@ -104,7 +141,10 @@ def get_meals(self, request: Optional[MealRequest] = None) -> list[Meal]:
>>> week_meals = api.get_meals(request)
"""
req = request or MealRequest(date=Date.today())
return self._get("/v1/neis/meals", params=req.to_params(), response_type=list[Meal])
response = self._get(
"/v1/neis/meals", params=req.to_params(), response_type=MealResponse
)
return response.meals

def get_schedules(self, request: Optional[ScheduleRequest] = None) -> list[Schedule]:
"""학사일정 정보 조회 (Get Schedule Information).
Expand Down Expand Up @@ -136,6 +176,41 @@ def get_schedules(self, request: Optional[ScheduleRequest] = None) -> list[Sched
>>> month_schedules = api.get_schedules(request)
"""
req = request or ScheduleRequest(date=Date.today())
return self._get(
"/v1/neis/schedules", params=req.to_params(), response_type=list[Schedule]
response = self._get(
"/v1/neis/schedules", params=req.to_params(), response_type=ScheduleResponse
)
return response.schedules

def get_timetables(self, request: TimetableRequest) -> list[Timetable]:
"""시간표 정보 조회 (Get Timetable Information).

Query school timetable information for a specific grade, class, and date or date range.
Either 'date' or both 'start_date' and 'end_date' must be provided in the request.

Args:
request: Query parameters (grade, class_num, and date/date range are required)

Returns:
List of timetables

Example:
>>> from datetime import date
>>> api = NeisApi(http_client)
>>>
>>> # Get timetable for a specific date
>>> request = TimetableRequest(grade=1, class_num=1, date=date(2026, 4, 7))
>>> timetables = api.get_timetables(request)
>>>
>>> # Get timetables for a date range
>>> request = TimetableRequest(
... grade=2,
... class_num=3,
... start_date=date(2026, 4, 7),
... end_date=date(2026, 4, 11)
... )
>>> week_timetables = api.get_timetables(request)
"""
response = self._get(
"/v1/neis/timetables", params=request.to_params(), response_type=TimetableResponse
)
return response.timetables
6 changes: 5 additions & 1 deletion src/datagsm_openapi/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
StudentRole,
StudentSortBy,
)
from .neis import Meal, Schedule
from .neis import Meal, MealResponse, Schedule, ScheduleResponse, Timetable, TimetableResponse
from .project import ParticipantInfo, Project, ProjectResponse
from .student import Student, StudentResponse

Expand All @@ -28,16 +28,20 @@
"CommonApiResponse",
"Major",
"Meal",
"MealResponse",
"MealType",
"ParticipantInfo",
"Project",
"ProjectResponse",
"ProjectSortBy",
"Schedule",
"ScheduleResponse",
"Sex",
"SortDirection",
"Student",
"StudentResponse",
"StudentRole",
"StudentSortBy",
"Timetable",
"TimetableResponse",
]
74 changes: 73 additions & 1 deletion src/datagsm_openapi/models/neis.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""NEIS-related models (급식, 학사일정) for DataGSM OpenAPI SDK."""
"""NEIS-related models (급식, 학사일정, 시간표) for DataGSM OpenAPI SDK."""

from datetime import date
from typing import Optional
Expand Down Expand Up @@ -56,6 +56,18 @@ class Meal(BaseModel):
model_config = ConfigDict(populate_by_name=True)


class MealResponse(BaseModel):
"""급식 목록 응답 래퍼 (Meal List Response Wrapper).

Attributes:
meals: List of meal information
"""

meals: list[Meal] = Field(default_factory=list, description="List of meals")

model_config = ConfigDict(populate_by_name=True)


class Schedule(BaseModel):
"""학사일정 정보 (School Schedule Information).

Expand Down Expand Up @@ -100,3 +112,63 @@ class Schedule(BaseModel):
)

model_config = ConfigDict(populate_by_name=True)


class ScheduleResponse(BaseModel):
"""학사일정 목록 응답 래퍼 (Schedule List Response Wrapper).

Attributes:
schedules: List of schedule information
"""

schedules: list[Schedule] = Field(default_factory=list, description="List of schedules")

model_config = ConfigDict(populate_by_name=True)


class Timetable(BaseModel):
"""시간표 정보 (Timetable Information).

Information about school timetables from NEIS.

Attributes:
timetable_id: Timetable ID
school_code: School code
school_name: School name
office_code: Education office code
office_name: Education office name
timetable_date: Timetable date
academic_year: Academic year
semester: Semester (nullable)
grade: Grade (1-3)
class_num: Class number (1-4)
period: Period number
subject: Subject name (nullable)
"""

timetable_id: str = Field(..., alias="timetableId", description="Timetable ID")
school_code: str = Field(..., alias="schoolCode", description="School code")
school_name: str = Field(..., alias="schoolName", description="School name")
office_code: str = Field(..., alias="officeCode", description="Education office code")
office_name: str = Field(..., alias="officeName", description="Education office name")
timetable_date: date = Field(..., alias="timetableDate", description="Timetable date")
academic_year: str = Field(..., alias="academicYear", description="Academic year")
semester: Optional[str] = Field(None, alias="semester", description="Semester")
grade: int = Field(..., alias="grade", description="Grade (1-3)")
class_num: int = Field(..., alias="classNum", description="Class number (1-4)")
period: int = Field(..., alias="period", description="Period number")
subject: Optional[str] = Field(None, alias="subject", description="Subject name")

model_config = ConfigDict(populate_by_name=True)


class TimetableResponse(BaseModel):
"""시간표 목록 응답 래퍼 (Timetable List Response Wrapper).

Attributes:
timetables: List of timetable information
"""

timetables: list[Timetable] = Field(default_factory=list, description="List of timetables")

model_config = ConfigDict(populate_by_name=True)
Loading