Skip to content

Models

All returned data is wrapped in typed, frozen dataclasses generated with marshmallow-dataclass. Each model has a .Schema attribute for serialisation/deserialisation.

Base

tju.models.base.Result

Base class for a single typed result item. Each subclass carries a marshmallow Schema class attribute generated by marshmallow-dataclass.

Source code in src/tju/models/base.py
class Result:
    """Base class for a single typed result item.  Each subclass carries a
    marshmallow ``Schema`` class attribute generated by ``marshmallow-dataclass``."""

    Schema: ClassVar[Type["Schema"]] = Schema

tju.models.base.Results

Bases: List[T_Result]

Typed list of :class:Result items. Subclasses set _item to the concrete dataclass and call :meth:load to deserialise a list of dicts.

Source code in src/tju/models/base.py
class Results(List[T_Result]):
    """Typed list of :class:`Result` items.  Subclasses set ``_item`` to the
    concrete dataclass and call :meth:`load` to deserialise a list of dicts."""

    _item: Type[T_Result]

    def __init__(self):
        super().__init__()

    def load(self, data: list):
        """Deserialise *data* (list of dicts) into typed items and extend this list."""
        schema = self._item.Schema(many=True)
        results = schema.load(data)
        if results is None:
            raise DataError("Failed to load data")
        self.extend(results)

load

load(data: list)

Deserialise data (list of dicts) into typed items and extend this list.

Source code in src/tju/models/base.py
def load(self, data: list):
    """Deserialise *data* (list of dicts) into typed items and extend this list."""
    schema = self._item.Schema(many=True)
    results = schema.load(data)
    if results is None:
        raise DataError("Failed to load data")
    self.extend(results)

Common

tju.models.common.StuType

Bases: Enum

Student Type

Source code in src/tju/models/common.py
class StuType(Enum):
    """Student Type"""

    UNDERGRADUATE = "本科生"
    GRADUATE = "研究生"

    def __str__(self):
        return self.value

    def __repr__(self):
        return self.value

tju.models.common.Gender

Bases: Enum

Gender

Source code in src/tju/models/common.py
class Gender(Enum):
    """Gender"""

    MALE = "男"
    FEMALE = "女"

    def __str__(self):
        return self.value

    def __repr__(self):
        return self.value

Schedule

tju.models.schedule.Course

Bases: Result

A course entry on a personal timetable. arrange holds the list of :class:CourseArrange slots; weeks is the raw week-range string.

Source code in src/tju/models/schedule.py
@dataclass(frozen=True)
class Course(Result):
    """A course entry on a personal timetable.  ``arrange`` holds the list of
    :class:`CourseArrange` slots; ``weeks`` is the raw week-range string."""

    class_id: Optional[str] = mfield(default=None)
    course_id: Optional[str] = mfield(default=None)
    name: Optional[str] = mfield(default=None)
    credit: Optional[float] = mfield(default=None)
    campus: Optional[str] = mfield(default=None)
    weeks: Optional[str] = mfield(default=None)
    arrange: Optional[list[CourseArrange]] = mfield(default=None)
    teacher: Optional[list] = mfield(default=None)

    class Meta:
        unknown = EXCLUDE

tju.models.schedule.CourseArrange

Bases: Result

A single scheduled slot (day / period / location) for a course.

Source code in src/tju/models/schedule.py
@dataclass(frozen=True)
class CourseArrange(Result):
    """A single scheduled slot (day / period / location) for a course."""

    teacher: Optional[list] = mfield(default=None)
    week: Optional[list] = mfield(default=None)
    unit: Optional[list] = mfield(default=None)
    weekday: Optional[int] = mfield(default=None)
    location: Optional[str] = mfield(default=None)

tju.models.schedule.Schedule

Bases: Results[Course]

List of :class:Course items representing a personal timetable.

Source code in src/tju/models/schedule.py
class Schedule(Results[Course]):
    """List of :class:`Course` items representing a personal timetable."""

    _item = Course

Course library

tju.models.course.LibCourse

Bases: Course

