Package: zermeloapi
zermelo api v:2024.02.15.14.56.58documentationinstallpipinstall zermeloapihow to importimportzermeloapiinitializingzermelo=zermeloapi.zermelo(school,username,password,teacher=False,version=3)get tokentoken=zermelo.get_token()if u wand to u can give the school username and password again but u don't need to in that case u can useget_token(school,username,password)get access tokenaccess_token=zermelo.get_access_token()again if u wand to u can give the school and username again but u don't need to in that case u can useget_access_token(school,username)get raw scheduleraw_schedule=zermelo.get_schedule()u can give the week number and or year of u wand to else it uses the current year/week u can useget_schedule(week=07),get_schedule(year=2024)orget_schedule(year=2024,week=07)get scheduleraw_schedule=zermelo.get_schedule()u can give the week number and or year of u wand to else it uses the current year/week u can useget_schedule(week=07),get_schedule(year=2024)orget_schedule(year=2024,week=07)and u you can pass a rawshedule from get_raw_shedule()sort schedulesorted_schedule=zermelo.sort_schedule()like before u can give the week and year but u can also give the schedule fromget_schedule()instead to sort it defaults to getting it self but if u give it uses that one u can do that by using it like thissort_schedule(schedule)for the week and year it is the same syntaxreadable schedulereadable=zermelo.readable_schedule()again u can give it week and year with the same syntax as before also you can give it a sorted schedule (output fromsort_schedule()) by usingreadable_schedule(sorted_schedule)print schedulezermelo.print_schedule()yes u can use it like thiszermelo.print_schedule(zermelo.readable_schedule())butWHYif u wand that use it like that justprint(zermelo.readable_schedule())the only use is that it defaults tozermelo.readable_schedule()source code# from calendar import weekdayimportos.pathimportrequestsimportjsonimporttimeimportbase64# from datetime import datetimeimportdatetimeclasszermelo:expires_in=0school=''username=''teacher=Falsedebug=FalseTimeToAddToUtc=0def__init__(self,school,username,password=None,teacher=False,version=3,debug=False,linkcode=None):self.school=schoolself.username=usernameself.teacher=teacherself.debug=debugself.version='v'+str(version)self.TimeToAddToUtc=self.get_date()[2]iflinkcode==Noneandpassword!=None:self.token=self.get_tokenfromusrpsw(school=school,username=username,password=password)else:self.token=self.gettokenfromfile(linkcode=linkcode)defupdatetoken(self,linkcode):self.token=self.gettokenfromfile(linkcode=linkcode)defgettokenfromlinkcode(self,linkcode=None):iflinkcode==None:linkcode=input("apitoken: ").replace(" ",'')else:linkcode=str(linkcode).replace(" ",'')returnjson.loads(requests.post(f"https://ozhw.zportal.nl/api/v3/oauth/token?grant_type=authorization_code&code={linkcode}").text)["access_token"]defget_tokenfromusrpsw(self,school=None,username=None,password=None):if(school==None):school=self.schoolif(username==None):username=self.usernameif(password==None):password=self.passwordurl='https://'+school+'.zportal.nl/api/'+self.version+'/oauth'myobj={'username':username,'password':password,'client_id':'OAuthPage','redirect_uri':'/main/','scope':'','state':'4E252A','response_type':'code','tenant':school}x=requests.post(url,data=myobj,allow_redirects=False)ifself.debug:print(x.text)try:respons=x.headers['Location']except:respons=x.text# exit(0)# exit(0)start=respons.find("code=")+len("code=")end=respons.find("&",start)token=respons[start:end]start=respons.find("tenant=")+len("tenant=")end=respons.find("&",start)school=respons[start:end]start=respons.find("expires_in=")+len("expires_in=")end=respons.find("&",start)self.expires_in=respons[start:end]start=respons.find("loginMethod=")+len("loginMethod=")end=respons.find("&",start)self.loginMethod=respons[start:end]start=respons.find("interfaceVersion=")+len("interfaceVersion=")end=respons.find("&",start)self.interfaceVersion=respons[start:end]if(school==None):school=self.schoolif(token==None):token=self.get_token()ifself.debug:print(token)url='https://'+school+'.zportal.nl/api/'+self.version+'/oauth/token'myobj={'code':token,'client_id':'ZermeloPortal','client_secret':42,'grant_type':'authorization_code','rememberMe':False}ifself.debug:print("\n\n")ifself.debug:print(myobj,url)l=requests.post(url,data=myobj)ifself.debug:print(l.text)jl=json.loads(l.text)token=jl['access_token']return(token)defgettokenfromfile(self,file="./token",linkcode=None):ifnotos.path.exists(file):self.settokentofile(linkcode=linkcode)withopen(file)asf:filevalue=f.read()returnstr(base64.b64decode(filevalue))[2:-1]defsettokentofile(self,token=None,file="./token",linkcode=None):iftoken==None:token=self.gettokenfromlinkcode(linkcode=linkcode)file=str(base64.b64encode(bytes(token,"utf-8")))[2:-1]withopen("./token","w")asf:f.write(str(file))defget_date(self):now=datetime.datetime.now()return(now.year,now.isocalendar()[1],int((datetime.datetime.fromtimestamp(now.timestamp())-datetime.datetime.utcfromtimestamp(now.timestamp())).total_seconds()/3600))defget_raw_schedule(self,year:int=None,week:int=None,username:str=None,teacher:bool=False)->dict:time=self.get_date()ifyear==None:year=time[0]ifweek==None:week=time[1]ifusername==None:url=f"https://{self.school}.zportal.nl/api/v3/liveschedule?access_token={self.token}&{'teacher'if(self.teacher)else'student'}={self.username}&week={year}{week:0>2}"else:start=str(datetime.datetime.strptime(f"{year}-{week}-0","%Y-%U-%w").timestamp())[0:-2]end=str(datetime.datetime.strptime(f"{year}-{week}-6","%Y-%U-%w").timestamp())[0:-2]url=f"https://{self.school}.zportal.nl/api/v3/appointments?access_token={self.token}&start={start}&end={end}&{'teachers'if(teacher)else'possibleStudents'}={username}"ifself.debug:print(url)rawr=requests.get(url)ifself.debug:print(rawr)rl=json.loads(rawr.text)returnrldefget_schedule(self,rawschedule:dict=None,year=None,week=None,username=None,teacher=None):ifrawschedule==None:rawschedule=self.get_raw_schedule(year=year,week=week,username=username,teacher=teacher)response=rawschedule["response"]ifusername!=None:appointments=response["data"]else:data=response["data"][0]appointments=data["appointments"]return(appointments)defsort_schedule(self,schedule=None,year=None,week=None,username=None,teacher=None):if(schedule==None):schedule=self.get_schedule(year=year,week=week,username=username,teacher=teacher)pdate=0days=[[[],[],[]]]thisweek={}forlesinschedule:date=datetime.datetime.utcfromtimestamp(les["start"]).strftime('%Y%m%d')hour=str(int(datetime.datetime.utcfromtimestamp(les["start"]).strftime('%H'))+self.TimeToAddToUtc)time=datetime.datetime.utcfromtimestamp(les["start"]).strftime('%Y-%m-%d')+(hourifint(hour)>9else('0'+hour))+datetime.datetime.utcfromtimestamp(les["start"]).strftime(':%M')ehour=str(int(datetime.datetime.utcfromtimestamp(les["end"]).strftime('%H'))+self.TimeToAddToUtc)etime=datetime.datetime.utcfromtimestamp(les["end"]).strftime('%Y-%m-%d')+(ehourifint(ehour)>9else('0'+ehour))+datetime.datetime.utcfromtimestamp(les["end"]).strftime(':%M')# print(les["status"])ifusername!=None:code=2002ifles["cancelled"]ornotles["valid"]:code=4007elifles["moved"]:code=3012elifles["modified"]:code=3011ifnotdateinthisweek:thisweek[date]=[[],[]]if(notles["cancelled"]andles["valid"]):thisweek[date][0].append([les["subjects"][0],time,etime,str(les["locations"]),[{"code":code}],False])else:thisweek[date][1].append([les["subjects"][0],time,etime,str(les["locations"]),[{"code":code}],False])else:ifdate!=pdate:days.append([[],[],[]])ifself.debug:print(les)#,les["appointmentType"])# passforappointmentin(les["actions"]ifles["appointmentType"]=="choice"else[{'appointment':les,'status':les['status']}]):app=appointment["appointment"]ifapp!=Noneand(len(appointment["status"])>0):if(appointment["status"][0]["code"]<4000andappointment["status"][0]["code"]>=2000):days[-1][0].append([app["subjects"][0],time,etime,str(app["locations"]),appointment["status"],app["online"],"",app["id"]])elif(appointment["status"][0]["code"]==1004):days[-1][2].append([app["subjects"][0],time,etime,str(app["locations"]),appointment["status"],app["online"],appointment["post"],app["id"]])else:days[-1][1].append([app["subjects"][0],time,etime,str(app["locations"]),appointment["status"],app["online"],"",app["id"]])pdate=dateifusername!=None:thisweekdayssorted=[thisweek[i]foriin[str(c)forcinsorted([int(i)foriinthisweek.keys()])]]days=[]forvinthisweekdayssorted:part=[]forv2inv:part.append(sorted(v2,key=lambdax:int(x[1].replace(":","").replace("-","").replace(" ",""))))days.append(part)else:days.pop(0)return(days)defenroll(self,les=None,lesid=0):ifles==Noneandlesid==0:returnFalseurl=f"https://{self.school}.zportal.nl{les[6]iflesid==0elsef'/api/v3/liveschedule/enrollment?enroll={lesid}'}&access_token={self.token}"ifself.debug:print(url)req=requests.post(url)ifself.debug:print(req,req.text)returnTruedefunenroll(self,les=None,lesid=0):ifles==Noneandlesid==0:returnFalseurl=f"https://{self.school}.zportal.nl{str(les[6]).replace('&unenroll=','').replace('enroll','unenroll')iflesid==0elsef'/api/v3/liveschedule/enrollment?unenroll={lesid}'}&access_token={self.token}"ifself.debug:print(url)req=requests.post(url)ifself.debug:print(req,req.text)returnTruedefreadable_schedule(self,days=None,year=None,week=None,username=None,teacher=None):result=''if(days==None):days=self.sort_schedule(year=year,week=week,username=username,teacher=teacher)daysofweek=["Monday","Tuesday","Wednesday","Thursday","Friday"]fori,dayinenumerate(days):iflen(day[0])>0:result+=(daysofweek[i]+"\nstart: "+day[0][0][1]+"\tend: "+day[0][-1][2]+'\n')else:result+=(f"{daysofweek[i]}\n")forlesinday[0]:# if (les[4][0]["code"] < 4000 and les[4][0]["code"] >= 2000):ifTrue:result+=f"les:{les[0].ljust(8,' ')}lokaal:{('📷'if(les[5])else(les[3][2:-2]if(len(les[3][2:-2])>0)else'----')).ljust(8,' ')}start:{les[1]}\tend:{les[2]}\n"passresult+=("\n\n")returnresultdefprint_schedule(self,readable=None,days=None,year=None,week=None,username=None,teacher=None):if(readable==None):readable=self.readable_schedule(days=days,year=year,week=week,username=username,teacher=teacher)print(readable)