Skip to content
This repository was archived by the owner on Jan 20, 2026. It is now read-only.

Commit 0c7b94b

Browse files
author
kurwjan
committed
⚡ feat: get web folders, get responsible of event
+ Added getting Lanis applet folders + Added getting the responsible of a event suggested by @baum-eule
1 parent 876405b commit 0c7b94b

8 files changed

Lines changed: 148 additions & 11 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
"empf",
99
"entrys",
1010
"exporteur",
11+
"farbe",
1112
"furo",
13+
"glyphicon",
14+
"Glyphicons",
1215
"hessen",
1316
"httpx",
1417
"inhalt",

docs/source/api/main.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ Functions
117117
.. currentmodule:: lanisapi.LanisClient
118118
.. autofunction:: get_apps
119119

120+
.. autofunction:: get_folders
121+
120122
.. autofunction:: get_available_apps
121123

122124
.. autofunction:: get_app_availability
@@ -125,4 +127,6 @@ Types
125127
^^^^^
126128

127129
.. currentmodule:: lanisapi.functions.apps
130+
.. autoclass:: Folder
131+
128132
.. autoclass:: App

docs/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
copyright = "2023, kurwjan"
77
author = "kurwjan"
88

9-
release = "0.3.1"
10-
version = "0.3.1"
9+
release = "0.3.2"
10+
version = "0.3.2"
1111

1212
# -- General configuration
1313

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "lanisapi"
7-
version = "0.3.1"
7+
version = "0.3.2"
88
authors = [
99
{ name = "kurwjan" }
1010
]

src/lanisapi/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""A lib to interact with the Schulportal Hessen. Use LanisClient to interact."""
22

33
from .client import LanisClient
4-
from .functions.apps import App
4+
from .functions.apps import App, Folder
55
from .functions.authentication_types import LanisAccount, LanisCookie, School
66
from .functions.calendar import Calendar
77
from .functions.conversations import Conversation
@@ -15,6 +15,7 @@
1515
"Calendar",
1616
"Conversation",
1717
"App",
18+
"Folder",
1819
"LanisAccount",
1920
"LanisCookie",
2021
"School",

src/lanisapi/client.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
import httpx
66

77
from .constants import LOGGER, URL
8-
from .functions.apps import App, _get_app_availability, _get_apps, _get_available_apps
8+
from .functions.apps import (
9+
App,
10+
Folder,
11+
_get_app_availability,
12+
_get_apps,
13+
_get_available_apps,
14+
_get_folders,
15+
)
916
from .functions.authentication_types import LanisAccount, LanisCookie
1017
from .functions.calendar import Calendar, _get_calendar, _get_calendar_month
1118
from .functions.conversations import Conversation, _get_conversations
@@ -66,7 +73,7 @@ def __init__( # noqa: D107
6673

6774
self.cryptor = Cryptor()
6875

69-
LOGGER.info("USING VERSION 0.3.1")
76+
LOGGER.info("USING VERSION 0.3.2")
7077

7178
LOGGER.warning("LANISAPI IS STILL IN A EARLY STAGE SO EXPECT BUGS.")
7279

@@ -321,3 +328,15 @@ def get_app_availability(self, app_name: str) -> bool:
321328
bool
322329
"""
323330
return _get_app_availability(app_name)
331+
332+
@requires_auth
333+
@handle_exceptions
334+
def get_folders(self) -> list[Folder]:
335+
"""Get all web folders from Lanis.
336+
337+
Returns
338+
-------
339+
list[Folder]
340+
A list of Folder.
341+
"""
342+
return _get_folders()

src/lanisapi/functions/apps.py

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""This script includes functions and classes for getting Lanis applets and checking for the availability of this library supported applets."""
22

3+
import re
34
from dataclasses import dataclass
45
from difflib import SequenceMatcher
56
from functools import cache
@@ -9,6 +10,25 @@
910
from ..helpers.request import Request
1011

1112

13+
@dataclass
14+
class Folder:
15+
"""A Lanis web folder.
16+
17+
Parameters
18+
----------
19+
name : str
20+
The name.
21+
symbol : str
22+
A symbol which represents this applet. Lanis uses Font Awesome and Glyphicons for this.
23+
colour : str
24+
The colour of these small top bars which you can see on Lanis.
25+
"""
26+
27+
name: str
28+
symbol: str
29+
colour: str
30+
31+
1232
@dataclass
1333
class App:
1434
"""A Lanis web applet.
@@ -19,17 +39,51 @@ class App:
1939
The name.
2040
colour : str
2141
The colour which you can see on Lanis.
22-
group : str
42+
folder : list of Folder
2343
The groups, category, folder or how you want to call it of the app.
2444
You can see it on the index page of Lanis.
2545
link : str
2646
The full Lanis link.
47+
symbol : str
48+
A symbol which represents this applet. Lanis uses Font Awesome and Glyphicons for this.
2749
"""
2850

2951
name: str
3052
colour: str
31-
group: str
53+
folder: list[Folder]
3254
link: str
55+
symbol: str
56+
57+
58+
@cache
59+
def _get_folders() -> list[Folder]:
60+
"""Get all web folders from Lanis.
61+
62+
Returns
63+
-------
64+
list[Folder]
65+
A list of Folder.
66+
"""
67+
folders: list[Folder] = []
68+
69+
response = Request.get(URL.index, params={"a": "ajax", "f": "apps"})
70+
71+
for entry in response.json()["folders"]:
72+
folders.append(
73+
Folder(
74+
name=entry["name"],
75+
symbol=re.sub(
76+
r"(fas.|fa.|flip-\w+|glyphicon.|-o|-alt|fw|\n|\r| )",
77+
"",
78+
entry["logo"],
79+
),
80+
colour=None if entry["farbe"] == "000000" else entry["farbe"],
81+
)
82+
)
83+
84+
LOGGER.info("Get folders: Success.")
85+
86+
return folders
3387

3488

3589
@cache
@@ -43,15 +97,29 @@ def _get_apps() -> list[App]:
4397
"""
4498
apps: list[App] = []
4599