A course entry from the public course library. Extends :class:~tju.models.schedule.Course with library-specific fields such as lession_id, course_type, enrollment limits, and has_syllabus. The selected/limit fields are populated for UG courses (16-column format) but are None for GS courses (12-column format).

Source code in src/tju/models/course.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class LibCourse(Course):
    """A course entry from the public course library.  Extends :class:`~tju.models.schedule.Course`
    with library-specific fields such as ``lession_id``, ``course_type``, enrollment limits,
    and ``has_syllabus``.  The ``selected``/``limit`` fields are populated for UG courses
    (16-column format) but are ``None`` for GS courses (12-column format)."""
    # key in Course
    class_id: Optional[str] = mfield(default=None, data_key="课程序号")
    course_id: Optional[str] = mfield(default=None, data_key="课程代码")
    name: Optional[str] = mfield(default=None, data_key="课程名称")
    credit: Optional[float] = mfield(default=None, data_key="学分")
    campus: Optional[str] = mfield(default=None, data_key="开课校区")
    weeks: Optional[str] = mfield(default=None, data_key="起止周")
    arrange: Optional[list[CourseArrange]] = mfield(default=None)
    teacher: Optional[list] = mfield(default=None, data_key="教师")
    # key in LibCourse
    semester: Optional[str] = mfield(default=None)
    lession_id: Optional[str] = mfield(default=None)
    course_type: Optional[list] = mfield(default=None, data_key="课程类别")
    teaching_class: Optional[list] = mfield(default=None, data_key="教学班")
    selected: Optional[int] = mfield(default=None, data_key="实际")
    limit: Optional[int] = mfield(default=None, data_key="总上限")
    extra_limit: Optional[int] = mfield(default=None, data_key="计划外人数上限")
    is_extra_open: Annotated[Optional[bool], ChineseBool] = mfield(default=None, data_key="是否开放计划外")
    hours: Optional[float] = mfield(default=None, data_key="总学时")
    week_hours: Optional[float] = mfield(default=None, data_key="周学时")
    has_syllabus: Annotated[Optional[bool], ChineseHasBool] = mfield(default=None, data_key="课程大纲")

    class Meta:
        unknown = EXCLUDE

tju.models.course.CourseLib

Bases: Results[LibCourse]

List of :class:LibCourse items from the public course library.

Source code in src/tju/models/course.py
class CourseLib(Results[LibCourse]):
    """List of :class:`LibCourse` items from the public course library."""

    _item = LibCourse

Exam

tju.models.exam.Exam

Bases: Result

A single exam entry. exam_time is a list of two datetime.time objects (start, end); exam_date is a datetime.date.

Source code in src/tju/models/exam.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class Exam(Result):
    """A single exam entry.  ``exam_time`` is a list of two ``datetime.time`` objects
    (start, end); ``exam_date`` is a ``datetime.date``."""

    class_id: Optional[str] = mfield(default=None, data_key="课程序号")
    name: Optional[str] = mfield(default=None, data_key="课程名称")
    exam_type: Optional[str] = mfield(default=None, data_key="考试类别")
    exam_date: Annotated[Optional[date], DatetimeField] = mfield(default=None, data_key="考试日期")
    exam_category: Optional[str] = mfield(default=None, data_key="批次")
    exam_time: Annotated[Optional[list], ExamTimeField] = mfield(default=None, data_key="考试安排")
    location: Optional[str] = mfield(default=None, data_key="考试地点")
    seat: Optional[str] = mfield(default=None, data_key="考场座位号")
    status: Optional[str] = mfield(default=None, data_key="考试情况")
    notice: Optional[str] = mfield(default=None, data_key="其它说明")

    class Meta:
        unknown = EXCLUDE

tju.models.exam.Exams

Bases: Results[Exam]

List of :class:Exam items from the exam schedule page.

Source code in src/tju/models/exam.py
class Exams(Results[Exam]):
    """List of :class:`Exam` items from the exam schedule page."""

    _item = Exam

Scores

tju.models.score.UGScore

Bases: Score

An undergraduate course score record.

