├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.md ├── README_pl.md ├── py_librus_api ├── __init__.py └── librus.py └── setup.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. (optional) 22 | 23 | **Package version:** 24 | - e.g. [1.2.5] 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__ 3 | .main 4 | build 5 | dist 6 | py_librus_api.egg-info -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Tomasz Nieżurawski 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dokumentacja w języku polskim jest [tutaj.](README_pl.md) 2 | # py-librus-api 3 | # Table of contents 4 | 1. [Intro](#intro) 5 | 2. [Instalation](#instalation) 6 | 3. [Exmaple usage](#example-usage) 7 | 4. [Functions](#functions) 8 | # Intro 9 | API for librus e-register.
10 | There is no guarantee of developing this API further more! 11 | # Instalation 12 | `pip install py-librus-api` 13 | # Example usage 14 | ```python 15 | from py_librus_api import Librus 16 | 17 | 18 | librus = Librus() 19 | 20 | """Loops until user logs in successfully""" 21 | while not librus.logged_in: 22 | if not librus.login(login, password): 23 | print("Log in failed! Check your username and/or password!") 24 | else: 25 | print("Logged in successfully!") 26 | 27 | # Your code goes here 28 | ``` 29 | More info in [functions](#functions) 30 | # Functions 31 | **Required params/functions are marked with `!` prefix.**
32 | **`*` means that there is explanation below or something is optional.** 33 | ## !login(!login, !password) 34 | Function returns `true` if logging in was successful and `false` when not.
35 | `login` - Variable that contains user login.
36 | `password` - Variable that contains user password.
37 | Example usage: 38 | ```python 39 | librus.login(login_var, password_var) 40 | ``` 41 | ## You can check if user is logged in! 42 | ```python 43 | if librus.logged_in: 44 | ... 45 | ``` 46 | ## If user is not logged in, "User not logged in" exception will be raised! 47 | ## If connection error occurs, "Connection error" will be raised! 48 | ## get_lucky_number() 49 | Returns lucky number (`int`). 50 | ## get_grades() 51 | **For displaying grades in the console, it is recommended to use pretty-print (pprint)!** 52 | Returns all user grades in this foramt:
53 | ``` 54 | grades = { 55 | "Biologia": [ 56 | { 57 | "Grade": "5", 58 | "Weight": "3", 59 | "Category": "Kartkówka", 60 | 'Teacher': {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 61 | "Comment": "kartkówka z działu o płazach", 62 | "To_the_average": "Tak" 63 | } 64 | ... 65 | ] 66 | ... 67 | } 68 | ``` 69 | **Note that subject names are in language provided by Librus API (in this example it's polish)** 70 | ## get_teachers(mode*) 71 | Returns teachers' personal data (firstname, lastname) in couple of formats. 72 | You can choose format like that: 73 | ```python 74 | librus.get_teachers(mode="print") # etc. 75 | ``` 76 | ### List of formats: 77 | #### normal (default) 78 | ``` 79 | { 80 | 1123576: {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 81 | 1983456: {'FirstName': 'Grażyna', 'LastName': 'Kowalska'}, 82 | ... 83 | } 84 | ``` 85 | #### fullname 86 | ``` 87 | [ 88 | "Jan Kowalski", 89 | "Grażyna Kowalska", 90 | ... 91 | ] 92 | ``` 93 | #### fullname-id 94 | ``` 95 | [ 96 | '1476937: Jan Kowalski', 97 | '1484010: Grazyna Kowalska', 98 | ... 99 | ] 100 | ``` 101 | ## get_school_free_days() 102 | Returns a list of days free from school. 103 | Format: 104 | ``` 105 | [ 106 | {'DateFrom': '2019-01-01', 'DateTo': '2019-01-01', 'Name': 'Nowy Rok'}, 107 | ... 108 | ] 109 | ``` 110 | ## get_teacher_free_days() 111 | Returns a list of teachers' absence. 112 | Format: 113 | ``` 114 | [ 115 | { 116 | 'DateFrom': '2018-10-24', 117 | 'DateTo': '2018-10-26', 118 | 'Teacher': {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 119 | 'TimeFrom': '13:40:00', 120 | 'TimeTo': '15:15:00', 121 | 'Type': 'szkolenie' 122 | }, 123 | ] 124 | ``` 125 | **It can happen that `TimeFrom` and `TimeTo` won't exist!** 126 | 127 | ## get_attendances() 128 | Returns attendances in this format: 129 | ``` 130 | [ 131 | {'AddDate': '2018-10-29 12:52:51', 132 | 'AddedBy': {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 133 | 'Date': '2018-10-29', 134 | 'Id': 123456, 135 | 'Lesson': {'Subject': 'Chemia', 136 | 'Teacher': {'FirstName': 'Jan', 'LastName': 'Kowalski'}}, 137 | 'LessonNo': 6, 138 | 'Semester': 1, 139 | 'Type': {'IsPresenceKind': True, 140 | 'Name': 'Obecność', 141 | 'Order': 1, 142 | 'Short': 'ob', 143 | 'Standard': True} 144 | } 145 | ... 146 | ] 147 | ``` -------------------------------------------------------------------------------- /README_pl.md: -------------------------------------------------------------------------------- 1 | Documentation in english can be found [there.](README.md) 2 | # py-librus-api 3 | # Spis treści 4 | 1. [Wstęp](#wstęp) 5 | 2. [Instalacja](#instalacja) 6 | 3. [Przykładowe użycie](#przykładowe-użycie) 7 | 4. [Spis funkcji](#spis-funkcji) 8 | # Wstęp 9 | API do e-dziennika librus synergia. 10 | Nie ma gwarancji na to, że API będzie rozwijane! 11 | # Instalacja 12 | `pip install py-librus-api` 13 | # Przykładowe użycie 14 | ```python 15 | from py_librus_api import Librus 16 | 17 | 18 | librus = Librus() 19 | 20 | """Zapętla się dopóki użytkownik nie zaloguje się""" 21 | while not librus.logged_in: 22 | if not librus.login(login, password): 23 | print("Logowanie nie powiodło się! Sprawdź twój nick i/lub hasło.") 24 | else: 25 | print("Zalogowano.") 26 | 27 | ``` 28 | # Spis funkcji 29 | **Wymagane parametry/funkcje zaznaczone są prefixem `!`**
30 | **Jeżeli przy nazwie jest `*` oznacza to, że poniżej będzie wyjaśnienie jakiejś rzeczy** 31 | ## !login(!login, !password) 32 | Funkcja zwraca true gdy logowanie powiodło się i false gdy nie.
33 | `login` - Zmienna string z loginem użytkownika.
34 | `password` - Zmienna string z hasłem użytkownika.
35 | Przykładowe użycie: 36 | ```python 37 | librus.login(login_var, password_var) 38 | ``` 39 | ## Możesz sprawdzić czy użytkownik jest zalogowany! 40 | ```python 41 | if librus.logged_in: 42 | ... 43 | ``` 44 | ## Jeżeli użytkownik nie jest zalogowany, funkcje zwrócą -1! 45 | ## Jeżeli nastąpi błąd połączenia, funkcje zwrócą -2! 46 | ## get_lucky_number() 47 | Zwraca szczęśiwy numerek w formacie `int` 48 | ## get_grades() 49 | Zwraca wszystkie oceny użytkownika w poniższym formacie.
50 | **Do wypisania ocen w konsoli zaleca się używanie pretty-print (pprint)!** 51 | ``` 52 | grades = { 53 | "Biologia": [ 54 | { 55 | "grade": "5", 56 | "Weight": "3", 57 | "Category": "Kartkówka", 58 | 'Teacher': {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 59 | "Comment": "kartkówka z działu o płazach", 60 | "To_the_average": "Tak" 61 | } 62 | ... 63 | ] 64 | ... 65 | } 66 | ``` 67 | ## get_teachers(mode*) 68 | Funkcja zwraca dane nauczycieli, w formacie zależnym od wybranego trybu. 69 | Np. `print` 70 | ``` 71 | get_teachers(mode="print") 72 | ``` 73 | ### Lista trybów oraz format w jakim zwracane są dane: 74 | #### normal (domyślny) 75 | ``` 76 | { 77 | 1123576: {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 78 | 1983456: {'FirstName': 'Grażyna', 'LastName': 'Kowalska'}, 79 | ... 80 | } 81 | ``` 82 | #### fullname 83 | ``` 84 | [ 85 | "Jan Kowalski", 86 | "Grażyna Kowalska", 87 | ... 88 | ] 89 | ``` 90 | #### fullname-id 91 | ``` 92 | [ 93 | '1476937: Jan Kowalski', 94 | '1484010: Grazyna Kowalska', 95 | ... 96 | ] 97 | ``` 98 | ## get_school_free_days() 99 | Zwraca listę dni wolnych od szkoły w formacie: 100 | ``` 101 | [ 102 | {'DateFrom': '2019-01-01', 'DateTo': '2019-01-01', 'Name': 'Nowy Rok'}, 103 | ... 104 | ] 105 | ``` 106 | ## get_teacher_free_days() 107 | Zwraca listę nieobecności nauczyciela w formacie*: 108 | ``` 109 | [ 110 | { 111 | 'DateFrom': '2018-10-09', 112 | 'DateTo': '2018-10-09', 113 | 'Teacher': {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 114 | 'TimeFrom': '13:40:00', 115 | 'TimeTo': '15:15:00', 116 | 'Type': 'szkolenie' 117 | }, 118 | ] 119 | ``` 120 | **Może się zdarzyć, że pola `TimeFrom` i `TimeTo` nie będą istniały!** 121 | 122 | ## get_attendances() 123 | Zwraca listę obecności, nieobecności itp. w tym formacie: 124 | ``` 125 | [ 126 | {'AddDate': '2018-10-29 12:52:51', 127 | 'AddedBy': {'FirstName': 'Jan', 'LastName': 'Kowalski'}, 128 | 'Date': '2018-10-29', 129 | 'Id': 123456, 130 | 'Lesson': {'Subject': 'Chemia', 131 | 'Teacher': {'FirstName': 'Jan', 'LastName': 'Kowalski'}}, 132 | 'LessonNo': 6, 133 | 'Semester': 1, 134 | 'Type': {'IsPresenceKind': True, 135 | 'Name': 'Obecność', 136 | 'Order': 1, 137 | 'Short': 'ob', 138 | 'Standard': True}} 139 | ... 140 | ] 141 | ``` -------------------------------------------------------------------------------- /py_librus_api/__init__.py: -------------------------------------------------------------------------------- 1 | from .librus import Librus 2 | -------------------------------------------------------------------------------- /py_librus_api/librus.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import sys 3 | 4 | 5 | class Librus: 6 | host = "https://api.librus.pl/" 7 | headers = { 8 | "Authorization": "Basic Mjg6ODRmZGQzYTg3YjAzZDNlYTZmZmU3NzdiNThiMzMyYjE=" 9 | } 10 | logged_in = False 11 | 12 | lucky_number = None 13 | grades = None 14 | subjects = None 15 | categories = None 16 | students = None 17 | teachers = None 18 | comments = None 19 | lessons = None 20 | school_free_days = None 21 | teacher_free_days = None 22 | teacher_free_days_types = None 23 | attendances = None 24 | attendances_types = None 25 | 26 | # Checks data and decides method of login 27 | def login(self, login, password): 28 | if not self.logged_in: 29 | if login is None or password is None or login == "" or password == "": 30 | return False 31 | else: 32 | if self.make_connection(login, password): 33 | return True 34 | else: 35 | return False 36 | 37 | # Make connection and get access token 38 | def make_connection(self, login, password): 39 | r = None 40 | loop = 0 41 | while r is None: 42 | try: 43 | r = requests.post(self.host + "OAuth/Token", data={"username": login, 44 | "password": password, 45 | "librus_long_term_token": "1", 46 | "grant_type": "password"}, 47 | headers=self.headers) 48 | 49 | if r.ok: 50 | self.logged_in = True 51 | self.headers["Authorization"] = "Bearer " + r.json()["access_token"] 52 | 53 | return True 54 | else: 55 | return False 56 | except requests.exceptions.Timeout: 57 | if loop >= 10: 58 | return False 59 | else: 60 | loop += 1 61 | continue 62 | except requests.exceptions.RequestException: 63 | raise requests.exceptions.ConnectionError 64 | 65 | def get_data(self, url): 66 | if self.logged_in: 67 | try: 68 | return requests.get(self.host + "2.0/" + url, headers=self.headers) 69 | except (requests.exceptions.ConnectionError, TimeoutError, requests.exceptions.Timeout, 70 | requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout): 71 | raise Exception("Connection error") 72 | else: 73 | raise Exception("User not logged in") 74 | 75 | def get_lucky_number(self): 76 | if self.lucky_number is None: 77 | r = self.get_data("LuckyNumbers") 78 | self.lucky_number = r.json()["LuckyNumber"]["LuckyNumber"] 79 | 80 | return self.lucky_number 81 | 82 | return self.lucky_number 83 | 84 | def get_grades(self): 85 | r = self.get_data("Grades") 86 | 87 | if not self.subjects: 88 | self.get_subjects() 89 | 90 | if not self.categories: 91 | self.get_categories() 92 | 93 | if not self.teachers: 94 | self.get_teachers() 95 | 96 | if self.grades is None: 97 | self.grades = {i: [] for i in self.subjects.values()} 98 | 99 | grades_comments = self.get_comments() 100 | 101 | for i in r.json()["Grades"]: 102 | if "Comments" in i: 103 | comment = grades_comments[i["Comments"][0]["Id"]]["Text"] 104 | else: 105 | comment = "Brak komentarza" 106 | 107 | self.grades[self.subjects[i["Subject"]["Id"]]].append({ 108 | "Grade": i["Grade"], 109 | "Weight": self.categories[i["Category"]["Id"]]["Weight"], 110 | "Category": self.categories[i["Category"]["Id"]]["Name"], 111 | "Teacher": self.teachers[i["AddedBy"]["Id"]], 112 | "Comment": comment, 113 | "To_the_average": self.categories[i["Category"]["Id"]]["CountToTheAverage"] 114 | }) 115 | 116 | return self.grades 117 | 118 | def get_subjects(self): 119 | if self.subjects is None: 120 | r = self.get_data("Subjects") 121 | 122 | self.subjects = {i["Id"]: i["Name"] for i in r.json()["Subjects"]} 123 | 124 | return self.subjects 125 | 126 | def get_categories(self): 127 | if self.categories is None: 128 | self.categories = {} 129 | 130 | r = self.get_data("Grades/Categories") 131 | 132 | for i in r.json()["Categories"]: 133 | if "Weight" in i: 134 | w = i["Weight"] 135 | else: 136 | w = None 137 | 138 | if i["CountToTheAverage"]: 139 | i["CountToTheAverage"] = "Tak" 140 | else: 141 | i["CountToTheAverage"] = "Nie" 142 | 143 | self.categories[i["Id"]] = { 144 | "Name": i["Name"], 145 | "Weight": w, 146 | "CountToTheAverage": i["CountToTheAverage"], 147 | } 148 | 149 | return self.categories 150 | 151 | def get_teachers(self, *, mode="normal"): 152 | if self.teachers is None: 153 | r = self.get_data("Users") 154 | 155 | self.teachers = { 156 | i["Id"]: { 157 | "FirstName": i["FirstName"], 158 | "LastName": i["LastName"] 159 | } for i in r.json()["Users"] 160 | } 161 | 162 | if mode == "fullname": 163 | return ["%s %s" % (data["FirstName"], data["LastName"]) for t_id, data in self.teachers.items()] 164 | elif mode == "fullname-id": 165 | return ["%s: %s %s" % (t_id, data["FirstName"], data["LastName"]) for t_id, data in self.teachers.items()] 166 | 167 | return self.teachers 168 | 169 | def get_comments(self): 170 | if self.comments is None: 171 | r = self.get_data("Grades/Comments") 172 | 173 | self.comments = { 174 | i["Id"]: { 175 | "Text": i["Text"] 176 | } for i in r.json()["Comments"] 177 | } 178 | 179 | return self.comments 180 | 181 | def get_school_free_days(self): 182 | if self.school_free_days is None: 183 | r = self.get_data("SchoolFreeDays") 184 | self.school_free_days = r.json()["SchoolFreeDays"] 185 | 186 | for i in self.school_free_days: 187 | for e in ["Id", "Units"]: 188 | i.pop(e) 189 | 190 | return self.school_free_days 191 | 192 | def get_teacher_free_days(self): 193 | if self.teachers is None: 194 | self.get_teachers() 195 | 196 | if self.teacher_free_days_types is None: 197 | r = self.get_data("TeacherFreeDays/Types") 198 | 199 | self.teacher_free_days_types = { 200 | i["Id"]: i["Name"] for i in r.json()["Types"] 201 | } 202 | 203 | if self.teacher_free_days is None: 204 | r = self.get_data("TeacherFreeDays") 205 | 206 | self.teacher_free_days = r.json()["TeacherFreeDays"] 207 | 208 | for i in self.teacher_free_days: 209 | i.pop("Id") 210 | i["Teacher"] = self.teachers[i["Teacher"]["Id"]] 211 | i["Type"] = self.teacher_free_days_types[i["Type"]["Id"]] 212 | 213 | return self.teacher_free_days 214 | 215 | def get_lessons(self): 216 | if self.lessons is None: 217 | if self.subjects is None: 218 | self.get_subjects() 219 | 220 | if self.teachers is None: 221 | self.get_teachers() 222 | 223 | r = self.get_data("Lessons") 224 | 225 | self.lessons = { 226 | i["Id"]: { 227 | "Subject": self.subjects[i["Subject"]["Id"]], 228 | "Teacher": self.teachers[i["Teacher"]["Id"]] 229 | 230 | } for i in r.json()["Lessons"] 231 | } 232 | 233 | return self.lessons 234 | 235 | def get_attendances(self): 236 | if self.attendances is None: 237 | if self.attendances_types is None: 238 | r = self.get_data("Attendances/Types") 239 | 240 | self.attendances_types = { 241 | i["Id"]: { 242 | "Name": i["Name"], 243 | "Short": i["Short"], 244 | "Standard": i["Standard"], 245 | "IsPresenceKind": i["IsPresenceKind"], 246 | "Order": i["Order"] 247 | } for i in r.json()["Types"] 248 | } 249 | 250 | if self.lessons is None: 251 | self.get_lessons() 252 | 253 | self.attendances = self.get_data("Attendances").json()["Attendances"] 254 | 255 | for i in self.attendances: 256 | i.pop("Student") 257 | i["Type"] = self.attendances_types[i["Type"]["Id"]] 258 | i["AddedBy"] = self.teachers[i["AddedBy"]["Id"]] 259 | i["Lesson"] = self.lessons[i["Lesson"]["Id"]] 260 | 261 | return self.attendances 262 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="py_librus_api", 8 | version="0.3.1", 9 | author="Tomasz Nieżurawski", 10 | author_email="tomek.niezurawski@gmail.com", 11 | description="A librus api made in python.", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/TheAmazingRak/py-librus-api", 15 | packages=setuptools.find_packages(), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | ) 22 | 23 | install_requires = [ 24 | 'requests' 25 | ] 26 | --------------------------------------------------------------------------------