├── .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 |
--------------------------------------------------------------------------------