Source code in src/tju/models/score.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class UGScore(Score):
    """An undergraduate course score record."""

    course_props: Optional[str] = mfield(default=None, data_key="课程性质")
    score: Optional[str] = mfield(default=None, data_key="总评成绩")
    gpa: Annotated[Optional[float], GPAField] = mfield(default=None, data_key="绩点")

    class Meta:
        unknown = EXCLUDE

tju.models.score.GSScore

Bases: Score

A graduate course score record.

Source code in src/tju/models/score.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class GSScore(Score):
    """A graduate course score record."""

    class_id: Optional[str] = mfield(default=None, data_key="课程序号")
    exam_status: Optional[str] = mfield(default=None, data_key="考试情况")
    score: Optional[str] = mfield(default=None, data_key="最终")
    is_in_plan: Annotated[Optional[str], ChineseBool] = mfield(default=None, data_key="是否方案内课程")
    is_credited: Annotated[Optional[str], ChineseBool] = mfield(default=None, data_key="选修课是否认定学分")

    class Meta:
        unknown = EXCLUDE

tju.models.score.ExpScore

Bases: Result

An experiment (lab) course score record.

Source code in src/tju/models/score.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class ExpScore(Result):
    """An experiment (lab) course score record."""

    semester: Annotated[Optional[str], ExpScoreSemesterField] = mfield(default=None, data_key="学年学期")
    course_id: Optional[str] = mfield(default=None, data_key="课程代码")
    class_id: Optional[str] = mfield(default=None, data_key="课程序号")
    course_name: Optional[str] = mfield(default=None, data_key="课程名称")
    project_id: Optional[str] = mfield(default=None, data_key="项目代码")
    project_name: Optional[str] = mfield(default=None, data_key="项目名称")
    sub_score: Optional[str] = mfield(default=None, data_key="分项成绩")
    score: Optional[float] = mfield(default=None, data_key="项目成绩")

    class Meta:
        unknown = EXCLUDE

tju.models.score.UGScoreSummary

Bases: ScoreSummary

Undergraduate score summary, includes GPA and weighted average.

Source code in src/tju/models/score.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class UGScoreSummary(ScoreSummary):
    """Undergraduate score summary, includes GPA and weighted average."""

    gpa: Optional[float] = mfield(default=None, data_key="平均绩点")
    score: Optional[float] = mfield(default=None, data_key="加权平均成绩")

tju.models.score.GSScoreSummary

Bases: ScoreSummary

Graduate score summary, broken down by school year and term.

Source code in src/tju/models/score.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class GSScoreSummary(ScoreSummary):
    """Graduate score summary, broken down by school year and term."""

    school_year: Optional[str] = mfield(default=None, data_key="学年度")
    term: Optional[int] = mfield(default=None, data_key="学期")

Profile

tju.models.profile.Profile

Bases: Result

Complete student record scraped from the EAMS profile page. Contains identity, enrolment, contact and supervisory information.

