42api 0.1.0__tar.gz → 0.1.2__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.
- {42api-0.1.0 → 42api-0.1.2}/.github/workflows/ci.yml +1 -1
- 42api-0.1.2/.github/workflows/publish.yml +44 -0
- {42api-0.1.0 → 42api-0.1.2}/.gitignore +4 -1
- {42api-0.1.0 → 42api-0.1.2}/PKG-INFO +11 -8
- {42api-0.1.0 → 42api-0.1.2}/README.md +8 -6
- {42api-0.1.0 → 42api-0.1.2}/pyproject.toml +3 -2
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/__init__.py +12 -2
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/client.py +10 -0
- 42api-0.1.2/src/intra42/_async/resources/accreditations.py +16 -0
- 42api-0.1.2/src/intra42/_async/resources/cursus.py +23 -0
- 42api-0.1.2/src/intra42/_async/resources/exams.py +23 -0
- 42api-0.1.2/src/intra42/_async/resources/projects.py +19 -0
- 42api-0.1.2/src/intra42/_async/resources/projects_users.py +22 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/users.py +4 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/client.py +10 -0
- 42api-0.1.2/src/intra42/_sync/resources/accreditations.py +22 -0
- 42api-0.1.2/src/intra42/_sync/resources/cursus.py +29 -0
- 42api-0.1.2/src/intra42/_sync/resources/exams.py +27 -0
- 42api-0.1.2/src/intra42/_sync/resources/projects.py +25 -0
- 42api-0.1.2/src/intra42/_sync/resources/projects_users.py +26 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/users.py +4 -0
- 42api-0.1.2/src/intra42/models/accreditation.py +29 -0
- 42api-0.1.2/src/intra42/models/cursus.py +41 -0
- 42api-0.1.2/src/intra42/models/exam.py +32 -0
- 42api-0.1.2/src/intra42/models/project.py +106 -0
- 42api-0.1.2/src/intra42/models/project_user.py +74 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/user.py +6 -0
- 42api-0.1.2/tests/integration/resources/test_resources_accreditations.py +78 -0
- 42api-0.1.2/tests/integration/resources/test_resources_cursus.py +81 -0
- 42api-0.1.2/tests/integration/resources/test_resources_exams.py +159 -0
- 42api-0.1.2/tests/integration/resources/test_resources_projects.py +193 -0
- 42api-0.1.2/tests/integration/resources/test_resources_projects_users.py +176 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/test_pagination_walk.py +3 -3
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_public_api.py +10 -0
- {42api-0.1.0 → 42api-0.1.2}/uv.lock +2 -2
- {42api-0.1.0 → 42api-0.1.2}/.pre-commit-config.yaml +0 -0
- {42api-0.1.0 → 42api-0.1.2}/LICENSE +0 -0
- {42api-0.1.0 → 42api-0.1.2}/scripts/unasync_generate.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/__init__.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/query.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/__init__.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/base.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/campus_users.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/campuses.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/events.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_async/resources/locations.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_auth.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_config.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_pagination.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_query_params.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_rate_limit.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/__init__.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/query.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/__init__.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/base.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/campus_users.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/campuses.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/events.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/_sync/resources/locations.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/exceptions.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/__init__.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/base.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/campus.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/campus_user.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/event.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/models/location.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/src/intra42/py.typed +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/conftest.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/resources/test_resources_campus_users.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/resources/test_resources_campuses.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/resources/test_resources_events.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/resources/test_resources_locations.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/test_client_integration.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/integration/test_nested_access.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_auth.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_models_campus.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_models_user.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_pagination.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_query_builder.py +0 -0
- {42api-0.1.0 → 42api-0.1.2}/tests/unit/test_rate_limit.py +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment:
|
|
16
|
+
name: pypi
|
|
17
|
+
url: https://pypi.org/project/42api/
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v3
|
|
23
|
+
with:
|
|
24
|
+
version: "latest"
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install 3.12
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: uv sync --all-extras
|
|
31
|
+
|
|
32
|
+
- name: Re-run checks before publishing
|
|
33
|
+
run: |
|
|
34
|
+
uv run ruff check .
|
|
35
|
+
uv run ruff format --check .
|
|
36
|
+
uv run python scripts/unasync_generate.py --check
|
|
37
|
+
uv run mypy
|
|
38
|
+
uv run pytest
|
|
39
|
+
|
|
40
|
+
- name: Build
|
|
41
|
+
run: uv build
|
|
42
|
+
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
run: uv publish --trusted-publishing always
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: 42api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Object-oriented Python client for the 42 School API (api.intra.42.fr)
|
|
5
5
|
Project-URL: Homepage, https://github.com/Shadowaker/42api
|
|
6
6
|
Author-email: Dan <danyridolfo98@gmail.com>
|
|
7
7
|
License: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
|
-
Keywords: 42,42school,api,client,intra
|
|
9
|
+
Keywords: 42,42school,42staff,api,client,intra,intra42
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Framework :: AsyncIO
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Typing :: Typed
|
|
21
22
|
Requires-Python: >=3.10
|
|
22
23
|
Requires-Dist: httpx>=0.27
|
|
@@ -54,7 +55,7 @@ with Client(client_id="...", client_secret="...") as client:
|
|
|
54
55
|
user = client.users.get("jdoe")
|
|
55
56
|
print(user.login, user.email)
|
|
56
57
|
|
|
57
|
-
for user in client.users.filter(
|
|
58
|
+
for user in client.users.filter(primary_campus_id=1).sort("-pool_year"):
|
|
58
59
|
print(user.login)
|
|
59
60
|
```
|
|
60
61
|
|
|
@@ -68,7 +69,7 @@ from intra42 import AsyncClient
|
|
|
68
69
|
async def main() -> None:
|
|
69
70
|
async with AsyncClient(client_id="...", client_secret="...") as client:
|
|
70
71
|
user = await client.users.get("jdoe")
|
|
71
|
-
async for user in client.users.filter(
|
|
72
|
+
async for user in client.users.filter(primary_campus_id=1).sort("-pool_year"):
|
|
72
73
|
print(user.login)
|
|
73
74
|
|
|
74
75
|
|
|
@@ -107,10 +108,12 @@ and `NetworkError` (connection/timeout failures).
|
|
|
107
108
|
|
|
108
109
|
## Status
|
|
109
110
|
|
|
110
|
-
Early-stage: currently covers `users`, `
|
|
111
|
-
`events`,
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
Early-stage: currently covers `users`, `accreditations`, `campuses`,
|
|
112
|
+
`campus_users`, `cursus`, `events`, `exams`, `locations`, `projects`, and
|
|
113
|
+
`projects_users` (`exams`, `locations`, and `projects_users` each include
|
|
114
|
+
their `graph` analytics endpoint). More resources are added incrementally
|
|
115
|
+
on top of the same client/model/query-builder pattern.
|
|
116
|
+
The api with restricted access are not implemented currently.
|
|
114
117
|
|
|
115
118
|
## Development
|
|
116
119
|
|
|
@@ -29,7 +29,7 @@ with Client(client_id="...", client_secret="...") as client:
|
|
|
29
29
|
user = client.users.get("jdoe")
|
|
30
30
|
print(user.login, user.email)
|
|
31
31
|
|
|
32
|
-
for user in client.users.filter(
|
|
32
|
+
for user in client.users.filter(primary_campus_id=1).sort("-pool_year"):
|
|
33
33
|
print(user.login)
|
|
34
34
|
```
|
|
35
35
|
|
|
@@ -43,7 +43,7 @@ from intra42 import AsyncClient
|
|
|
43
43
|
async def main() -> None:
|
|
44
44
|
async with AsyncClient(client_id="...", client_secret="...") as client:
|
|
45
45
|
user = await client.users.get("jdoe")
|
|
46
|
-
async for user in client.users.filter(
|
|
46
|
+
async for user in client.users.filter(primary_campus_id=1).sort("-pool_year"):
|
|
47
47
|
print(user.login)
|
|
48
48
|
|
|
49
49
|
|
|
@@ -82,10 +82,12 @@ and `NetworkError` (connection/timeout failures).
|
|
|
82
82
|
|
|
83
83
|
## Status
|
|
84
84
|
|
|
85
|
-
Early-stage: currently covers `users`, `
|
|
86
|
-
`events`,
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
Early-stage: currently covers `users`, `accreditations`, `campuses`,
|
|
86
|
+
`campus_users`, `cursus`, `events`, `exams`, `locations`, `projects`, and
|
|
87
|
+
`projects_users` (`exams`, `locations`, and `projects_users` each include
|
|
88
|
+
their `graph` analytics endpoint). More resources are added incrementally
|
|
89
|
+
on top of the same client/model/query-builder pattern.
|
|
90
|
+
The api with restricted access are not implemented currently.
|
|
89
91
|
|
|
90
92
|
## Development
|
|
91
93
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "42api"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Object-oriented Python client for the 42 School API (api.intra.42.fr)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -18,10 +18,11 @@ classifiers = [
|
|
|
18
18
|
"Programming Language :: Python :: 3.11",
|
|
19
19
|
"Programming Language :: Python :: 3.12",
|
|
20
20
|
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
21
22
|
"Typing :: Typed",
|
|
22
23
|
"Framework :: AsyncIO",
|
|
23
24
|
]
|
|
24
|
-
keywords = ["42", "42school", "api", "client", "intra"]
|
|
25
|
+
keywords = ["42", "42school", "api", "client", "intra", "intra42", "42staff"]
|
|
25
26
|
|
|
26
27
|
[project.urls]
|
|
27
28
|
Homepage = "https://github.com/Shadowaker/42api"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
with Client(client_id, client_secret) as client:
|
|
6
6
|
user = client.users.get("jdoe")
|
|
7
|
-
for user in client.users.filter(
|
|
7
|
+
for user in client.users.filter(primary_campus_id=1).sort("-pool_year"):
|
|
8
8
|
...
|
|
9
9
|
|
|
10
10
|
An async client with the same interface is available as ``AsyncClient``.
|
|
@@ -25,10 +25,15 @@ from .exceptions import (
|
|
|
25
25
|
ServerError,
|
|
26
26
|
ValidationError,
|
|
27
27
|
)
|
|
28
|
+
from .models.accreditation import Accreditation
|
|
28
29
|
from .models.campus import Campus
|
|
29
30
|
from .models.campus_user import CampusUser
|
|
31
|
+
from .models.cursus import Cursus
|
|
30
32
|
from .models.event import Event
|
|
33
|
+
from .models.exam import Exam
|
|
31
34
|
from .models.location import Location
|
|
35
|
+
from .models.project import Project
|
|
36
|
+
from .models.project_user import ProjectUser
|
|
32
37
|
from .models.user import User
|
|
33
38
|
|
|
34
39
|
__all__ = [
|
|
@@ -36,10 +41,15 @@ __all__ = [
|
|
|
36
41
|
"Client",
|
|
37
42
|
"ClientConfig",
|
|
38
43
|
"User",
|
|
44
|
+
"Accreditation",
|
|
39
45
|
"Campus",
|
|
40
46
|
"CampusUser",
|
|
47
|
+
"Cursus",
|
|
41
48
|
"Event",
|
|
49
|
+
"Exam",
|
|
42
50
|
"Location",
|
|
51
|
+
"Project",
|
|
52
|
+
"ProjectUser",
|
|
43
53
|
"FortyTwoAPIError",
|
|
44
54
|
"AuthenticationError",
|
|
45
55
|
"PermissionDeniedError",
|
|
@@ -50,4 +60,4 @@ __all__ = [
|
|
|
50
60
|
"NetworkError",
|
|
51
61
|
]
|
|
52
62
|
|
|
53
|
-
__version__ = "0.1.
|
|
63
|
+
__version__ = "0.1.2"
|
|
@@ -12,10 +12,15 @@ from .._auth import TokenManager
|
|
|
12
12
|
from .._config import DEFAULT_BASE_URL, ClientConfig
|
|
13
13
|
from .._rate_limit import RateLimiter
|
|
14
14
|
from ..exceptions import NetworkError, raise_for_status
|
|
15
|
+
from .resources.accreditations import AsyncAccreditationsResource
|
|
15
16
|
from .resources.campus_users import AsyncCampusUsersResource
|
|
16
17
|
from .resources.campuses import AsyncCampusesResource
|
|
18
|
+
from .resources.cursus import AsyncCursusResource
|
|
17
19
|
from .resources.events import AsyncEventsResource
|
|
20
|
+
from .resources.exams import AsyncExamsResource
|
|
18
21
|
from .resources.locations import AsyncLocationsResource
|
|
22
|
+
from .resources.projects import AsyncProjectsResource
|
|
23
|
+
from .resources.projects_users import AsyncProjectUsersResource
|
|
19
24
|
from .resources.users import AsyncUsersResource
|
|
20
25
|
|
|
21
26
|
|
|
@@ -57,10 +62,15 @@ class AsyncClient:
|
|
|
57
62
|
)
|
|
58
63
|
|
|
59
64
|
self.users = AsyncUsersResource(self)
|
|
65
|
+
self.accreditations = AsyncAccreditationsResource(self)
|
|
60
66
|
self.campuses = AsyncCampusesResource(self)
|
|
61
67
|
self.campus_users = AsyncCampusUsersResource(self)
|
|
68
|
+
self.cursus = AsyncCursusResource(self)
|
|
62
69
|
self.events = AsyncEventsResource(self)
|
|
70
|
+
self.exams = AsyncExamsResource(self)
|
|
63
71
|
self.locations = AsyncLocationsResource(self)
|
|
72
|
+
self.projects = AsyncProjectsResource(self)
|
|
73
|
+
self.projects_users = AsyncProjectUsersResource(self)
|
|
64
74
|
|
|
65
75
|
@property
|
|
66
76
|
def base_url(self) -> str:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ...models.accreditation import Accreditation
|
|
4
|
+
from .base import AsyncResource
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncAccreditationsResource(AsyncResource[Accreditation]):
|
|
8
|
+
path = "/accreditations"
|
|
9
|
+
model = Accreditation
|
|
10
|
+
|
|
11
|
+
def _bind_relations(self, instance: Accreditation) -> None:
|
|
12
|
+
# Referenced in the users apidoc page: GET /accreditations/:id/users.
|
|
13
|
+
instance._bind_relation(
|
|
14
|
+
"users",
|
|
15
|
+
lambda: self._client.users._queryset(f"/accreditations/{instance.id}/users"),
|
|
16
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ...models.cursus import Cursus
|
|
4
|
+
from .base import AsyncResource
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncCursusResource(AsyncResource[Cursus]):
|
|
8
|
+
path = "/cursus"
|
|
9
|
+
model = Cursus
|
|
10
|
+
|
|
11
|
+
def _bind_relations(self, instance: Cursus) -> None:
|
|
12
|
+
instance._bind_relation(
|
|
13
|
+
"users",
|
|
14
|
+
lambda: self._client.users._queryset(f"/cursus/{instance.id}/users"),
|
|
15
|
+
)
|
|
16
|
+
instance._bind_relation(
|
|
17
|
+
"events",
|
|
18
|
+
lambda: self._client.events._queryset(f"/cursus/{instance.id}/events"),
|
|
19
|
+
)
|
|
20
|
+
instance._bind_relation(
|
|
21
|
+
"projects",
|
|
22
|
+
lambda: self._client.projects._queryset(f"/cursus/{instance.id}/projects"),
|
|
23
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ...models.exam import Exam
|
|
4
|
+
from .base import AsyncResource
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncExamsResource(AsyncResource[Exam]):
|
|
8
|
+
path = "/exams"
|
|
9
|
+
model = Exam
|
|
10
|
+
|
|
11
|
+
async def graph(
|
|
12
|
+
self, *, field: str | None = None, interval: str | None = None
|
|
13
|
+
) -> dict[str, int]:
|
|
14
|
+
"""Grouped temporal counts (``GET /exams/graph...``)."""
|
|
15
|
+
path = "/exams/graph"
|
|
16
|
+
if interval is not None and field is None:
|
|
17
|
+
field = "created_at"
|
|
18
|
+
if field is not None:
|
|
19
|
+
path += f"/on/{field}"
|
|
20
|
+
if interval is not None:
|
|
21
|
+
path += f"/by/{interval}"
|
|
22
|
+
response = await self._client.request("GET", path)
|
|
23
|
+
return response.json()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ...models.project import Project
|
|
4
|
+
from .base import AsyncResource
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncProjectsResource(AsyncResource[Project]):
|
|
8
|
+
path = "/projects"
|
|
9
|
+
model = Project
|
|
10
|
+
|
|
11
|
+
def _bind_relations(self, instance: Project) -> None:
|
|
12
|
+
# Referenced in the projects_users apidoc page:
|
|
13
|
+
# GET /projects/:id/projects_users.
|
|
14
|
+
instance._bind_relation(
|
|
15
|
+
"projects_users",
|
|
16
|
+
lambda: self._client.projects_users._queryset(
|
|
17
|
+
f"/projects/{instance.id}/projects_users"
|
|
18
|
+
),
|
|
19
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ...models.project_user import ProjectUser
|
|
4
|
+
from .base import AsyncResource
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AsyncProjectUsersResource(AsyncResource[ProjectUser]):
|
|
8
|
+
path = "/projects_users"
|
|
9
|
+
model = ProjectUser
|
|
10
|
+
|
|
11
|
+
async def graph(
|
|
12
|
+
self, *, field: str | None = None, interval: str | None = None
|
|
13
|
+
) -> dict[str, int]:
|
|
14
|
+
path = "/projects_users/graph"
|
|
15
|
+
if interval is not None and field is None:
|
|
16
|
+
field = "created_at"
|
|
17
|
+
if field is not None:
|
|
18
|
+
path += f"/on/{field}"
|
|
19
|
+
if interval is not None:
|
|
20
|
+
path += f"/by/{interval}"
|
|
21
|
+
response = await self._client.request("GET", path)
|
|
22
|
+
return response.json()
|
|
@@ -21,3 +21,7 @@ class AsyncUsersResource(AsyncResource[User]):
|
|
|
21
21
|
"locations",
|
|
22
22
|
lambda: self._client.locations._queryset(f"/users/{instance.id}/locations"),
|
|
23
23
|
)
|
|
24
|
+
instance._bind_relation(
|
|
25
|
+
"projects_users",
|
|
26
|
+
lambda: self._client.projects_users._queryset(f"/users/{instance.id}/projects_users"),
|
|
27
|
+
)
|
|
@@ -18,10 +18,15 @@ from .._auth import TokenManager
|
|
|
18
18
|
from .._config import DEFAULT_BASE_URL, ClientConfig
|
|
19
19
|
from .._rate_limit import RateLimiter
|
|
20
20
|
from ..exceptions import NetworkError, raise_for_status
|
|
21
|
+
from .resources.accreditations import AccreditationsResource
|
|
21
22
|
from .resources.campus_users import CampusUsersResource
|
|
22
23
|
from .resources.campuses import CampusesResource
|
|
24
|
+
from .resources.cursus import CursusResource
|
|
23
25
|
from .resources.events import EventsResource
|
|
26
|
+
from .resources.exams import ExamsResource
|
|
24
27
|
from .resources.locations import LocationsResource
|
|
28
|
+
from .resources.projects import ProjectsResource
|
|
29
|
+
from .resources.projects_users import ProjectUsersResource
|
|
25
30
|
from .resources.users import UsersResource
|
|
26
31
|
|
|
27
32
|
|
|
@@ -63,10 +68,15 @@ class Client:
|
|
|
63
68
|
)
|
|
64
69
|
|
|
65
70
|
self.users = UsersResource(self)
|
|
71
|
+
self.accreditations = AccreditationsResource(self)
|
|
66
72
|
self.campuses = CampusesResource(self)
|
|
67
73
|
self.campus_users = CampusUsersResource(self)
|
|
74
|
+
self.cursus = CursusResource(self)
|
|
68
75
|
self.events = EventsResource(self)
|
|
76
|
+
self.exams = ExamsResource(self)
|
|
69
77
|
self.locations = LocationsResource(self)
|
|
78
|
+
self.projects = ProjectsResource(self)
|
|
79
|
+
self.projects_users = ProjectUsersResource(self)
|
|
70
80
|
|
|
71
81
|
@property
|
|
72
82
|
def base_url(self) -> str:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# GENERATED FILE — DO NOT EDIT BY HAND.
|
|
2
|
+
#
|
|
3
|
+
# Generated from the corresponding module under intra42._async by
|
|
4
|
+
# scripts/unasync_generate.py (via the `unasync` library). Edit the
|
|
5
|
+
# async source and re-run that script instead.
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ...models.accreditation import Accreditation
|
|
10
|
+
from .base import Resource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AccreditationsResource(Resource[Accreditation]):
|
|
14
|
+
path = "/accreditations"
|
|
15
|
+
model = Accreditation
|
|
16
|
+
|
|
17
|
+
def _bind_relations(self, instance: Accreditation) -> None:
|
|
18
|
+
# Referenced in the users apidoc page: GET /accreditations/:id/users.
|
|
19
|
+
instance._bind_relation(
|
|
20
|
+
"users",
|
|
21
|
+
lambda: self._client.users._queryset(f"/accreditations/{instance.id}/users"),
|
|
22
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# GENERATED FILE — DO NOT EDIT BY HAND.
|
|
2
|
+
#
|
|
3
|
+
# Generated from the corresponding module under intra42._async by
|
|
4
|
+
# scripts/unasync_generate.py (via the `unasync` library). Edit the
|
|
5
|
+
# async source and re-run that script instead.
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ...models.cursus import Cursus
|
|
10
|
+
from .base import Resource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CursusResource(Resource[Cursus]):
|
|
14
|
+
path = "/cursus"
|
|
15
|
+
model = Cursus
|
|
16
|
+
|
|
17
|
+
def _bind_relations(self, instance: Cursus) -> None:
|
|
18
|
+
instance._bind_relation(
|
|
19
|
+
"users",
|
|
20
|
+
lambda: self._client.users._queryset(f"/cursus/{instance.id}/users"),
|
|
21
|
+
)
|
|
22
|
+
instance._bind_relation(
|
|
23
|
+
"events",
|
|
24
|
+
lambda: self._client.events._queryset(f"/cursus/{instance.id}/events"),
|
|
25
|
+
)
|
|
26
|
+
instance._bind_relation(
|
|
27
|
+
"projects",
|
|
28
|
+
lambda: self._client.projects._queryset(f"/cursus/{instance.id}/projects"),
|
|
29
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# GENERATED FILE — DO NOT EDIT BY HAND.
|
|
2
|
+
#
|
|
3
|
+
# Generated from the corresponding module under intra42._async by
|
|
4
|
+
# scripts/unasync_generate.py (via the `unasync` library). Edit the
|
|
5
|
+
# async source and re-run that script instead.
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ...models.exam import Exam
|
|
10
|
+
from .base import Resource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExamsResource(Resource[Exam]):
|
|
14
|
+
path = "/exams"
|
|
15
|
+
model = Exam
|
|
16
|
+
|
|
17
|
+
def graph(self, *, field: str | None = None, interval: str | None = None) -> dict[str, int]:
|
|
18
|
+
"""Grouped temporal counts (``GET /exams/graph...``)."""
|
|
19
|
+
path = "/exams/graph"
|
|
20
|
+
if interval is not None and field is None:
|
|
21
|
+
field = "created_at"
|
|
22
|
+
if field is not None:
|
|
23
|
+
path += f"/on/{field}"
|
|
24
|
+
if interval is not None:
|
|
25
|
+
path += f"/by/{interval}"
|
|
26
|
+
response = self._client.request("GET", path)
|
|
27
|
+
return response.json()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# GENERATED FILE — DO NOT EDIT BY HAND.
|
|
2
|
+
#
|
|
3
|
+
# Generated from the corresponding module under intra42._async by
|
|
4
|
+
# scripts/unasync_generate.py (via the `unasync` library). Edit the
|
|
5
|
+
# async source and re-run that script instead.
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ...models.project import Project
|
|
10
|
+
from .base import Resource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ProjectsResource(Resource[Project]):
|
|
14
|
+
path = "/projects"
|
|
15
|
+
model = Project
|
|
16
|
+
|
|
17
|
+
def _bind_relations(self, instance: Project) -> None:
|
|
18
|
+
# Referenced in the projects_users apidoc page:
|
|
19
|
+
# GET /projects/:id/projects_users.
|
|
20
|
+
instance._bind_relation(
|
|
21
|
+
"projects_users",
|
|
22
|
+
lambda: self._client.projects_users._queryset(
|
|
23
|
+
f"/projects/{instance.id}/projects_users"
|
|
24
|
+
),
|
|
25
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# GENERATED FILE — DO NOT EDIT BY HAND.
|
|
2
|
+
#
|
|
3
|
+
# Generated from the corresponding module under intra42._async by
|
|
4
|
+
# scripts/unasync_generate.py (via the `unasync` library). Edit the
|
|
5
|
+
# async source and re-run that script instead.
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ...models.project_user import ProjectUser
|
|
10
|
+
from .base import Resource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ProjectUsersResource(Resource[ProjectUser]):
|
|
14
|
+
path = "/projects_users"
|
|
15
|
+
model = ProjectUser
|
|
16
|
+
|
|
17
|
+
def graph(self, *, field: str | None = None, interval: str | None = None) -> dict[str, int]:
|
|
18
|
+
path = "/projects_users/graph"
|
|
19
|
+
if interval is not None and field is None:
|
|
20
|
+
field = "created_at"
|
|
21
|
+
if field is not None:
|
|
22
|
+
path += f"/on/{field}"
|
|
23
|
+
if interval is not None:
|
|
24
|
+
path += f"/by/{interval}"
|
|
25
|
+
response = self._client.request("GET", path)
|
|
26
|
+
return response.json()
|
|
@@ -27,3 +27,7 @@ class UsersResource(Resource[User]):
|
|
|
27
27
|
"locations",
|
|
28
28
|
lambda: self._client.locations._queryset(f"/users/{instance.id}/locations"),
|
|
29
29
|
)
|
|
30
|
+
instance._bind_relation(
|
|
31
|
+
"projects_users",
|
|
32
|
+
lambda: self._client.projects_users._queryset(f"/users/{instance.id}/projects_users"),
|
|
33
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""The ``Accreditation`` resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from .base import FortyTwoModel
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .._async.query import AsyncQuerySet
|
|
12
|
+
from .._sync.query import QuerySet
|
|
13
|
+
from .user import User
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Accreditation(FortyTwoModel):
|
|
17
|
+
id: int
|
|
18
|
+
name: str | None = None
|
|
19
|
+
user_id: int | None = None
|
|
20
|
+
cursus_id: int | None = None
|
|
21
|
+
difficulty: int | None = None
|
|
22
|
+
validated: bool | None = None
|
|
23
|
+
created_at: datetime | None = None
|
|
24
|
+
updated_at: datetime | None = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def users(self) -> AsyncQuerySet[User] | QuerySet[User]:
|
|
28
|
+
"""Users holding this accreditation (``GET /accreditations/{id}/users``)."""
|
|
29
|
+
return self._relation("users")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""The ``Cursus`` resource (a curriculum/track, e.g. "42", "Piscine C")."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from .base import FortyTwoModel
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .._async.query import AsyncQuerySet
|
|
12
|
+
from .._sync.query import QuerySet
|
|
13
|
+
from .event import Event
|
|
14
|
+
from .project import Project
|
|
15
|
+
from .user import User
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Cursus(FortyTwoModel):
|
|
19
|
+
id: int
|
|
20
|
+
name: str | None = None
|
|
21
|
+
slug: str | None = None
|
|
22
|
+
kind: str | None = None
|
|
23
|
+
created_at: datetime | None = None
|
|
24
|
+
updated_at: datetime | None = None
|
|
25
|
+
restricted: bool | None = None
|
|
26
|
+
is_subscriptable: bool | None = None
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def users(self) -> AsyncQuerySet[User] | QuerySet[User]:
|
|
30
|
+
"""Users enrolled in this cursus (``GET /cursus/{id}/users``)."""
|
|
31
|
+
return self._relation("users")
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def events(self) -> AsyncQuerySet[Event] | QuerySet[Event]:
|
|
35
|
+
"""Events for this cursus (``GET /cursus/{id}/events``)."""
|
|
36
|
+
return self._relation("events")
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def projects(self) -> AsyncQuerySet[Project] | QuerySet[Project]:
|
|
40
|
+
"""Projects belonging to this cursus (``GET /cursus/{id}/projects``)."""
|
|
41
|
+
return self._relation("projects")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""The ``Exam`` resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from .base import FortyTwoModel
|
|
8
|
+
from .campus import Campus
|
|
9
|
+
from .cursus import Cursus
|
|
10
|
+
from .project import Project
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Exam(FortyTwoModel):
|
|
14
|
+
id: int
|
|
15
|
+
name: str | None = None
|
|
16
|
+
ip_range: str | None = None
|
|
17
|
+
begin_at: datetime | None = None
|
|
18
|
+
end_at: datetime | None = None
|
|
19
|
+
location: str | None = None
|
|
20
|
+
max_people: int | None = None
|
|
21
|
+
nbr_subscribers: int | None = None
|
|
22
|
+
created_at: datetime | None = None
|
|
23
|
+
updated_at: datetime | None = None
|
|
24
|
+
campus_id: int | None = None
|
|
25
|
+
campus: Campus | None = None
|
|
26
|
+
cursus: list[Cursus] | None = None
|
|
27
|
+
projects: list[Project] | None = None
|
|
28
|
+
visible: bool | None = None
|
|
29
|
+
validated: bool | None = None
|
|
30
|
+
validated_at: datetime | None = None
|
|
31
|
+
validator_id: int | None = None
|
|
32
|
+
prohibition_of_cancellation: int | None = None
|