100+
folders = _get_folders()
101+
46102
response = Request.get(URL.index, params={"a": "ajax", "f": "apps"})
47103

48104
for entry in response.json()["entrys"]:
105+
# Get Folder dataclass from folder list.
106+
folder: list[str] = []
107+
for entry_folder in entry["Ordner"]:
108+
folder.append(
109+
next(folder for folder in folders if folder.name == entry_folder)
110+
)
111+
49112
apps.append(
50113
App(
51114
name=entry["Name"],
52115
colour=entry["Farbe"],
53-
group=entry["Ordner"][0],
116+
folder=folder,
54117
link=urljoin(URL.base, entry["link"]),
118+
symbol=re.sub(
119+
r"(fas.|fa.|flip-\w+|glyphicon.|-o|-alt|fw|\n|\r| )",
120+
"",
121+
entry["Logo"],
122+
),
55123
)
56124
)
57125

@@ -84,7 +152,7 @@ def _get_available_apps() -> list[str]:
84152
for implemented in implemented_apps:
85153
if (
86154
SequenceMatcher(None, app.name.lower(), implemented.lower()).ratio()
87-
> 0.8 # Probably need to tweak this number
155+
> 0.8 # Probably need to tweak this number
88156
):
89157
available_apps.append(implemented)
90158

src/lanisapi/functions/calendar.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import calendar
44
from dataclasses import dataclass
55
from datetime import datetime
6+
from functools import partial
7+
from typing import Callable
68

79
from ..constants import LOGGER, URL
810
from ..helpers.request import Request
@@ -21,7 +23,7 @@ class Calendar:
2123
events : list[Calendar] or list[dict[str, any]]
2224
Use `events` to access the most important properties.
2325
It is either in `Calendar` or `JSON` format.
24-
When it is in JSON format it has all including unnecessary properties.
26+
When it is in JSON format it has all, including unnecessary ones, properties.
2527
"""
2628

2729
@dataclass
@@ -43,6 +45,9 @@ class Event:
4345
Could also exceed the calendars start and end.
4446
whole_day : bool
4547
Does it happen the whole day or only between a specific time.
48+
responsible : Callable
49+
The person who is responsible for this event.
50+
You need to call this first, then it returns (hopefully) a string.
4651
"""
4752

4853
title: str
@@ -51,12 +56,38 @@ class Event:
5156
start: datetime
5257
end: datetime
5358
whole_day: bool
59+
responsible: Callable
5460

5561
start: datetime
5662
end: datetime
5763
events: list[Event] | list[dict[str, any]] = None
5864

5965

66+
def _get_responsible(id: str) -> str:
67+
"""Get the responsible person of an event.
68+
69+
Parameters
70+
----------
71+
id : str
72+
The id of the calendar event, if None just return None.
73+
74+
Returns
75+
-------
76+
str
77+
"""
78+
if not id:
79+
return None
80+
81+
data = {
82+
"f": "getEvent",
83+
"id": id,
84+
}
85+
86+
response = Request.post(URL.calendar, data=data)
87+
88+
return response.json()["properties"]["verantwortlich"]
89+
90+
6091
def _get_calendar_month(json: bool = False) -> Calendar:
6192
"""Use the _get_calendar() function but only returns all events of the current month.
6293
@@ -140,13 +171,24 @@ def _get_calendar(start: datetime, end: datetime, json: bool = False) -> Calenda
140171
# Get JSON and map it to `Calendar` data class.
141172
calendar = Calendar(start, end, events=[])
142173
for data in calendar_raw_data.json():
174+
# If data["Verantwortlich"] doesn't even exist, we can just return None when called.
175+
try:
176+
responsible = (
177+
partial(_get_responsible, id=data["Id"])
178+
if data["Verantwortlich"]
179+
else partial(_get_responsible, id=None)
180+
)
181+
except KeyError:
182+
responsible = partial(_get_responsible, id=None)
183+
143184
calendar_data = Calendar.Event(
144185
title=data["title"],
145186
description=data["description"],
146187
place=data["Ort"],
147188
start=datetime.strptime(data["Anfang"], "%Y-%m-%d %H:%M:%S"),
148189
end=datetime.strptime(data["Ende"], "%Y-%m-%d %H:%M:%S"),
149190
whole_day=data["allDay"],
191+
responsible=responsible,
150192
)
151193
calendar.events.append(calendar_data)
152194

0 commit comments

Comments
 (0)