Source code in src/tju/models/profile.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class Profile(Result):
    """Complete student record scraped from the EAMS profile page.  Contains
    identity, enrolment, contact and supervisory information."""

    stu_id: Optional[str] = mfield(default=None, data_key="学号")
    stu_name: Optional[str] = mfield(default=None, data_key="姓名")
    stu_name_en: Optional[str] = mfield(default=None, data_key="英文名")
    gender: Annotated[Optional[Gender], ChineseGender] = mfield(default=None, data_key="性别")
    grade: Optional[str] = mfield(default=None, data_key="所在年级")
    edu_system: Optional[str] = mfield(default=None, data_key="学制")
    project: Optional[str] = mfield(default=None, data_key="项目")
    edu_level: Optional[str] = mfield(default=None, data_key="学历层次")
    stu_type: Annotated[Optional[StuType], ChineseStuType] = mfield(default=None, data_key="学生类别")
    faculty: Optional[str] = mfield(default=None, data_key="院系")
    major: Optional[str] = mfield(default=None, data_key="专业")
    direction: Optional[str] = mfield(default=None, data_key="方向")
    enrollment_date: Annotated[Optional[date], DatetimeField] = mfield(default=None, data_key="入学日期")
    graduation_date: Annotated[Optional[date], DatetimeField] = mfield(default=None, data_key="预毕业时间")
    admin_faculty: Optional[str] = mfield(default=None, data_key="行政管理院系")
    study_form: Optional[str] = mfield(default=None, data_key="学习形式")
    is_enrolled: Annotated[Optional[bool], ChineseBool] = mfield(default=None, data_key="是否在籍")
    is_on_campus: Annotated[Optional[bool], ChineseBool] = mfield(default=None, data_key="是否在校")
    campus: Optional[str] = mfield(default=None, data_key="所属校区")
    admin_class: Optional[str] = mfield(default=None, data_key="行政班")
    training_type: Optional[str] = mfield(default=None, data_key="培养类型")
    stu_tag: Optional[str] = mfield(default=None, data_key="学生标签")
    is_eth_class: Annotated[Optional[bool], ChineseBool] = mfield(default=None, data_key="是否民族班")
    effective_date: Annotated[Optional[date], DatetimeField] = mfield(default=None, data_key="学籍生效日期")
    has_enrolled: Annotated[Optional[bool], ChineseBool] = mfield(default=None, data_key="是否有学籍")
    training_plan: Optional[str] = mfield(default=None, data_key="培养方案")
    stu_category: Optional[str] = mfield(default=None, data_key="学生分类")
    cooperative_unit: Optional[str] = mfield(default=None, data_key="联合办学单位")
    original_class: Optional[str] = mfield(default=None, data_key="原班级")
    teaching_class: Optional[str] = mfield(default=None, data_key="教学班")
    remark: Optional[str] = mfield(default=None, data_key="备注")
    supervisor: Optional[str] = mfield(default=None, data_key="导师")
    co_supervisor: Optional[str] = mfield(default=None, data_key="合作导师")
    total_tuition: Optional[str] = mfield(default=None, data_key="总学费")
    scholarship_supervisor: Optional[str] = mfield(default=None, data_key="助学金导师")
    degree_category: Optional[str] = mfield(default=None, data_key="学位类别")
    stu_status: Optional[str] = mfield(default=None, data_key="学籍状态")
    cooperative_unit: Optional[str] = mfield(default=None, data_key="合作单位")
    orientation_unit: Optional[str] = mfield(default=None, data_key="定向单位")
    stu_on_campus_category: Optional[str] = mfield(default=None, data_key="在校生类别")
    is_off_campus: Annotated[Optional[bool], ChineseBool] = mfield(default=None, data_key="是否校外生")
    training_mode: Optional[str] = mfield(default=None, data_key="培养模式")
    email: Optional[str] = mfield(default=None, data_key="电子邮件")
    phone: Optional[str] = mfield(default=None, data_key="联系电话")
    mobile: Optional[str] = mfield(default=None, data_key="移动电话")
    address: Optional[str] = mfield(default=None, data_key="联系地址")
    home_phone: Optional[str] = mfield(default=None, data_key="家庭电话")
    home_address: Optional[str] = mfield(default=None, data_key="家庭地址")
    home_address_zip: Optional[str] = mfield(default=None, data_key="家庭地址邮编")

    class Meta:
        unknown = EXCLUDE

Free classroom

tju.models.classroom.FreeClassroom

Bases: Result

A single free-classroom entry returned by the EAMS free-classroom search.

Source code in src/tju/models/classroom.py
@dataclass(frozen=True, base_schema=LoadDumpSchema)
class FreeClassroom(Result):
    """A single free-classroom entry returned by the EAMS free-classroom search."""
    campus:     Optional[str] = mfield(default=None, data_key="校区")
    building:   Optional[str] = mfield(default=None, data_key="教学楼")
    name:       Optional[str] = mfield(default=None, data_key="教室")
    room_type:  Optional[str] = mfield(default=None, data_key="教室类型")
    seats:      Optional[str] = mfield(default=None, data_key="座位数")

    class Meta:
        unknown = EXCLUDE

tju.models.classroom.FreeClassrooms

Bases: Results[FreeClassroom]

List of available :class:FreeClassroom entries.

Source code in src/tju/models/classroom.py
class FreeClassrooms(Results[FreeClassroom]):
    """List of available :class:`FreeClassroom` entries."""

    _item = FreeClassroom