zermelo-api-vogk 0.2.16__py3-none-any.whl
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.
- zermelo_api/__init__.py +8 -0
- zermelo_api/src/__init__.py +0 -0
- zermelo_api/src/appointments.py +92 -0
- zermelo_api/src/branches.py +93 -0
- zermelo_api/src/config.py +52 -0
- zermelo_api/src/credentials.py +26 -0
- zermelo_api/src/groepen.py +35 -0
- zermelo_api/src/leerjaren.py +48 -0
- zermelo_api/src/lesgroepen.py +238 -0
- zermelo_api/src/logger.py +47 -0
- zermelo_api/src/time_utils.py +18 -0
- zermelo_api/src/users.py +139 -0
- zermelo_api/src/vakken.py +53 -0
- zermelo_api/src/zermelo_api.py +134 -0
- zermelo_api_vogk-0.2.16.dist-info/LICENSE +21 -0
- zermelo_api_vogk-0.2.16.dist-info/METADATA +112 -0
- zermelo_api_vogk-0.2.16.dist-info/RECORD +19 -0
- zermelo_api_vogk-0.2.16.dist-info/WHEEL +5 -0
- zermelo_api_vogk-0.2.16.dist-info/top_level.txt +1 -0
zermelo_api/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from .src.zermelo_api import ZermeloAPI
|
|
2
|
+
from .src.branches import Branch, Branches
|
|
3
|
+
from .src.time_utils import *
|
|
4
|
+
from .src.users import Leerling, Medewerker, Leerlingen, Personeel
|
|
5
|
+
from .src.lesgroepen import Lesgroepen, Lesgroep
|
|
6
|
+
from .src.leerjaren import Leerjaar, Leerjaren
|
|
7
|
+
from .src.appointments import get_user_appointments, get_department_updates, Appointment
|
|
8
|
+
from .src.vakken import Vakken, Vak
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from .zermelo_api import zermelo, from_zermelo_dict
|
|
2
|
+
from dataclasses import dataclass, InitVar, field
|
|
3
|
+
from logger import makeLogger, DEBUG
|
|
4
|
+
|
|
5
|
+
logger = makeLogger("APPS")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Appointment:
|
|
10
|
+
id: int
|
|
11
|
+
appointmentInstance: int = 0
|
|
12
|
+
start: int = 0
|
|
13
|
+
end: int = 0
|
|
14
|
+
startTimeSlot: int = 0
|
|
15
|
+
endTimeSlot: int = 0
|
|
16
|
+
branch: int = 0
|
|
17
|
+
type: str = "unknown"
|
|
18
|
+
groupsInDepartments: list[int] = field(default_factory=list)
|
|
19
|
+
locationsOfBranch: list[int] = field(default_factory=list)
|
|
20
|
+
locations: list[str] = field(default_factory=list)
|
|
21
|
+
optional: bool = False
|
|
22
|
+
valid: bool = False
|
|
23
|
+
cancelled: bool = False
|
|
24
|
+
cancelledReason: str = ""
|
|
25
|
+
modified: bool = False
|
|
26
|
+
teacherChanged: bool = False
|
|
27
|
+
groupChanged: bool = False
|
|
28
|
+
locationChanged: bool = False
|
|
29
|
+
timeChanged: bool = False
|
|
30
|
+
moved: bool = False
|
|
31
|
+
created: int = 0
|
|
32
|
+
hidden: bool = False
|
|
33
|
+
commonSchedule: bool = False
|
|
34
|
+
ignoreSubstitutions: bool = False
|
|
35
|
+
changeDescription: str = ""
|
|
36
|
+
schedulerRemark: str = ""
|
|
37
|
+
capacity: int = 0
|
|
38
|
+
content: str = ""
|
|
39
|
+
lastModified: int = 0
|
|
40
|
+
new: bool = True
|
|
41
|
+
choosableInDepartments: list[int] = field(default_factory=list)
|
|
42
|
+
choosableInDepartmentCodes: list[str] = field(default_factory=list)
|
|
43
|
+
courses: list[int] = field(default_factory=list)
|
|
44
|
+
alternativeSubject: str = ""
|
|
45
|
+
onlineStudents: list[str] = field(default_factory=list)
|
|
46
|
+
extraStudentSource: str = ""
|
|
47
|
+
appointmentLastModified: int = 0
|
|
48
|
+
onlineLocationUrl: str = ""
|
|
49
|
+
remark: str = ""
|
|
50
|
+
capacityManually: bool = False
|
|
51
|
+
teachingTimeManually: bool = False
|
|
52
|
+
teachingTime: int = 0
|
|
53
|
+
expectedStudentCount: int = 0
|
|
54
|
+
expectedStudentCountOnline: int = 0
|
|
55
|
+
availableSpace: int = 0
|
|
56
|
+
udmUUID: str = ""
|
|
57
|
+
creator: str = ""
|
|
58
|
+
subjects: list[str] = field(default_factory=list)
|
|
59
|
+
teachers: list[int] = field(default_factory=list)
|
|
60
|
+
onlineTeachers: list[str] = field(default_factory=list)
|
|
61
|
+
students: list[str] = field(default_factory=list)
|
|
62
|
+
|
|
63
|
+
# doesn't work (not enough rights)
|
|
64
|
+
# @classmethod
|
|
65
|
+
# def get_appointment(cls, id: int):
|
|
66
|
+
# query = f"appointments/{id}"
|
|
67
|
+
# status, data = zermelo.getData(query, from_id=True)
|
|
68
|
+
# if status != 200:
|
|
69
|
+
# raise Exception(data)
|
|
70
|
+
# return from_zermelo_dict(cls, data[0])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_appointments(query: str) -> list[Appointment]:
|
|
74
|
+
status, data = zermelo.getData(query)
|
|
75
|
+
if status != 200:
|
|
76
|
+
raise Exception(data)
|
|
77
|
+
return [from_zermelo_dict(Appointment, row) for row in data]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_user_appointments(user: int | str, **kwargs) -> list[Appointment]:
|
|
81
|
+
query = f"appointments/?user={user}"
|
|
82
|
+
for key, val in kwargs.items():
|
|
83
|
+
query += f"&{key}={val}"
|
|
84
|
+
logger.debug(query)
|
|
85
|
+
return get_appointments(query)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_department_updates(id: int, **kwargs) -> list[Appointment]:
|
|
89
|
+
query = f"appointments/?containsStudentsFromGroupInDepartment={id}"
|
|
90
|
+
for key, val in kwargs.items():
|
|
91
|
+
query += f"&{key}={val}"
|
|
92
|
+
return get_appointments(query)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from .logger import makeLogger
|
|
2
|
+
from .zermelo_api import ZermeloCollection, zermelo, from_zermelo_dict
|
|
3
|
+
from .time_utils import get_date, get_year, datetime
|
|
4
|
+
from .users import Leerling, Leerlingen, Personeel, Medewerker
|
|
5
|
+
from .leerjaren import Leerjaren, Leerjaar
|
|
6
|
+
from .groepen import Groep, Groepen
|
|
7
|
+
from .lesgroepen import Lesgroepen, Lesgroep
|
|
8
|
+
from .vakken import Vakken, Vak
|
|
9
|
+
from dataclasses import dataclass, InitVar, field
|
|
10
|
+
|
|
11
|
+
# branch is roughly translated to 'afdeling' in Dutch
|
|
12
|
+
# for readability kept as branch, might be changed in the future
|
|
13
|
+
|
|
14
|
+
logger = makeLogger("BRANCH")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class SchoolInSchoolYear:
|
|
19
|
+
id: int
|
|
20
|
+
# school: int
|
|
21
|
+
# year: int
|
|
22
|
+
# archived: bool
|
|
23
|
+
# name: str
|
|
24
|
+
# projectName: str
|
|
25
|
+
# schoolName: str
|
|
26
|
+
# schoolHrmsCode: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Branch:
|
|
31
|
+
id: int
|
|
32
|
+
schoolInSchoolYear: int
|
|
33
|
+
branch: str
|
|
34
|
+
name: str
|
|
35
|
+
schoolYear: int
|
|
36
|
+
date: datetime = datetime.now()
|
|
37
|
+
leerlingen: list[Leerling] = field(default_factory=list)
|
|
38
|
+
personeel: list[Medewerker] = field(default_factory=list)
|
|
39
|
+
leerjaren: list[Leerjaar] = field(default_factory=list)
|
|
40
|
+
vakken: Vakken = field(default_factory=list)
|
|
41
|
+
groepen: Groepen = field(default_factory=list)
|
|
42
|
+
|
|
43
|
+
def __post_init__(self):
|
|
44
|
+
logger.info(f"*** loading branch: {self.name} ***")
|
|
45
|
+
self.leerlingen = Leerlingen(self.schoolInSchoolYear)
|
|
46
|
+
self.personeel = Personeel(self.schoolInSchoolYear)
|
|
47
|
+
self.leerjaren = Leerjaren(self.schoolInSchoolYear)
|
|
48
|
+
self.groepen = Groepen(self.schoolInSchoolYear)
|
|
49
|
+
self.vakken = Vakken(self.schoolInSchoolYear)
|
|
50
|
+
|
|
51
|
+
def find_lesgroepen(self) -> Lesgroepen | bool:
|
|
52
|
+
if self.leerlingen and self.personeel:
|
|
53
|
+
return Lesgroepen(
|
|
54
|
+
self.leerjaren,
|
|
55
|
+
self.vakken,
|
|
56
|
+
self.groepen,
|
|
57
|
+
self.leerlingen,
|
|
58
|
+
self.personeel,
|
|
59
|
+
)
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class Branches(ZermeloCollection, list[Branch]):
|
|
65
|
+
datestring: InitVar = ""
|
|
66
|
+
|
|
67
|
+
def __post_init__(self, datestring):
|
|
68
|
+
logger.debug("init branches")
|
|
69
|
+
date = get_date(datestring)
|
|
70
|
+
year = get_year(datestring)
|
|
71
|
+
logger.debug(year)
|
|
72
|
+
query = f"schoolsinschoolyears/?year={year}&archived=False"
|
|
73
|
+
data = zermelo.load_query(query)
|
|
74
|
+
for schoolrow in data:
|
|
75
|
+
school = from_zermelo_dict(SchoolInSchoolYear, schoolrow)
|
|
76
|
+
query = f"branchesofschools/?schoolInSchoolYear={school.id}"
|
|
77
|
+
self.load_collection(query, Branch)
|
|
78
|
+
for branch in self:
|
|
79
|
+
branch.date = date
|
|
80
|
+
|
|
81
|
+
def __str__(self):
|
|
82
|
+
return "Branches(" + ", ".join([br.name for br in self]) + ")"
|
|
83
|
+
|
|
84
|
+
def get(self, name: str) -> Branch:
|
|
85
|
+
logger.info(f"loading branch: {name} ")
|
|
86
|
+
for branch in self:
|
|
87
|
+
if (
|
|
88
|
+
name.lower() in branch.branch.lower()
|
|
89
|
+
or branch.branch.lower() in name.lower()
|
|
90
|
+
):
|
|
91
|
+
return branch
|
|
92
|
+
else:
|
|
93
|
+
logger.error(f"NO Branch found for {name}")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from os import path, mkdir
|
|
2
|
+
from shutil import copyfile
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class getConfig:
|
|
7
|
+
def __init__(self, file):
|
|
8
|
+
HOMEDIR = getConfigDir()
|
|
9
|
+
filename = file + ".json"
|
|
10
|
+
backupname = file + ".bak"
|
|
11
|
+
if not HOMEDIR:
|
|
12
|
+
raise Exception("Kan configuratie directory niet aanmaken")
|
|
13
|
+
self.filepath = path.join(HOMEDIR, filename)
|
|
14
|
+
self.backup = path.join(HOMEDIR, backupname)
|
|
15
|
+
|
|
16
|
+
def __repr__(self):
|
|
17
|
+
return f'ConfigFile(path="{self.filepath}")'
|
|
18
|
+
|
|
19
|
+
def save(self, data):
|
|
20
|
+
try:
|
|
21
|
+
copyfile(self.filepath, self.backup)
|
|
22
|
+
except Exception:
|
|
23
|
+
print("warning - geen datafile")
|
|
24
|
+
try:
|
|
25
|
+
with open(self.filepath, "w") as file:
|
|
26
|
+
json.dump(data, file, indent=2)
|
|
27
|
+
except Exception:
|
|
28
|
+
return False
|
|
29
|
+
return True
|
|
30
|
+
|
|
31
|
+
def load(self):
|
|
32
|
+
data = {}
|
|
33
|
+
for file in [self.filepath, self.backup]:
|
|
34
|
+
try:
|
|
35
|
+
if path.isfile(file):
|
|
36
|
+
with open(file, "r") as json_file:
|
|
37
|
+
data = json.load(json_file)
|
|
38
|
+
except Exception:
|
|
39
|
+
continue
|
|
40
|
+
return data
|
|
41
|
+
|
|
42
|
+
def get(self, key: str = None):
|
|
43
|
+
data = self.load()
|
|
44
|
+
if not key:
|
|
45
|
+
return data
|
|
46
|
+
return data[key] if key in data else {}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def getConfigDir():
|
|
50
|
+
abspath = path.abspath(__file__)
|
|
51
|
+
dname = path.dirname(abspath)
|
|
52
|
+
return dname
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .config import getConfig
|
|
2
|
+
from .logger import makeLogger, INFO, DEBUG
|
|
3
|
+
|
|
4
|
+
logger = makeLogger("creds", INFO)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Credentials:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.file = getConfig("creds")
|
|
10
|
+
self.token = ""
|
|
11
|
+
self.load()
|
|
12
|
+
|
|
13
|
+
def load(self):
|
|
14
|
+
self.token = ""
|
|
15
|
+
try:
|
|
16
|
+
data = self.file.load()
|
|
17
|
+
self.token = data["token"]
|
|
18
|
+
except Exception as e:
|
|
19
|
+
logger.trace()
|
|
20
|
+
finally:
|
|
21
|
+
logger.debug(f"token: {self.token}")
|
|
22
|
+
return True if self.token else False
|
|
23
|
+
|
|
24
|
+
def settoken(self, token):
|
|
25
|
+
self.file.save({"token": token})
|
|
26
|
+
self.token = token
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from .zermelo_api import ZermeloCollection, zermelo
|
|
2
|
+
from .logger import makeLogger
|
|
3
|
+
from dataclasses import dataclass, InitVar
|
|
4
|
+
|
|
5
|
+
logger = makeLogger("GROEPEN")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Groep:
|
|
10
|
+
id: int
|
|
11
|
+
isMainGroup: bool
|
|
12
|
+
isMentorGroup: bool
|
|
13
|
+
departmentOfBranch: int
|
|
14
|
+
name: str
|
|
15
|
+
extendedName: str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Groepen(ZermeloCollection, list[Groep]):
|
|
20
|
+
schoolinschoolyear: InitVar
|
|
21
|
+
|
|
22
|
+
def __post_init__(self, schoolinschoolyear: int):
|
|
23
|
+
query = f"groupindepartments?schoolInSchoolYear={schoolinschoolyear}"
|
|
24
|
+
self.load_collection(query, Groep)
|
|
25
|
+
|
|
26
|
+
def get_department_groups(
|
|
27
|
+
self, departmentOfBranch: int, maingroup: bool = False
|
|
28
|
+
) -> list[Groep]:
|
|
29
|
+
# returns all groups (if it is/isnt stamklas (maingroup))
|
|
30
|
+
return [
|
|
31
|
+
groep
|
|
32
|
+
for groep in self
|
|
33
|
+
if groep.departmentOfBranch == departmentOfBranch
|
|
34
|
+
and groep.isMainGroup == maingroup
|
|
35
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from .zermelo_api import ZermeloCollection
|
|
2
|
+
from .logger import makeLogger
|
|
3
|
+
from dataclasses import dataclass, InitVar
|
|
4
|
+
|
|
5
|
+
# Leerjaar is a rough dutch translation of department in Zermelo
|
|
6
|
+
|
|
7
|
+
logger = makeLogger("Leerjaren")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def getLeerjaarNaam(string):
|
|
11
|
+
names = {"LG1": "klas 1", "LG2": "klas 2", "LG3": "klas 3"}
|
|
12
|
+
for name, result in names.items():
|
|
13
|
+
if name in string:
|
|
14
|
+
return result
|
|
15
|
+
else:
|
|
16
|
+
return string
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Leerjaar:
|
|
21
|
+
id: int
|
|
22
|
+
code: str
|
|
23
|
+
yearOfEducation: int
|
|
24
|
+
branchOfSchool: int
|
|
25
|
+
teacherTeams: list[int]
|
|
26
|
+
mainGroupAuthority: str
|
|
27
|
+
educationType: str
|
|
28
|
+
weekTimeTable: int
|
|
29
|
+
teachingLevel: str
|
|
30
|
+
educations: list[int]
|
|
31
|
+
schoolInSchoolYear: int
|
|
32
|
+
branchOfSchoolCode: str
|
|
33
|
+
schoolInSchoolYearName: str
|
|
34
|
+
profiles: list[int]
|
|
35
|
+
schoolInSchoolYearId: int
|
|
36
|
+
name: str = ""
|
|
37
|
+
|
|
38
|
+
def __post_init__(self):
|
|
39
|
+
self.name = getLeerjaarNaam(self.code.upper())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Leerjaren(ZermeloCollection, list[Leerjaar]):
|
|
44
|
+
schoolinschoolyear: InitVar
|
|
45
|
+
|
|
46
|
+
def __post_init__(self, schoolinschoolyear: int):
|
|
47
|
+
query = f"departmentsofbranches?schoolInSchoolYear={schoolinschoolyear}"
|
|
48
|
+
self.load_collection(query, Leerjaar)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
from dataclasses import dataclass, field, InitVar
|
|
2
|
+
from .vakken import Vakken, Vak
|
|
3
|
+
from .groepen import Groepen, Groep
|
|
4
|
+
from .users import Leerlingen, Leerling, Personeel, Medewerker
|
|
5
|
+
from .leerjaren import Leerjaren, Leerjaar
|
|
6
|
+
from .time_utils import get_date, delta_week
|
|
7
|
+
from .logger import makeLogger, DEBUG
|
|
8
|
+
from .zermelo_api import zermelo, from_zermelo_dict
|
|
9
|
+
|
|
10
|
+
# from typing import Tuple
|
|
11
|
+
|
|
12
|
+
logger = makeLogger("LESGROEP")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def createLesgroepNaam(vak: Vak, groep: Groep) -> str:
|
|
16
|
+
leerjaar, groepnaam = groep.extendedName.split(".")
|
|
17
|
+
jaarnaam = leerjaar[2:].upper()
|
|
18
|
+
if vak.subjectCode in groepnaam:
|
|
19
|
+
return f"{jaarnaam}.{groepnaam}"
|
|
20
|
+
else:
|
|
21
|
+
return f"{jaarnaam}.{vak.subjectCode}{groepnaam[-1]}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def find_groepen(vak: Vak, groepen: Groepen) -> list[Groep]:
|
|
25
|
+
result = []
|
|
26
|
+
logger.debug(f"finding groep for vak: {vak.subjectCode}")
|
|
27
|
+
for groep in groepen.get_department_groups(vak.departmentOfBranch):
|
|
28
|
+
if groep in result:
|
|
29
|
+
continue
|
|
30
|
+
if vak.qualifiedCode and vak.qualifiedCode in groep.extendedName:
|
|
31
|
+
logger.debug(
|
|
32
|
+
f"found {groep.name} with {vak.qualifiedCode} in {groep.extendedName}"
|
|
33
|
+
)
|
|
34
|
+
result.append(groep)
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def clean_docs(docs: list[str]) -> list[str]:
|
|
39
|
+
checklist = list(set(docs))
|
|
40
|
+
if "lgverv" in checklist:
|
|
41
|
+
checklist.remove("lgverv")
|
|
42
|
+
max = 0
|
|
43
|
+
if len(checklist) > 1:
|
|
44
|
+
logger.warning(f"multiple docs: {checklist}")
|
|
45
|
+
for doc in checklist:
|
|
46
|
+
doc_count = docs.count(doc)
|
|
47
|
+
if doc_count > max:
|
|
48
|
+
result = doc
|
|
49
|
+
max = doc_count
|
|
50
|
+
logger.warning(f"result: {result} ({max})")
|
|
51
|
+
return [result]
|
|
52
|
+
return checklist
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Les:
|
|
57
|
+
id: int
|
|
58
|
+
appointmentInstance: int
|
|
59
|
+
teachers: list[str]
|
|
60
|
+
students: list[str]
|
|
61
|
+
subjects: list[str]
|
|
62
|
+
groups: list[str]
|
|
63
|
+
groupsInDepartments: list[int]
|
|
64
|
+
choosableInDepartmentCodes: list[str]
|
|
65
|
+
valid: bool
|
|
66
|
+
cancelled: bool
|
|
67
|
+
|
|
68
|
+
def filter(self, name: str) -> bool:
|
|
69
|
+
if self.cancelled:
|
|
70
|
+
return False
|
|
71
|
+
if not self.valid:
|
|
72
|
+
return False
|
|
73
|
+
if len(self.students) > 40:
|
|
74
|
+
logger.debug("groep te groot")
|
|
75
|
+
return False
|
|
76
|
+
if not any([name.split(".")[-1] in group for group in self.groups]):
|
|
77
|
+
logger.debug(f"{name} not in {self}")
|
|
78
|
+
return False
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_vak_data(
|
|
83
|
+
id: int, code: str, groupName: str, start, eind
|
|
84
|
+
) -> tuple[list[int], list[str], list[str]]:
|
|
85
|
+
query = f"appointments/?containsStudentsFromGroupInDepartment={id}&subjects={code}&type=lesson&start={start}&end={eind}&fields=appointmentInstance,id,teachers,students,subjects,groups,groupsInDepartments,choosableInDepartmentCodes,valid,cancelled"
|
|
86
|
+
vakdata = zermelo.load_query(query)
|
|
87
|
+
grp_bck = []
|
|
88
|
+
ll_bck = []
|
|
89
|
+
doc_bck = []
|
|
90
|
+
leerlingen = []
|
|
91
|
+
docenten = []
|
|
92
|
+
grp_namen = []
|
|
93
|
+
lessen = [Les(**row) for row in reversed(vakdata) if row]
|
|
94
|
+
for les in [les for les in lessen if les.filter(groupName)]:
|
|
95
|
+
if len(les.groups) > 1:
|
|
96
|
+
if not grp_namen and not grp_bck or len(les.groups) < len(grp_bck):
|
|
97
|
+
logger.debug("meerdere groepen")
|
|
98
|
+
grp_bck = les.choosableInDepartmentCodes
|
|
99
|
+
ll_bck = list(set([llnr for llnr in les.students]))
|
|
100
|
+
doc_bck = list(set([doc for doc in les.teachers]))
|
|
101
|
+
continue
|
|
102
|
+
if les.students and les.teachers:
|
|
103
|
+
[leerlingen.append(llnr) for llnr in les.students if llnr not in leerlingen]
|
|
104
|
+
[docenten.append(doc) for doc in les.teachers]
|
|
105
|
+
[
|
|
106
|
+
grp_namen.append(grp)
|
|
107
|
+
for grp in les.choosableInDepartmentCodes
|
|
108
|
+
if grp not in grp_namen
|
|
109
|
+
]
|
|
110
|
+
if not grp_namen and grp_bck:
|
|
111
|
+
logger.debug(f"result groepen: {grp_bck}")
|
|
112
|
+
grp_namen = grp_bck
|
|
113
|
+
if not docenten and doc_bck:
|
|
114
|
+
logger.debug(f"result docenten: {doc_bck}")
|
|
115
|
+
docenten = doc_bck
|
|
116
|
+
if not leerlingen and ll_bck:
|
|
117
|
+
logger.debug(f"result leerlingen: {ll_bck}")
|
|
118
|
+
leerlingen = ll_bck
|
|
119
|
+
docenten = clean_docs(docenten)
|
|
120
|
+
leerlingen = [int(llnr) for llnr in leerlingen]
|
|
121
|
+
return (leerlingen, docenten, grp_namen)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def find_deelnemers(
|
|
125
|
+
vak: Vak, groep: Groep
|
|
126
|
+
) -> tuple[list[int], list[str], list[str]] | bool:
|
|
127
|
+
date = get_date()
|
|
128
|
+
try:
|
|
129
|
+
logger.debug(groep)
|
|
130
|
+
for x in [0, -1, -2, 1, 2, 3]:
|
|
131
|
+
dweek = x * 4
|
|
132
|
+
starttijd = int(delta_week(date, dweek).timestamp())
|
|
133
|
+
eindtijd = int(delta_week(date, dweek + 4).timestamp())
|
|
134
|
+
data = get_vak_data(
|
|
135
|
+
groep.id, vak.subjectCode, groep.extendedName, starttijd, eindtijd
|
|
136
|
+
)
|
|
137
|
+
leerlingen, docenten, groep_namen = data
|
|
138
|
+
if len(leerlingen) and len(docenten):
|
|
139
|
+
logger.debug(f"found for {groep}")
|
|
140
|
+
namen = [
|
|
141
|
+
groepnaam
|
|
142
|
+
for groepnaam in groep_namen
|
|
143
|
+
if vak.departmentOfBranchCode in groepnaam
|
|
144
|
+
]
|
|
145
|
+
return (leerlingen, docenten, namen)
|
|
146
|
+
if not len(leerlingen) or not len(docenten):
|
|
147
|
+
logger.debug(f"geen deelnemers gevonden voor {groep}\n {vak}")
|
|
148
|
+
return False
|
|
149
|
+
except Exception:
|
|
150
|
+
logger.trace()
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_info(
|
|
155
|
+
llnrs: list[int],
|
|
156
|
+
doc_codes: list[str],
|
|
157
|
+
names: list[str],
|
|
158
|
+
ll: Leerlingen,
|
|
159
|
+
docs: Personeel,
|
|
160
|
+
) -> tuple[list[Leerling], list[Medewerker], list[str]]:
|
|
161
|
+
leerlingen = [ll.get(llnr) for llnr in llnrs]
|
|
162
|
+
docenten = [docs.get(code) for code in doc_codes]
|
|
163
|
+
return (leerlingen, docenten, names)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class Lesgroep:
|
|
168
|
+
vak: Vak
|
|
169
|
+
groep: Groep
|
|
170
|
+
leerjaar: Leerjaar
|
|
171
|
+
leerlingen: list[Leerling] = field(default_factory=list)
|
|
172
|
+
docenten: list[Medewerker] = field(default_factory=list)
|
|
173
|
+
namen: list[str] = field(default_factory=list)
|
|
174
|
+
naam: str = ""
|
|
175
|
+
|
|
176
|
+
def __post_init__(self):
|
|
177
|
+
self.naam = createLesgroepNaam(self.vak, self.groep)
|
|
178
|
+
for leerling in self.leerlingen:
|
|
179
|
+
leerling.leerjaren.add(self.leerjaar.id)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass
|
|
183
|
+
class Lesgroepen(list[Lesgroep]):
|
|
184
|
+
leerjaren: InitVar
|
|
185
|
+
vakken: InitVar
|
|
186
|
+
groepen: InitVar
|
|
187
|
+
leerlingen: InitVar
|
|
188
|
+
personeel: InitVar
|
|
189
|
+
|
|
190
|
+
def __post_init__(
|
|
191
|
+
self,
|
|
192
|
+
leerjaren: Leerjaren,
|
|
193
|
+
vakken: Vakken,
|
|
194
|
+
groepen: Groepen,
|
|
195
|
+
leerlingen: Leerlingen,
|
|
196
|
+
personeel: Personeel,
|
|
197
|
+
):
|
|
198
|
+
for leerjaar in leerjaren:
|
|
199
|
+
for vak in vakken.get_leerjaar_vakken(leerjaar.id):
|
|
200
|
+
if vak.subjectType in ["education", "profile"] or vak.scheduleCode in [
|
|
201
|
+
"lo",
|
|
202
|
+
"sport",
|
|
203
|
+
]:
|
|
204
|
+
# skip educatio / profile / lo
|
|
205
|
+
continue
|
|
206
|
+
logger.debug(vak)
|
|
207
|
+
found = False
|
|
208
|
+
for groep in find_groepen(vak, groepen):
|
|
209
|
+
groepinfo = find_deelnemers(vak, groep)
|
|
210
|
+
if groepinfo:
|
|
211
|
+
data = get_info(*groepinfo, leerlingen, personeel)
|
|
212
|
+
self.append(Lesgroep(vak, groep, leerjaar, *data))
|
|
213
|
+
found = True
|
|
214
|
+
if found:
|
|
215
|
+
continue
|
|
216
|
+
logger.debug(f"trying maingroups for {vak.subjectName}")
|
|
217
|
+
for groep in groepen.get_department_groups(
|
|
218
|
+
vak.departmentOfBranch, True
|
|
219
|
+
):
|
|
220
|
+
logger.debug(f"trying: {groep}")
|
|
221
|
+
groepinfo = find_deelnemers(vak, groep)
|
|
222
|
+
if groepinfo:
|
|
223
|
+
data = get_info(*groepinfo, leerlingen, personeel)
|
|
224
|
+
self.append(Lesgroep(vak, groep, leerjaar, *data))
|
|
225
|
+
found = True
|
|
226
|
+
if not found:
|
|
227
|
+
logger.warning(f"geen groepen gevonden voor {vak}")
|
|
228
|
+
self.clean_leerlingen()
|
|
229
|
+
logger.info(f"found {len(self)} lesgroepen")
|
|
230
|
+
|
|
231
|
+
def clean_leerlingen(self):
|
|
232
|
+
for lesgroep in self:
|
|
233
|
+
for leerling in lesgroep.leerlingen.copy():
|
|
234
|
+
if leerling.leerjaren.get_id() != lesgroep.leerjaar.id:
|
|
235
|
+
logger.warning(
|
|
236
|
+
f"removing leerling ({leerling.fullName}) from {lesgroep.naam}"
|
|
237
|
+
)
|
|
238
|
+
lesgroep.leerlingen.remove(leerling)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import logging.handlers
|
|
3
|
+
from logging import Logger
|
|
4
|
+
from logging import DEBUG, INFO, WARNING, ERROR
|
|
5
|
+
from os import path, mkdir
|
|
6
|
+
from traceback import format_exc
|
|
7
|
+
|
|
8
|
+
FOLDER = "log"
|
|
9
|
+
FILE = "info"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MyLogger(Logger):
|
|
13
|
+
def trace(self):
|
|
14
|
+
self.error(format_exc())
|
|
15
|
+
|
|
16
|
+
def header(self, text: str):
|
|
17
|
+
self.info(f" ###### {text.upper()} ######")
|
|
18
|
+
|
|
19
|
+
def write(self, text: str):
|
|
20
|
+
text.replace("\n", "")
|
|
21
|
+
self.debug(text)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def makeLogger(name: str = None, LOG_LEVEL: int = INFO, filename=FILE) -> MyLogger:
|
|
25
|
+
logger = logging.getLogger(name)
|
|
26
|
+
logger.setLevel(LOG_LEVEL)
|
|
27
|
+
# if not path.isdir(FOLDER):
|
|
28
|
+
# mkdir(FOLDER)
|
|
29
|
+
# filename = path.join(FOLDER, f"{filename}.log")
|
|
30
|
+
|
|
31
|
+
# create console handler and set level to debug
|
|
32
|
+
# handler = logging.handlers.TimedRotatingFileHandler(
|
|
33
|
+
# filename, when="W4", backupCount=3, encoding="utf-8"
|
|
34
|
+
# )
|
|
35
|
+
handler = logging.StreamHandler()
|
|
36
|
+
handler.setLevel(LOG_LEVEL)
|
|
37
|
+
|
|
38
|
+
formatter = logging.Formatter(
|
|
39
|
+
"%(asctime)s - %(levelname)s - %(name)s - %(message)s"
|
|
40
|
+
)
|
|
41
|
+
handler.setFormatter(formatter)
|
|
42
|
+
|
|
43
|
+
logger.addHandler(handler)
|
|
44
|
+
|
|
45
|
+
logger.__class__ = MyLogger
|
|
46
|
+
|
|
47
|
+
return logger
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_date(datestring: str = "") -> datetime:
|
|
5
|
+
try:
|
|
6
|
+
date = datetime.strptime(datestring, "%Y-%m-%d")
|
|
7
|
+
except:
|
|
8
|
+
date = datetime.today()
|
|
9
|
+
finally:
|
|
10
|
+
return date
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def delta_week(date: datetime, delta: int = 0) -> datetime:
|
|
14
|
+
return date + timedelta(weeks=delta)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_year(datestring: str = "") -> int:
|
|
18
|
+
return delta_week(get_date(datestring), -33).strftime("%Y")
|
zermelo_api/src/users.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from .zermelo_api import ZermeloCollection, zermelo, from_zermelo_dict
|
|
2
|
+
from .logger import makeLogger
|
|
3
|
+
from dataclasses import dataclass, InitVar, field
|
|
4
|
+
|
|
5
|
+
logger = makeLogger("USERS")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class User:
|
|
10
|
+
code: str
|
|
11
|
+
roles: list[str]
|
|
12
|
+
firstName: str
|
|
13
|
+
prefix: str
|
|
14
|
+
lastName: str
|
|
15
|
+
schoolInSchoolYears: list[int]
|
|
16
|
+
isApplicationManager: bool
|
|
17
|
+
archived: bool
|
|
18
|
+
isStudent: bool
|
|
19
|
+
isEmployee: bool
|
|
20
|
+
isFamilyMember: bool
|
|
21
|
+
hasPassword: bool
|
|
22
|
+
isSchoolScheduler: bool
|
|
23
|
+
isSchoolLeader: bool
|
|
24
|
+
isStudentAdministrator: bool
|
|
25
|
+
isTeamLeader: bool
|
|
26
|
+
isSectionLeader: bool
|
|
27
|
+
isMentor: bool
|
|
28
|
+
isParentTeacherNightScheduler: bool
|
|
29
|
+
isDean: bool
|
|
30
|
+
fullName: str = ""
|
|
31
|
+
|
|
32
|
+
def __post_init__(self):
|
|
33
|
+
self.fullName = self.generatename()
|
|
34
|
+
|
|
35
|
+
def generatename(self) -> str:
|
|
36
|
+
if self.prefix:
|
|
37
|
+
fullname = " ".join(
|
|
38
|
+
[self.firstName, self.prefix, self.lastName.split(",")[0]]
|
|
39
|
+
)
|
|
40
|
+
elif self.firstName is not None and self.lastName is not None:
|
|
41
|
+
fullname = " ".join([self.firstName, self.lastName])
|
|
42
|
+
elif self.lastName is not None:
|
|
43
|
+
fullname = self.lastName
|
|
44
|
+
else:
|
|
45
|
+
fullname = "unknown"
|
|
46
|
+
return fullname
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Users(list[User]):
|
|
50
|
+
def print_list(self):
|
|
51
|
+
return (
|
|
52
|
+
f" = [" + ", ".join([str(user) for user in self]) + "]" if len(self) else ""
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class LeerjaarCounter:
|
|
58
|
+
id: int
|
|
59
|
+
count: int = 0
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LeerjaarCounters(list[LeerjaarCounter]):
|
|
63
|
+
def get(self, id) -> LeerjaarCounter:
|
|
64
|
+
for ljcounter in self:
|
|
65
|
+
if ljcounter.id == id:
|
|
66
|
+
return ljcounter
|
|
67
|
+
self.append(LeerjaarCounter(id))
|
|
68
|
+
return self[-1]
|
|
69
|
+
|
|
70
|
+
def add(self, leerjaar_id: int):
|
|
71
|
+
counter = self.get(leerjaar_id)
|
|
72
|
+
counter.count += 1
|
|
73
|
+
|
|
74
|
+
def get_id(self) -> int:
|
|
75
|
+
if len(self):
|
|
76
|
+
self.sort(key=lambda x: x.count, reverse=True)
|
|
77
|
+
return self[0].id
|
|
78
|
+
return 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class Leerling(User):
|
|
83
|
+
volgnr: int = 0
|
|
84
|
+
leerjaren: LeerjaarCounters = field(default_factory=LeerjaarCounters)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class Leerlingen(ZermeloCollection, Users, list[Leerling]):
|
|
89
|
+
schoolinschoolyear: InitVar
|
|
90
|
+
|
|
91
|
+
def __init__(self, schoolinschoolyear: int):
|
|
92
|
+
query = f"users?schoolInSchoolYear={schoolinschoolyear}&isStudent=true"
|
|
93
|
+
data = zermelo.load_query(query)
|
|
94
|
+
if data:
|
|
95
|
+
self.load_leerlingen(data)
|
|
96
|
+
|
|
97
|
+
def load_leerlingen(self, data: list[dict]):
|
|
98
|
+
logger.info("loading Leerlingen")
|
|
99
|
+
data.sort(key=lambda x: (x["lastName"], x["firstName"]))
|
|
100
|
+
for idx, row in enumerate(data):
|
|
101
|
+
self.append(from_zermelo_dict(Leerling, row, volgnr=idx + 1))
|
|
102
|
+
logger.info(f"found: {len(self)} leerlingen")
|
|
103
|
+
|
|
104
|
+
def __repr__(self):
|
|
105
|
+
return f"{self}{self.print_list()}"
|
|
106
|
+
|
|
107
|
+
def __str__(self):
|
|
108
|
+
return f"Leerlingen({len(self)})"
|
|
109
|
+
|
|
110
|
+
def get(self, llnr) -> Leerling:
|
|
111
|
+
for user in self:
|
|
112
|
+
if user.code == str(llnr):
|
|
113
|
+
return user
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
class Medewerker(User):
|
|
118
|
+
def __repr__(self):
|
|
119
|
+
return self.fullName + f"({self.code})"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass
|
|
123
|
+
class Personeel(ZermeloCollection, Users, list[Medewerker]):
|
|
124
|
+
schoolinschoolyear: InitVar
|
|
125
|
+
|
|
126
|
+
def __init__(self, schoolinschoolyear: int):
|
|
127
|
+
query = f"users?schoolInSchoolYear={schoolinschoolyear}&isEmployee=true"
|
|
128
|
+
self.load_collection(query, Medewerker)
|
|
129
|
+
|
|
130
|
+
def __repr__(self):
|
|
131
|
+
return f"{self}{self.print_list()}"
|
|
132
|
+
|
|
133
|
+
def __str__(self):
|
|
134
|
+
return f"Personeel({len(self)})"
|
|
135
|
+
|
|
136
|
+
def get(self, code: str):
|
|
137
|
+
for user in self:
|
|
138
|
+
if user.code == code:
|
|
139
|
+
return user
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from .zermelo_api import ZermeloCollection, zermelo
|
|
2
|
+
from .logger import makeLogger, DEBUG
|
|
3
|
+
from .groepen import Groepen, Groep
|
|
4
|
+
from dataclasses import dataclass, InitVar, field
|
|
5
|
+
|
|
6
|
+
logger = makeLogger("VAKKEN")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Vak:
|
|
11
|
+
id: int
|
|
12
|
+
subject: int
|
|
13
|
+
departmentOfBranch: int
|
|
14
|
+
studentCanEdit: bool
|
|
15
|
+
sectionOfBranch: int
|
|
16
|
+
courseType: str
|
|
17
|
+
lessonHoursInClassPeriods: list[dict]
|
|
18
|
+
excludedSegments: list[int]
|
|
19
|
+
referenceWeek: dict # (year:int, weekNumber: int, schoolYear: int)
|
|
20
|
+
isExam: bool
|
|
21
|
+
scheduleCode: str
|
|
22
|
+
subjectType: str
|
|
23
|
+
subjectCode: str
|
|
24
|
+
departmentOfBranchCode: str
|
|
25
|
+
iltCode: int
|
|
26
|
+
qualifiedCode: str
|
|
27
|
+
subjectScheduleCode: str
|
|
28
|
+
subjectName: str
|
|
29
|
+
sectionOfBranchAbbreviation: str
|
|
30
|
+
|
|
31
|
+
def getName(self) -> str:
|
|
32
|
+
if "/" in self.subjectName:
|
|
33
|
+
logger.debug(f"old name: {self.subjectName}")
|
|
34
|
+
parts = self.subjectName.split("/")
|
|
35
|
+
frontpart = parts[0]
|
|
36
|
+
nameparts = frontpart.split(" ")
|
|
37
|
+
nameparts.pop(-1)
|
|
38
|
+
name = " ".join(nameparts)
|
|
39
|
+
logger.debug(f"new name: {name}")
|
|
40
|
+
return name.strip()
|
|
41
|
+
return self.subjectName.strip()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class Vakken(ZermeloCollection, list[Vak]):
|
|
46
|
+
schoolinschoolyear: InitVar
|
|
47
|
+
|
|
48
|
+
def __post_init__(self, schoolinschoolyear: int):
|
|
49
|
+
query = f"choosableindepartments?schoolInSchoolYear={schoolinschoolyear}"
|
|
50
|
+
self.load_collection(query, Vak)
|
|
51
|
+
|
|
52
|
+
def get_leerjaar_vakken(self, leerjaar_id: int) -> list[Vak]:
|
|
53
|
+
return [vak for vak in self if vak.departmentOfBranch == leerjaar_id]
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from .credentials import Credentials
|
|
2
|
+
from .logger import makeLogger, DEBUG, INFO
|
|
3
|
+
import json
|
|
4
|
+
import requests
|
|
5
|
+
import inspect
|
|
6
|
+
|
|
7
|
+
logger = makeLogger("ZermeloAPI")
|
|
8
|
+
|
|
9
|
+
ZERMELO_NAME = "carmelhengelo"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ZermeloAPI:
|
|
13
|
+
def __init__(self, school=ZERMELO_NAME):
|
|
14
|
+
self.credentials = Credentials()
|
|
15
|
+
self.zerurl = f"https://{school}.zportal.nl/api/v3/"
|
|
16
|
+
|
|
17
|
+
def login(self, code: str) -> bool:
|
|
18
|
+
token = self.get_access_token(code)
|
|
19
|
+
return self.add_token(token)
|
|
20
|
+
|
|
21
|
+
def get_access_token(self, code: str) -> str:
|
|
22
|
+
token = ""
|
|
23
|
+
url = self.zerurl + "oauth/token"
|
|
24
|
+
# headers = {"Content-Type": "application/json"}
|
|
25
|
+
zerrequest = requests.post(
|
|
26
|
+
url, data={"grant_type": "authorization_code", "code": code}
|
|
27
|
+
)
|
|
28
|
+
if zerrequest.status_code == 200:
|
|
29
|
+
data = json.loads(zerrequest.text)
|
|
30
|
+
if "access_token" in data:
|
|
31
|
+
token = data["access_token"]
|
|
32
|
+
return token
|
|
33
|
+
|
|
34
|
+
def add_token(self, token: str) -> bool:
|
|
35
|
+
if not token:
|
|
36
|
+
return False
|
|
37
|
+
self.credentials.settoken(token)
|
|
38
|
+
return self.checkCreds()
|
|
39
|
+
|
|
40
|
+
def checkCreds(self):
|
|
41
|
+
result = False
|
|
42
|
+
try:
|
|
43
|
+
self.getName()
|
|
44
|
+
result = True
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.trace()
|
|
47
|
+
logger.error(e)
|
|
48
|
+
finally:
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
def getName(self):
|
|
52
|
+
if not self.credentials.token:
|
|
53
|
+
raise Exception("No Token loaded!")
|
|
54
|
+
status, data = self.getData("users/~me", True)
|
|
55
|
+
if status != 200 or not len(data):
|
|
56
|
+
raise Exception("could not load user data with token")
|
|
57
|
+
logger.debug(f"get name: {data[0]}")
|
|
58
|
+
row = data[0]
|
|
59
|
+
if not row["prefix"]:
|
|
60
|
+
return " ".join([row["firstName"], row["lastName"]])
|
|
61
|
+
else:
|
|
62
|
+
return " ".join([row["firstName"], row["prefix"], row["lastName"]])
|
|
63
|
+
|
|
64
|
+
def getData(self, task, from_id=False) -> tuple[int, list[dict] | str]:
|
|
65
|
+
result = (500, [])
|
|
66
|
+
try:
|
|
67
|
+
request = (
|
|
68
|
+
self.zerurl + task + f"?access_token={self.credentials.token}"
|
|
69
|
+
if from_id
|
|
70
|
+
else self.zerurl + task + f"&access_token={self.credentials.token}"
|
|
71
|
+
)
|
|
72
|
+
logger.debug(request)
|
|
73
|
+
json_response = requests.get(request).json()
|
|
74
|
+
if json_response:
|
|
75
|
+
json_status = json_response["response"]["status"]
|
|
76
|
+
if json_status == 200:
|
|
77
|
+
result = (200, json_response["response"]["data"])
|
|
78
|
+
logger.debug(" **** JSON OK ****")
|
|
79
|
+
else:
|
|
80
|
+
logger.debug(
|
|
81
|
+
f"oeps, geen juiste response: {task} - {json_response['response']}"
|
|
82
|
+
)
|
|
83
|
+
result = (json_status, json_response["response"])
|
|
84
|
+
else:
|
|
85
|
+
logger.error("JSON - response is leeg")
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.trace()
|
|
88
|
+
finally:
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
def load_query(self, query: str) -> list[dict]:
|
|
92
|
+
try:
|
|
93
|
+
status, data = self.getData(query)
|
|
94
|
+
if status != 200:
|
|
95
|
+
raise Exception(f"Error loading data {status}")
|
|
96
|
+
if not data:
|
|
97
|
+
logger.debug("no data")
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.debug(e)
|
|
100
|
+
data = []
|
|
101
|
+
return data
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
zermelo = ZermeloAPI()
|
|
105
|
+
|
|
106
|
+
if not zermelo.checkCreds():
|
|
107
|
+
with open("creds.ini") as f:
|
|
108
|
+
token = f.read()
|
|
109
|
+
zermelo.add_token(token)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def from_zermelo_dict(cls, data: dict, *args, **kwargs):
|
|
113
|
+
[
|
|
114
|
+
logger.debug(f"{k} ({v}) not defined in {cls}")
|
|
115
|
+
for k, v in data.items()
|
|
116
|
+
if k not in inspect.signature(cls).parameters
|
|
117
|
+
]
|
|
118
|
+
return cls(
|
|
119
|
+
*args,
|
|
120
|
+
**{k: v for k, v in data.items() if k in inspect.signature(cls).parameters},
|
|
121
|
+
**kwargs,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ZermeloCollection:
|
|
126
|
+
def load_collection(self: list, query: str, type: object) -> None:
|
|
127
|
+
data = zermelo.load_query(query)
|
|
128
|
+
for row in data:
|
|
129
|
+
self.append(from_zermelo_dict(type, row))
|
|
130
|
+
|
|
131
|
+
def test(self: list, query: str):
|
|
132
|
+
data = zermelo.load_query(query)
|
|
133
|
+
for row in data:
|
|
134
|
+
logger.info(f"test: {row}")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Klaas Vogel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: zermelo-api-vogk
|
|
3
|
+
Version: 0.2.16
|
|
4
|
+
Summary: A small module to create a Zermelo accesstoken and put some data from Zermelo in dataclasses
|
|
5
|
+
Home-page: https://github.com/KlaasVogel/zermelo_api_vogk
|
|
6
|
+
Author: Klaas Vogel
|
|
7
|
+
Author-email: zermelo_api@klaasvogel.nl
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
|
|
18
|
+
## zermelo_api_vogk
|
|
19
|
+
A small module to create a Zermelo accesstoken and put some data from Zermelo in dataclasses
|
|
20
|
+
|
|
21
|
+
# V0.2.16
|
|
22
|
+
- found bug when running as daemon, probably: logger.
|
|
23
|
+
- try without output to file
|
|
24
|
+
|
|
25
|
+
# V0.2.15
|
|
26
|
+
- disabled debug mode
|
|
27
|
+
|
|
28
|
+
# V0.2.14
|
|
29
|
+
- debug on for bugfixing
|
|
30
|
+
- added extra debug log for filter in lesgroepen
|
|
31
|
+
- edit filter to find second part of groupname in les.groups
|
|
32
|
+
|
|
33
|
+
# V0.2.13
|
|
34
|
+
- New bug: loading of stamgroepen as lesgroep is broken!
|
|
35
|
+
- need to update/edit filter in lesgroepen
|
|
36
|
+
|
|
37
|
+
# V0.2.12
|
|
38
|
+
- removed debug for wisa and lesgroepen
|
|
39
|
+
|
|
40
|
+
# V0.2.11
|
|
41
|
+
- added groep.extendentName to filter in lesgroep
|
|
42
|
+
|
|
43
|
+
# V0.2.10
|
|
44
|
+
- trying to optimize lesgroepen
|
|
45
|
+
- added (temp) debug to lesgroepen
|
|
46
|
+
- log group
|
|
47
|
+
|
|
48
|
+
# V0.2.9
|
|
49
|
+
- removed dependency of Groepen in Vakken (ot used)
|
|
50
|
+
- added getName to Vak (Vakken)
|
|
51
|
+
- start cleanup code
|
|
52
|
+
|
|
53
|
+
# V0.2.8
|
|
54
|
+
- changed definition of Vakken and Groepen in Branch
|
|
55
|
+
- added Vakken & Vak to __init__.py
|
|
56
|
+
|
|
57
|
+
# V0.2.7
|
|
58
|
+
- added response as data in getData on wrong output
|
|
59
|
+
- refactoring of Appointments
|
|
60
|
+
* added function: get_department_updates [doesn't work atm :( ]
|
|
61
|
+
|
|
62
|
+
# V0.2.6
|
|
63
|
+
- removed linebreaks from logger.write()
|
|
64
|
+
|
|
65
|
+
# V0.2.5
|
|
66
|
+
- trying getting extra data for each appointment from user
|
|
67
|
+
- changed "with_id" in get_data to "from_id" and reversed functionality for better readability
|
|
68
|
+
- added classmethod to Appointment to get Appointment direct from id
|
|
69
|
+
|
|
70
|
+
# V0.2.4
|
|
71
|
+
- added getting appointments
|
|
72
|
+
|
|
73
|
+
# V0.2.3
|
|
74
|
+
- changed getting lesgroepen only as return so not part of branch anymore
|
|
75
|
+
- removed lastcheck from lesgroepen
|
|
76
|
+
|
|
77
|
+
# V0.2.2
|
|
78
|
+
- bugfix lesgroepen
|
|
79
|
+
- added write to logger (debug)
|
|
80
|
+
- changed repr of Users
|
|
81
|
+
|
|
82
|
+
# V0.2.1
|
|
83
|
+
- added datetime to each branch
|
|
84
|
+
- init of branches is now only datestring, year will be determined internally
|
|
85
|
+
|
|
86
|
+
# V0.2.0
|
|
87
|
+
- changed decorator from_zermelo_dict to function
|
|
88
|
+
|
|
89
|
+
# V0.1.3
|
|
90
|
+
- added lesgroepen
|
|
91
|
+
|
|
92
|
+
# V0.1.2
|
|
93
|
+
- added Vakken
|
|
94
|
+
|
|
95
|
+
# V0.1.1
|
|
96
|
+
- added groepen / groups
|
|
97
|
+
- First setup of vakken
|
|
98
|
+
- cleanup of code
|
|
99
|
+
|
|
100
|
+
# V0.1.0: First Refactoring already:
|
|
101
|
+
- deleted fields.py and moved usefull functions to zermelo_api
|
|
102
|
+
- loading of data for (zermelo)list generalized in zermelo_api
|
|
103
|
+
|
|
104
|
+
# V0.0.8:
|
|
105
|
+
- Added loading of Users (Leerlingen & Medewerkers)
|
|
106
|
+
- Added backtracker to MyLogger
|
|
107
|
+
|
|
108
|
+
# V0.0.7:
|
|
109
|
+
- Added Loading of Branches
|
|
110
|
+
|
|
111
|
+
# V0.0.1 -> V0.0.5:
|
|
112
|
+
- Experimenting with creating of own package and major structure of project
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
zermelo_api/__init__.py,sha256=judeG_xciHkFfKbkcM7S29LFaey0ljdwC7tnpo61TX0,401
|
|
2
|
+
zermelo_api/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
zermelo_api/src/appointments.py,sha256=zHl41LsYWpJIaaHD1zTjgeKZXaNinTs9PxvKsbAzfLw,3019
|
|
4
|
+
zermelo_api/src/branches.py,sha256=BlhsqaKPjE8hb5LPeZwiW3pXqS1AoZeNyaF2JVM5YCI,3023
|
|
5
|
+
zermelo_api/src/config.py,sha256=_NMg7DVl0YvrsDblV8128TxNkWvHylIT8vFiqP6FwqI,1437
|
|
6
|
+
zermelo_api/src/credentials.py,sha256=rfgOatC6yY4iq8Fc7DboJ2tnlcZjcxsbQEAVt_VpaC0,644
|
|
7
|
+
zermelo_api/src/groepen.py,sha256=2HWDQxXgQaHYCJEFCmXJJH-qEB7mzeM3XLziGzKTT0M,941
|
|
8
|
+
zermelo_api/src/leerjaren.py,sha256=2h8SZBOj9bm0w2mYL2T8rMbRfCoIkDib_6ElfkdaNR4,1216
|
|
9
|
+
zermelo_api/src/lesgroepen.py,sha256=4o5C0q2ZfipkLnO9wpME-slYZFLTr5WEZvHNxFlRdl8,8417
|
|
10
|
+
zermelo_api/src/logger.py,sha256=QEKNK6TrDDrLPtXnW1hPbJIIjhmKwps1eFCJaanJjHE,1202
|
|
11
|
+
zermelo_api/src/time_utils.py,sha256=WAPhjZdaYFk3a5gtJGtH_Rmc2ckbQqpA_wMkzekdoOo,446
|
|
12
|
+
zermelo_api/src/users.py,sha256=eus_ZwvEfmqWAG7cC7Si4-qUsUvBF-cLv_PkhIiIs-s,3696
|
|
13
|
+
zermelo_api/src/vakken.py,sha256=ffYOjSi9f80ezCJeWjbQMc5fQAWAzTZTz6NayHuOdzc,1594
|
|
14
|
+
zermelo_api/src/zermelo_api.py,sha256=QsDHBa0y3h8jdSHQCB7hgFWh_Z0IStIBBizwc6cqJHs,4256
|
|
15
|
+
zermelo_api_vogk-0.2.16.dist-info/LICENSE,sha256=-g7wJAy8hK6L6CqC6eT8eGsW8t-6Ej8xK4pToiooumg,1068
|
|
16
|
+
zermelo_api_vogk-0.2.16.dist-info/METADATA,sha256=Vh6gL8dGflyYBW2OSV5AR9XPzQfUT_1y5AAeywvlx2I,2922
|
|
17
|
+
zermelo_api_vogk-0.2.16.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
18
|
+
zermelo_api_vogk-0.2.16.dist-info/top_level.txt,sha256=pDeE62UsvIU1PZdHVaksaQb3tkB5hvXla-bnHkLY-XA,12
|
|
19
|
+
zermelo_api_vogk-0.2.16.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zermelo_api
|