calendry-client 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- calendry_client-0.1.0/.github/workflows/ci.yml +50 -0
- calendry_client-0.1.0/.github/workflows/publish.yml +50 -0
- calendry_client-0.1.0/.gitignore +222 -0
- calendry_client-0.1.0/PKG-INFO +197 -0
- calendry_client-0.1.0/README.md +183 -0
- calendry_client-0.1.0/pyproject.toml +37 -0
- calendry_client-0.1.0/scripts/import_xlsx.py +419 -0
- calendry_client-0.1.0/src/calendry_client/__init__.py +180 -0
- calendry_client-0.1.0/src/calendry_client/_generic.py +64 -0
- calendry_client-0.1.0/src/calendry_client/_version.py +24 -0
- calendry_client-0.1.0/src/calendry_client/access_roles.py +56 -0
- calendry_client-0.1.0/src/calendry_client/calendar_periods.py +48 -0
- calendry_client-0.1.0/src/calendry_client/client.py +113 -0
- calendry_client-0.1.0/src/calendry_client/constraints.py +80 -0
- calendry_client-0.1.0/src/calendry_client/equipment.py +32 -0
- calendry_client-0.1.0/src/calendry_client/exceptions.py +26 -0
- calendry_client-0.1.0/src/calendry_client/groups.py +104 -0
- calendry_client-0.1.0/src/calendry_client/offerings.py +139 -0
- calendry_client-0.1.0/src/calendry_client/persons.py +100 -0
- calendry_client-0.1.0/src/calendry_client/roles.py +33 -0
- calendry_client-0.1.0/src/calendry_client/rooms.py +77 -0
- calendry_client-0.1.0/src/calendry_client/session_kinds.py +49 -0
- calendry_client-0.1.0/src/calendry_client/terms.py +49 -0
- calendry_client-0.1.0/src/calendry_client/time_grids.py +95 -0
- calendry_client-0.1.0/swagger.json +3910 -0
- calendry_client-0.1.0/tests/test_client.py +52 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.9", "3.11", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
|
|
23
|
+
- name: Install package with dev dependencies
|
|
24
|
+
run: pip install -e ".[xlsx]" pytest
|
|
25
|
+
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: pytest -q
|
|
28
|
+
|
|
29
|
+
build:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
needs: test
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 0
|
|
36
|
+
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.11"
|
|
40
|
+
|
|
41
|
+
- name: Install build tool
|
|
42
|
+
run: pip install build
|
|
43
|
+
|
|
44
|
+
- name: Build sdist and wheel
|
|
45
|
+
run: python -m build
|
|
46
|
+
|
|
47
|
+
- uses: actions/upload-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Publish package
|
|
2
|
+
|
|
3
|
+
# Builds the pure-function client library into a python package and publishes
|
|
4
|
+
# it to PyPI whenever a version tag (vX.Y.Z) is pushed.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags:
|
|
8
|
+
- "v*.*.*"
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.11"
|
|
22
|
+
|
|
23
|
+
- name: Install build tool
|
|
24
|
+
run: pip install build
|
|
25
|
+
|
|
26
|
+
- name: Build sdist and wheel
|
|
27
|
+
run: python -m build
|
|
28
|
+
|
|
29
|
+
- uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
needs: build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
environment: pypi
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # required for PyPI trusted publishing
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI
|
|
47
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
48
|
+
# Uses PyPI's Trusted Publisher (OIDC) setup for this repo/environment.
|
|
49
|
+
# If trusted publishing isn't configured, add a PYPI_API_TOKEN secret
|
|
50
|
+
# and pass `with: { password: ${{ secrets.PYPI_API_TOKEN }} }` instead.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# hatch-vcs generated version file
|
|
2
|
+
src/calendry_client/_version.py
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[codz]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py.cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
# Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# UV
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
# uv.lock
|
|
105
|
+
|
|
106
|
+
# poetry
|
|
107
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
108
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
109
|
+
# commonly ignored for libraries.
|
|
110
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
111
|
+
# poetry.lock
|
|
112
|
+
# poetry.toml
|
|
113
|
+
|
|
114
|
+
# pdm
|
|
115
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
116
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
117
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
118
|
+
# pdm.lock
|
|
119
|
+
# pdm.toml
|
|
120
|
+
.pdm-python
|
|
121
|
+
.pdm-build/
|
|
122
|
+
|
|
123
|
+
# pixi
|
|
124
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
125
|
+
# pixi.lock
|
|
126
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
127
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
128
|
+
.pixi
|
|
129
|
+
|
|
130
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
131
|
+
__pypackages__/
|
|
132
|
+
|
|
133
|
+
# Celery stuff
|
|
134
|
+
celerybeat-schedule
|
|
135
|
+
celerybeat.pid
|
|
136
|
+
|
|
137
|
+
# Redis
|
|
138
|
+
*.rdb
|
|
139
|
+
*.aof
|
|
140
|
+
*.pid
|
|
141
|
+
|
|
142
|
+
# RabbitMQ
|
|
143
|
+
mnesia/
|
|
144
|
+
rabbitmq/
|
|
145
|
+
rabbitmq-data/
|
|
146
|
+
|
|
147
|
+
# ActiveMQ
|
|
148
|
+
activemq-data/
|
|
149
|
+
|
|
150
|
+
# SageMath parsed files
|
|
151
|
+
*.sage.py
|
|
152
|
+
|
|
153
|
+
# Environments
|
|
154
|
+
.env
|
|
155
|
+
.envrc
|
|
156
|
+
.venv
|
|
157
|
+
env/
|
|
158
|
+
venv/
|
|
159
|
+
ENV/
|
|
160
|
+
env.bak/
|
|
161
|
+
venv.bak/
|
|
162
|
+
|
|
163
|
+
# Spyder project settings
|
|
164
|
+
.spyderproject
|
|
165
|
+
.spyproject
|
|
166
|
+
|
|
167
|
+
# Rope project settings
|
|
168
|
+
.ropeproject
|
|
169
|
+
|
|
170
|
+
# mkdocs documentation
|
|
171
|
+
/site
|
|
172
|
+
|
|
173
|
+
# mypy
|
|
174
|
+
.mypy_cache/
|
|
175
|
+
.dmypy.json
|
|
176
|
+
dmypy.json
|
|
177
|
+
|
|
178
|
+
# Pyre type checker
|
|
179
|
+
.pyre/
|
|
180
|
+
|
|
181
|
+
# pytype static type analyzer
|
|
182
|
+
.pytype/
|
|
183
|
+
|
|
184
|
+
# Cython debug symbols
|
|
185
|
+
cython_debug/
|
|
186
|
+
|
|
187
|
+
# PyCharm
|
|
188
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
189
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
190
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
191
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
192
|
+
# .idea/
|
|
193
|
+
|
|
194
|
+
# Abstra
|
|
195
|
+
# Abstra is an AI-powered process automation framework.
|
|
196
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
197
|
+
# Learn more at https://abstra.io/docs
|
|
198
|
+
.abstra/
|
|
199
|
+
|
|
200
|
+
# Visual Studio Code
|
|
201
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
202
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
203
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
204
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
205
|
+
# .vscode/
|
|
206
|
+
# Temporary file for partial code execution
|
|
207
|
+
tempCodeRunnerFile.py
|
|
208
|
+
|
|
209
|
+
# Ruff stuff:
|
|
210
|
+
.ruff_cache/
|
|
211
|
+
|
|
212
|
+
# PyPI configuration file
|
|
213
|
+
.pypirc
|
|
214
|
+
|
|
215
|
+
# Marimo
|
|
216
|
+
marimo/_static/
|
|
217
|
+
marimo/_lsp/
|
|
218
|
+
__marimo__/
|
|
219
|
+
|
|
220
|
+
# Streamlit
|
|
221
|
+
.streamlit/secrets.toml
|
|
222
|
+
*.xlsx
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: calendry-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client library for the Calendry scheduling API
|
|
5
|
+
Project-URL: Homepage, https://github.com/Calendry-de/Calendry-Importlib
|
|
6
|
+
Project-URL: Repository, https://github.com/Calendry-de/Calendry-Importlib
|
|
7
|
+
Author: Calendry
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Requires-Dist: requests>=2.28
|
|
11
|
+
Provides-Extra: xlsx
|
|
12
|
+
Requires-Dist: openpyxl>=3.1; extra == 'xlsx'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# calendry-client
|
|
16
|
+
|
|
17
|
+
[](https://github.com/Calendry-de/Calendry-Importlib/actions/workflows/ci.yml)
|
|
18
|
+
[](https://pypi.org/project/calendry-client/)
|
|
19
|
+
[](https://pypi.org/project/calendry-client/)
|
|
20
|
+
[](#license)
|
|
21
|
+
|
|
22
|
+
Python client library for the [Calendry](https://github.com/Calendry-de) scheduling API. It wraps every core resource (persons, groups, rooms, offerings, equipment, roles, terms, time grids, session kinds, calendar periods, constraints, access roles) as a small set of **pure functions** — `add_person`, `add_offering`, `set_offering_lecturers`, and so on — plus a ready-to-run script that imports a full Offerings/Rooms/People/Groups planning workbook.
|
|
23
|
+
|
|
24
|
+
The full HTTP surface is documented in [`swagger.json`](swagger.json); the library's functions build the request bodies described there. There is no code generation step — the swagger file is the reference the modules were written against, not something they're built from.
|
|
25
|
+
|
|
26
|
+
## Contents
|
|
27
|
+
|
|
28
|
+
- [Features](#features)
|
|
29
|
+
- [Installation](#installation)
|
|
30
|
+
- [Quick start](#quick-start)
|
|
31
|
+
- [Configuring the server URL and token](#configuring-the-server-url-and-token)
|
|
32
|
+
- [API coverage](#api-coverage)
|
|
33
|
+
- [Working with relations](#working-with-relations)
|
|
34
|
+
- [Error handling](#error-handling)
|
|
35
|
+
- [Importing an xlsx planning workbook](#importing-an-xlsx-planning-workbook)
|
|
36
|
+
- [Development](#development)
|
|
37
|
+
- [Publishing](#publishing)
|
|
38
|
+
- [License](#license)
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- One small `CalendryClient` HTTP wrapper — no ORM, no hidden state, no generated boilerplate.
|
|
43
|
+
- A plain function per operation, taking the client as its first argument and returning the parsed JSON response.
|
|
44
|
+
- Optional keyword arguments left as `None` are simply omitted from the request body (never sent as JSON `null`).
|
|
45
|
+
- Relation endpoints (`offerings/lecturers`, `groups/terms`, `rooms/equipment`, ...) get typed `get_*`/`set_*` helpers; `set_*` always **replaces** the whole membership set, mirroring the API's `PUT`.
|
|
46
|
+
- Server URL and auth token are runtime configuration everywhere (constructor args, `CalendryClient.from_env()`, or CLI flags on the import script) — never hard-coded.
|
|
47
|
+
- Includes a reference import script (`scripts/import_xlsx.py`) demonstrating the library against a real Offerings/Rooms/People/Groups workbook, with a `--dry-run` mode. It's a plain script, not part of the distributed `calendry-client` package — run it from a checkout of this repo.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install calendry-client
|
|
53
|
+
|
|
54
|
+
# with openpyxl, needed only for scripts/import_xlsx.py:
|
|
55
|
+
pip install "calendry-client[xlsx]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Requires Python 3.9+. The only runtime dependency is [`requests`](https://pypi.org/project/requests/).
|
|
59
|
+
|
|
60
|
+
## Quick start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from calendry_client import CalendryClient, add_person, add_offering, set_offering_lecturers
|
|
64
|
+
|
|
65
|
+
client = CalendryClient(base_url="https://calendry.example.com", token="...")
|
|
66
|
+
|
|
67
|
+
person = add_person(client, "Ada", "Lovelace", email="ada@example.com")
|
|
68
|
+
offering = add_offering(client, term_id="term-1", kind_id="kind-1", title="Intro to CS")
|
|
69
|
+
set_offering_lecturers(client, offering["id"], [{"person_id": person["id"]}])
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Every function returns the raw dict (or list of dicts) the API responds with — e.g. `person["id"]` is the newly created row's id.
|
|
73
|
+
|
|
74
|
+
## Configuring the server URL and token
|
|
75
|
+
|
|
76
|
+
Both values can be passed explicitly:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
client = CalendryClient(base_url="https://calendry.example.com", token="my-token")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
or read from the environment (`CALENDRY_SERVER_URL` / `CALENDRY_API_TOKEN` by default, both overridable):
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
client = CalendryClient.from_env()
|
|
86
|
+
client = CalendryClient.from_env(url_var="MY_URL_VAR", token_var="MY_TOKEN_VAR")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`token=None` sends unauthenticated requests, which is only useful against a server/route that doesn't require one. The token is sent as `Authorization: Bearer <token>`.
|
|
90
|
+
|
|
91
|
+
`scripts/import_xlsx.py` exposes the same configuration as `--server-url`/`--token` CLI flags, also falling back to the environment variables above — see [Importing an xlsx planning workbook](#importing-an-xlsx-planning-workbook).
|
|
92
|
+
|
|
93
|
+
## API coverage
|
|
94
|
+
|
|
95
|
+
Every resource module lives directly under `calendry_client` and follows the same shape: `add_x` (create), `get_x`, `list_xs`, `update_x`, `delete_x`, plus relation helpers where the API exposes a `/…/{id}/{relation}` route.
|
|
96
|
+
|
|
97
|
+
| Module (`calendry_client.*`) | Resource | Create | Relations |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `persons` | `persons` | `add_person` (alias: `add_lecturer`) | `roles`, `groups`, `access-roles` |
|
|
100
|
+
| `roles` | `roles` | `add_role` | — |
|
|
101
|
+
| `groups` | `groups` | `add_group`, `add_subgroup` (sets `parent_group_id`) | `terms`, `sources`, `availability` |
|
|
102
|
+
| `rooms` | `rooms` | `add_room` | `equipment` |
|
|
103
|
+
| `equipment` | `equipment` | `add_equipment` | — |
|
|
104
|
+
| `offerings` | `offerings` | `add_offering` | `groups`, `lecturers`, `equipment` |
|
|
105
|
+
| `terms` | `terms` | `add_term` | — |
|
|
106
|
+
| `time_grids` | `time-grids` | `add_time_grid` | `breaks` |
|
|
107
|
+
| `session_kinds` | `session-kinds` | `add_session_kind` | — |
|
|
108
|
+
| `calendar_periods` | `calendar-periods` | `add_calendar_period` | — |
|
|
109
|
+
| `constraints` | `constraints` | `add_constraint` | `scopes` |
|
|
110
|
+
| `access_roles` | `access-roles` | `add_access_role` | — |
|
|
111
|
+
|
|
112
|
+
Every one of these is also re-exported from the top-level `calendry_client` package, so `from calendry_client import add_room, set_room_equipment` works without knowing which module it lives in.
|
|
113
|
+
|
|
114
|
+
For anything not covered by a named convenience function (an unusual filter, a resource-specific field), the generic building blocks are always available:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from calendry_client import CalendryClient
|
|
118
|
+
from calendry_client._generic import list_rows, create_row, update_row, delete_row, get_relation, set_relation
|
|
119
|
+
|
|
120
|
+
list_rows(client, "offerings", term_id="term-1")
|
|
121
|
+
create_row(client, "offerings", {"termId": "term-1", "kindId": "kind-1", "title": "Ad-hoc offering"})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Working with relations
|
|
125
|
+
|
|
126
|
+
Relation setters replace the **entire** membership set in one call — there's no per-row add/remove, matching the API's `PUT` semantics:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from calendry_client import add_group, add_subgroup, set_offering_groups, set_person_roles
|
|
130
|
+
|
|
131
|
+
cohort = add_group(client, "dWI24-A")
|
|
132
|
+
section = add_subgroup(client, cohort["id"], "dWI24-A1", expected_size=28)
|
|
133
|
+
|
|
134
|
+
set_offering_groups(client, offering["id"], [section["id"]])
|
|
135
|
+
set_person_roles(client, person["id"], [lecturer_role["id"]])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Error handling
|
|
139
|
+
|
|
140
|
+
Any non-2xx response raises `CalendryAPIError`, carrying the HTTP status code and the parsed error payload (when the response was JSON):
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from calendry_client import CalendryAPIError, add_room
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
add_room(client, code="R-101", name="Room 101")
|
|
147
|
+
except CalendryAPIError as exc:
|
|
148
|
+
print(exc.status_code, exc.payload)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Importing an xlsx planning workbook
|
|
152
|
+
|
|
153
|
+
`scripts/import_xlsx.py` is a **reference script**, not an installable package or a `calendry-client` entry point — it's kept in this repo purely to demonstrate the library end-to-end and isn't published to PyPI. It reads a Calendry planning workbook — Offerings, Rooms, People, Groups and sub-groups, in the shape of [`Test_anonymized.xlsx`](Test_anonymized.xlsx) — and creates every row through the library above. It locates sheets by header row (not by name), so re-ordering or renaming sheets is fine as long as the columns are there.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
python scripts/import_xlsx.py \
|
|
157
|
+
--server-url https://calendry.example.com \
|
|
158
|
+
--token "$CALENDRY_API_TOKEN" \
|
|
159
|
+
--input Test_anonymized.xlsx \
|
|
160
|
+
--dry-run # preview only; drop this flag to actually write
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
| Flag | Required | Default | Purpose |
|
|
164
|
+
|---|---|---|---|
|
|
165
|
+
| `--input PATH` | yes | — | Workbook to import |
|
|
166
|
+
| `--server-url URL` | yes* | `$CALENDRY_SERVER_URL` | Calendry server base URL |
|
|
167
|
+
| `--token TOKEN` | no | `$CALENDRY_API_TOKEN` | Bearer token; omitted requests are sent unauthenticated with a warning |
|
|
168
|
+
| `--term-start-date` / `--term-end-date` | no | derived from the weeks sheet | Override the term's dates; only valid when the sheet has a single `Semester` value |
|
|
169
|
+
| `--default-frequency` | no | `1` | Sessions/week for created offerings (not present in the sheet) |
|
|
170
|
+
| `--default-duration-blocks` | no | `1` | Duration in grid blocks for created offerings (not present in the sheet) |
|
|
171
|
+
| `--dry-run` | no | off | Print the plan without calling the API |
|
|
172
|
+
|
|
173
|
+
\* required unless set via the environment variable.
|
|
174
|
+
|
|
175
|
+
What gets imported, from which columns:
|
|
176
|
+
|
|
177
|
+
- **Rooms** — `Name Raum`, `Anzahl Personen` (capacity), `Prio` (ranking), `Anzeigen Grid` (`isActive`). A room named `Online` is marked `isVirtual`. An `Ausstattung` value creates/links an `equipment` row via `rooms/equipment`. The `Buchung verursacht Raumkonflikte` column has no equivalent Calendry resource and is intentionally skipped (logged, not imported).
|
|
178
|
+
- **People** — the `Name` (lecturer) column, deduplicated by name across all rows.
|
|
179
|
+
- **Groups & sub-groups** — the `Planungsgruppe` column; codes sharing a common non-numeric prefix (e.g. `dWI24-A1`..`dWI24-A17`) become sub-groups of a shared parent group (`dWI24-A`).
|
|
180
|
+
- **Offerings** — one per (course, group) row, titled `"<Veranstaltung Abk> - <Planungsgruppe>"`, linked to its group and lecturer. `Ist_vorlesung` selects (and creates on first use) a `Vorlesung`/`Übung` session kind; `Pax` becomes `requiredCapacity`; `Online` becomes `allowOnline`. The term is looked up/created from the `Semester` column, with start/end dates derived from the weeks sheet (matching `Wochennummer` values against the `Semester` string) unless overridden with `--term-start-date`/`--term-end-date`.
|
|
181
|
+
|
|
182
|
+
The script is idempotent: it lists existing rows before creating anything and reuses matches (by name/code/key, as appropriate), so re-running it against the same server does not create duplicates.
|
|
183
|
+
|
|
184
|
+
## Development
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pip install -e ".[xlsx]" pytest
|
|
188
|
+
pytest
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Publishing
|
|
192
|
+
|
|
193
|
+
Pushing a `vX.Y.Z` tag runs [`.github/workflows/publish.yml`](.github/workflows/publish.yml), which builds the sdist/wheel and publishes them to PyPI via [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (configure a `pypi` GitHub environment as the trusted publisher for this repository, or swap in a `PYPI_API_TOKEN` secret and pass it to the publish step). [`.github/workflows/ci.yml`](.github/workflows/ci.yml) runs the test suite and a build check on every push and pull request. The package version is derived from git tags (via `hatch-vcs`) — there is no version number to bump by hand.
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT.
|