Metadata-Version: 2.5
Name: ofscli
Version: 0.2.1
Summary: OFS school portal CLI - raw JSON access to calendar, assessment, class resources, attendance, classes, timetable, MYP report, awards, homework
Project-URL: Homepage, https://pypi.org/project/ofscli/
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: httpx>=0.28.1
Requires-Dist: rich>=13.0.0
Requires-Dist: typer>=0.19.2
Description-Content-Type: text/markdown

# ofscli — OFS school portal CLI

Raw JSON access to the OFS "My OFS" portal resources: **calendar, assessment,
class resources, attendance, classes, timetable, MYP report, awards, homework**.

Responses are saved **verbatim** (no formatting / further processing) under
`data/<resource>/<timestamp>/` and echoed to stdout.  JSON is preferred;
if a resource only exposes HTML/PDF, the raw bytes are saved as-is.

> ⚠️ **Be a good neighbor.** This tool talks to a real school portal. It is
> rate-limited, caches responses, and fetches on demand by default — please
> keep it that way and do not bulk-download the portal.

## Install

```bash
pip install ofscli        # from PyPI
# or from source:
# cd ofscli && uv sync
```

## Setup

```bash
cp .env.example .env   # fill in OFS_USERNAME / OFS_PASSWORD / OFS_STUDENT_ID / OFS_BASE_URL
```

`OFS_BASE_URL` is **required**: point it at your school's "My OFS" JSON-RPC
endpoint, e.g. `https://<portal-host>/my-ofs/servlet/JSON-RPC`. The tool
deliberately does not hard-code any school domain.

## Usage

```bash
# one-time login (persists session to .ofscli/session.json)
ofscli login

# resources (each prints + saves raw JSON)
ofscli overview                 # user/student detail + classInfo (classes)
ofscli classes                  # class list (from overview payload)
ofscli grades                   # student grade history
ofscli homework list            # homework list
ofscli homework detail --id 123 # one homework detail
ofscli homework download        # details for homework due within 14 days
ofscli homework download --days 30   # wider window
ofscli homework download --all      # every assignment (not recommended)
ofscli calendar                 # school calendar, current month
ofscli calendar --student       # student calendar
ofscli calendar -m 6 -y 2026    # specific month/year
ofscli assessment               # list semesters
ofscli assessment --semester-id 5   # assessment data for a semester
ofscli resources                # all class resources
ofscli resources --class-id 42 # one class's resources
ofscli attendance               # attendance record
ofscli timetable                # timetable details
ofscli myp                      # MYP report result
ofscli awards                   # awards list

# exploration
ofscli discover                 # all discovered JSON-RPC methods
ofscli raw <method> '[params...]'   # call any method with raw params
```

## Being a good neighbor

The portal is a real school system — treat it gently. `ofscli` ships with
three layers of protection (all configurable):

| Feature | Default | Env var | CLI flag |
|---|---|---|---|
| **Rate limit** — min seconds between any two HTTP requests, with ±50% jitter so bursts don't stay in lockstep | 1.5 s | `OFS_MIN_INTERVAL` | `--min-interval` |
| **Prime caching** — the referrer page is GET at most once per 10 min instead of before every RPC call (halves request count) | 600 s | `OFS_PRIME_TTL` | — |
| **Response cache** — read-only RPC results cached on disk under `data/cache/`, keyed by method+params | 900 s | `OFS_CACHE_TTL` | `--cache-ttl` / `--no-cache` |

`homework download` is **on-demand by default**: it only fetches details for
assignments due within `--days` (default 14). Use `--limit N` to cap the
number of detail fetches, and `--all` only if you really need everything.

```bash
# examples
ofscli homework download --days 7 --limit 5   # small, polite
OFS_MIN_INTERVAL=3 ofscli homework list       # slower, gentler
ofscli homework list --no-cache               # force a fresh fetch
```

## Discovered JSON-RPC methods

| Resource | Method | Params |
|---|---|---|
| overview | `studentOverviewDelegate.getStudentOverview` | `[studentId]` |
| grades | `studentOverviewDelegate.getStudentGrades` | `[userId]` |
| homework list | `homeworkDelegate.getHomeworkListForParent` | `[userId, classId, sort, page, []]` |
| homework detail | `homeworkDelegate.getHomeworkForParent` | `[homeworkId, 2, studentId, false]` |
| school calendar | `academicCalendarDelegate.getCalendarEvents` | `[month, year]` |
| student calendar | `academicCalendarDelegate.getCalendarEventsForStudent` | `[studentId, month, year]` |
| assessment semesters | `assessmentDelegate.getStudentSemesterData` | `[studentId]` |
| assessment | `assessmentDelegate.getStudentAssessmentDataBySemesterId` | `[studentId, semesterId]` |
| assessment detail | `assessmentDelegate.getStudentAssessmentDataByDate` | `[assessmentId, createdDate]` |
| class resources | `classResourceDelegate.getAllStudentClassResources` | `[userId]` |
| class resources (one) | `classResourceDelegate.getClassResources` | `[userId, classId]` |
| attendance | `attendanceDelegate.getStudentAttendance` | `[userId]` |
| timetable | `userDelegate.getStudentTimetableDetails` | `[userId]` |
| MYP report | `academicReportDelegate.getMYPResult` | `[studentId]` |
| awards | `awardsDelegate.getStudentAwardsForOverview` | `[userId]` |

Methods were discovered by inspecting the portal's own JavaScript
(`student-overview.min.js`, `school-calendar-plugin.min.js`,
`assessment-student.min.js`, `myp-report-tab.min.js`,
`student-awards-tab.min.js`, `dashboard.min.js`).

## Notes

- `userId` usually equals `studentId` for a single-child account; the CLI
  falls back to `studentId` when `--user-id` is not given.
- Session cookies + `clientDeviceId` are persisted after `login`; commands
  reuse the session until it expires (then re-run `ofscli login --force`).
- `ofscli raw` is the exploration escape hatch for any method, including the
  extra ones listed by `ofscli discover` (mail, library, reports, etc.).
- `calendar --student` (`getCalendarEventsForStudent`) is permission-gated:
  the portal returns code 490 "You do not have permission to view the
  calendar of this student" for a student account.  The school calendar
  (`calendar` without `--student`) works for everyone.
- `dashboardDelegate.getAsyncData` returns `{}` for this account; the
  dashboard methods are listed under `discover` for staff/parent accounts
  that have dashboard widgets.

## Security

- `.env` (credentials) and `.ofscli/` (session cookies) are git-ignored —
  never commit them. Copy `.env.example` and fill in your own values.
- The session file contains live portal cookies; treat it like a password.
- This is an unofficial tool, not affiliated with Overseas Family School
  (OFS). Personal use only. Use at your own risk and respect the portal's
  terms of service.
