Skip to content

Releases: TFoxik/pyedookit

v0.7.0

Choose a tag to compare

@TFoxik TFoxik released this 24 Oct 13:18

Changes

  • get_grades() returns the current term's Grades: grades newest first (mark, weight, category, topic, teacher, entered date, subject and code) and subjects with teacher and attendance. Grades.average(course_id) weights each grade by its weight; marks other than 1–5 are kept as text and left out. Grade.short / Subject.short give the subject abbreviation ("Aj").
  • The current term is chosen by its heading ("2. pololetí" over "1. pololetí"). A grades page whose rows can't be read raises ParseError instead of returning no grades.
  • CAPTURE_PAGES gains evaluation_overall, to catch the term report's markup once it is published.
  • scripts/capture_fixtures.py --evaluation saves the grades pages for building parsers.

v0.6.0

Choose a tag to compare

@TFoxik TFoxik released this 23 Oct 18:40

Changes

  • get_inbox() lists the messages in Schránka: subject, sender, sent time, preview and whether each is unread.
  • get_message(id) returns one message in full; fetching it marks the message read on the portal. The body comes as a sanitised node tree (paragraphs, line breaks, bold, italics, underline, lists and http(s) links); scripts, styles, media and event handlers are dropped.
  • The inbox and message pages are fetched without the AJAX headers: to an AJAX request the portal answers with the inbox's first-run guide and its sample items instead of the real messages.
  • A page answered with the sign-in page counts as an expired session, so the session is renewed.

Breaking changes

  • "inbox" is no longer in CAPTURE_PAGES, so fetch_page("inbox") raises ValueError. Use get_inbox().

v0.5.1

Choose a tag to compare

@TFoxik TFoxik released this 16 Oct 23:21

Changes

  • refresh() raises AuthError only when Plus4U asks for a new login: an OIDC login_required, interaction_required, consent_required or account_selection_required answer, or a redirect to the Plus4U sign-in page. Any other renewal failure (Plus4U or portal errors, 429/5xx, a maintenance page, a rejected callback or a session that doesn't settle) raises EdookitConnectionError, so callers retry instead of asking for the password.
  • Renewal errors name the HTTP status, host, path and OIDC error, never the code or state. A needed login is logged at warning level.
  • README: the Plus4U sign-in cookie is set for 30 days from login(), not about 19.

No breaking changes.

v0.5.0

Choose a tag to compare

@TFoxik TFoxik released this 16 Oct 16:13

Changes

  • Lesson.code holds the subject abbreviation the portal shows (Čj, M, ČaS…).
  • get_timetable_week(week_offset=0) returns a TimetableWeek with the week's lessons and its bell schedule (Period(number, start, end)), from one page load.
  • A week whose page has a bell schedule but no lessons (holidays) comes back from get_timetable_week as an empty week instead of raising ParseError. get_timetable is unchanged.

No breaking changes.

v0.4.0

Choose a tag to compare

@TFoxik TFoxik released this 10 Oct 18:39
v0.4.0
289e3fe

Breaking changes

  • Session is now Session(school, cookies) and holds a CookieStore (session.cookies) instead of the edookit_cookies, plus4u_cookies and csrf_token fields. Build one with Session.from_cookies(school, edookit=…, plus4u=…). edookit_expires_at and is_edookit_expired are gone; csrf_token is now a read-only property taken from the uu.app.csrf cookie.
  • EdookitClient renews an expired session by itself and retries once. Pass on_session_update to persist each renewed session. SessionExpired now only surfaces when the session is still expired right after a renewal; AuthError means the Plus4U sign-in expired and login() is needed.
  • EdookitClient.session returns a new Session object after each renewal, so don't hold on to it.
  • Any redirect from the portal on a page request (HTTP or the portal's JSON redirect, including the /oidc/logout one) now counts as an expired session.
  • Removed from pyedookit.auth: build_headers, http_session, REQUEST_TIMEOUT, REDIRECT_STATUSES and CookiesByHost. HTTP now lives in pyedookit.transport.

Sessions stored by 0.3.x still load with Session.from_json(). Sessions written by 0.4.0 use a new JSON format that 0.3.x can't read, so downgrading after a renewal means signing in again.

Improvements

  • One transport for every request: cookies rotated by the portal are kept, and cookies the server deletes (Max-Age=0 or a past Expires) stop being sent.
  • session.plus4u_expires_at shows when the Plus4U sign-in runs out, or None when that's unknown. Plus4U doesn't send an expiry on a silent refresh, so it stays None for sessions carried over from 0.3.x until the next login().
  • Sessions are renewed when the portal reports them expired, instead of on a fixed 30-minute timer. Against a real portal, an idle Edookit session lasted under 30 minutes: polling every 30 minutes gave 4 renewals in 2 hours, all successful.

v0.3.2

Choose a tag to compare

@TFoxik TFoxik released this 10 Oct 13:41
v0.3.2
871ccbd

Fixes

  • Expired sessions are detected correctly. For an expired session the portal answers with HTTP 200 and a JSON {"redirect": ".../user/login"} instead of an HTTP redirect. pyedookit used to report that as ParseError; it now raises SessionExpired, so callers refresh and retry.

Security

  • Session validates the school subdomain when it's created, including in Session.from_json(). A tampered stored session can no longer send cookies to another host.
  • Every hop of the login and refresh flow must be https on plus4u.net or edookit.net.
  • repr(Session) no longer includes cookies or the CSRF token.
  • Minimum versions raised to aiohttp>=3.14.3 and lxml>=6.1.0, which include their security fixes. yarl is now a declared dependency.

Changes

  • Pages are parsed in a worker thread, so a large timetable doesn't block the event loop.
  • EdookitClient without an http session now uses the same cookieless session as login().
  • Removed the unused Student model.
  • Ships py.typed. CI tests Python 3.12 and 3.14.