pywebtop 0.0.1__py3-none-any.whl → 0.0.2__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.
- {pywebtop-0.0.1.dist-info → pywebtop-0.0.2.dist-info}/METADATA +46 -1
- pywebtop-0.0.2.dist-info/RECORD +9 -0
- {pywebtop-0.0.1.dist-info → pywebtop-0.0.2.dist-info}/WHEEL +1 -1
- webtop/client.py +91 -1
- pywebtop-0.0.1.dist-info/RECORD +0 -9
- {pywebtop-0.0.1.dist-info → pywebtop-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {pywebtop-0.0.1.dist-info → pywebtop-0.0.2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pywebtop
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Unofficial async Python API wrapper for Webtop (SmartSchool)
|
|
5
5
|
Home-page: https://github.com/t0mer/pywebtop
|
|
6
6
|
Download-URL: https://pypi.org/project/pywebtop/
|
|
@@ -40,6 +40,11 @@ Dynamic: summary
|
|
|
40
40
|
|
|
41
41
|
# pywebtop
|
|
42
42
|
|
|
43
|
+
|
|
44
|
+
[](https://badge.fury.io/py/pywebtop)
|
|
45
|
+
[](https://pypi.org/project/pywebtop/)
|
|
46
|
+
[](https://github.com/t0mer/pywebtop/blob/main/LICENSE)
|
|
47
|
+
|
|
43
48
|
An unofficial async Python API wrapper for **Webtop** (SmartSchool educational platform). This library provides easy access to Webtop's student portal API endpoints for retrieving grades, homework, schedules, messages, notifications, and more.
|
|
44
49
|
|
|
45
50
|
## Features
|
|
@@ -52,6 +57,7 @@ An unofficial async Python API wrapper for **Webtop** (SmartSchool educational p
|
|
|
52
57
|
- 🔔 **Notifications** - Get unread notifications and notification settings
|
|
53
58
|
- 📊 **Discipline Events** - Retrieve behavior/discipline records
|
|
54
59
|
- ⚙️ **Configurable** - Custom base URL, timeout, and auto-login support
|
|
60
|
+
- 👥 **Multi-Student Support** - Discover and switch between siblings/linked accounts
|
|
55
61
|
|
|
56
62
|
## Installation
|
|
57
63
|
|
|
@@ -157,6 +163,26 @@ session = await client.login()
|
|
|
157
163
|
# session.first_name, session.last_name - user names
|
|
158
164
|
```
|
|
159
165
|
|
|
166
|
+
#### Managing Multiple Students
|
|
167
|
+
If a parent account is linked to multiple students (e.g., siblings), you can discover and switch between them.
|
|
168
|
+
##### `get_linked_students()`
|
|
169
|
+
Get a list of all students linked to the current account.
|
|
170
|
+
```python
|
|
171
|
+
students = await client.get_linked_students()
|
|
172
|
+
for student in students:
|
|
173
|
+
print(f"Name: {student.get('studentLogin')}, ID: {student.get('studentId')}")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
##### `switch_student()`
|
|
177
|
+
Switch the active session to a specific student. This updates the session context for all subsequent requests.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
# Switch to a student using their ID
|
|
181
|
+
new_session = await client.switch_student(student_id="...")
|
|
182
|
+
print(f"Switched to school: {new_session.school_name}")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
|
|
160
186
|
#### Dashboard & Students
|
|
161
187
|
|
|
162
188
|
##### `get_students()`
|
|
@@ -414,6 +440,25 @@ except Exception as e:
|
|
|
414
440
|
|
|
415
441
|
Authentication is handled automatically via httpx's cookie jar. The token is stored as a cookie and included in all subsequent requests.
|
|
416
442
|
|
|
443
|
+
### Obtaining the `data` Parameter
|
|
444
|
+
|
|
445
|
+
The `data` parameter is an encryption key used by the Webtop API. To find your specific `data` value:
|
|
446
|
+
|
|
447
|
+
1. **Use Browser DevTools:**
|
|
448
|
+
- Open your browser and navigate to the Webtop login page
|
|
449
|
+
- Open Developer Tools (F12)
|
|
450
|
+
- Go to the Network tab
|
|
451
|
+
- Log in to Webtop
|
|
452
|
+
- Find the `LoginByUserNameAndPassword` request
|
|
453
|
+
- Check the request payload - the `Data` field contains your encryption key
|
|
454
|
+
|
|
455
|
+
2. **Default Value:**
|
|
456
|
+
- The default value `+Aabe7FAdVluG6Lu+0ibrA==` works for most SmartSchool installations
|
|
457
|
+
- If login fails, you may need to capture your specific `data` value using the method above
|
|
458
|
+
|
|
459
|
+
**Note:** The `data` parameter appears to be institution-specific. Once you find the correct value for your school, it should remain constant.
|
|
460
|
+
|
|
461
|
+
|
|
417
462
|
## Configuration
|
|
418
463
|
|
|
419
464
|
### Custom Base URL
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pywebtop-0.0.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
2
|
+
webtop/__init__.py,sha256=7ZxOqQrUn_zGb2ssl6wHwEq5-bC0z7UPzQrODTbHzCs,265
|
|
3
|
+
webtop/client.py,sha256=Ww6Ddum4tap9V__U9AJsq8K6TNUhEUVlTRyJ7prkqOo,18801
|
|
4
|
+
webtop/exceptions.py,sha256=MXCrWO3oCPYvjOi3FRw0hHA5wrt_GECtu4LH1gHrVsU,248
|
|
5
|
+
webtop/models.py,sha256=eeLE6KIWtfirQv2kOpkwY3-XAijjxaLiSPc2MRxC_hU,557
|
|
6
|
+
pywebtop-0.0.2.dist-info/METADATA,sha256=DkElOrbHdcYDswJgpPHwts6dBTWIUG0oKLw01WRmRYU,15022
|
|
7
|
+
pywebtop-0.0.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
pywebtop-0.0.2.dist-info/top_level.txt,sha256=_bDSmWeeECbJ-EVE_vSOQjwJIC-EJkFxNOBDpiBLP1c,7
|
|
9
|
+
pywebtop-0.0.2.dist-info/RECORD,,
|
webtop/client.py
CHANGED
|
@@ -162,6 +162,60 @@ class WebtopClient:
|
|
|
162
162
|
logger.info("Auto-login triggered")
|
|
163
163
|
await self.login()
|
|
164
164
|
|
|
165
|
+
async def switch_student(self, student_id: str, saved_user: str = "") -> WebtopSession:
|
|
166
|
+
"""
|
|
167
|
+
Switch the active student context (e.g. for siblings in different schools).
|
|
168
|
+
|
|
169
|
+
POST /server/api/user/ChangeUser
|
|
170
|
+
"""
|
|
171
|
+
logger.info(f"Switching to student_id: {student_id[:10]}...")
|
|
172
|
+
try:
|
|
173
|
+
resp = await self._http.post(
|
|
174
|
+
"/server/api/user/ChangeUser",
|
|
175
|
+
json={
|
|
176
|
+
"StudentId": student_id,
|
|
177
|
+
"institutionCode": None,
|
|
178
|
+
"savedUser": saved_user,
|
|
179
|
+
"userType": None
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if resp.status_code >= 400:
|
|
184
|
+
logger.error(f"Switch student failed: {resp.status_code} {resp.text}")
|
|
185
|
+
raise WebtopLoginError(f"Switch student failed: {resp.text}")
|
|
186
|
+
|
|
187
|
+
data = resp.json()
|
|
188
|
+
if data.get("status") is not True:
|
|
189
|
+
logger.error(f"Switch student returned status=false: {data}")
|
|
190
|
+
raise WebtopLoginError(f"Switch student failed: {data}")
|
|
191
|
+
|
|
192
|
+
new_data = data.get("data")
|
|
193
|
+
if not new_data:
|
|
194
|
+
raise WebtopLoginError("Switch student succeeded but returned no data")
|
|
195
|
+
|
|
196
|
+
# Update session
|
|
197
|
+
token = new_data.get("token")
|
|
198
|
+
# Update cookie if token changed
|
|
199
|
+
if token:
|
|
200
|
+
self._http.cookies.set("webToken", token)
|
|
201
|
+
|
|
202
|
+
self._session = WebtopSession(
|
|
203
|
+
token=token,
|
|
204
|
+
user_id=new_data.get("userId"),
|
|
205
|
+
student_id=new_data.get("id"), # Note: response uses 'id', request uses 'StudentId'
|
|
206
|
+
school_id=new_data.get("schoolId"),
|
|
207
|
+
school_name=new_data.get("schoolName"),
|
|
208
|
+
first_name=new_data.get("firstName"),
|
|
209
|
+
last_name=new_data.get("lastName"),
|
|
210
|
+
raw_login_data=new_data,
|
|
211
|
+
)
|
|
212
|
+
logger.info(f"Switched successfully to: {self._session.first_name} {self._session.last_name}")
|
|
213
|
+
return self._session
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.error(f"Failed to switch student: {e}")
|
|
217
|
+
raise
|
|
218
|
+
|
|
165
219
|
async def request(
|
|
166
220
|
self,
|
|
167
221
|
method: str,
|
|
@@ -202,6 +256,31 @@ class WebtopClient:
|
|
|
202
256
|
|
|
203
257
|
return resp
|
|
204
258
|
|
|
259
|
+
async def get_linked_students(self) -> list[Dict[str, Any]]:
|
|
260
|
+
"""
|
|
261
|
+
Get all students linked to this account (e.g. siblings in different schools).
|
|
262
|
+
|
|
263
|
+
POST /server/api/user/GetMultipleUsersForUser
|
|
264
|
+
"""
|
|
265
|
+
logger.info("Fetching linked students...")
|
|
266
|
+
try:
|
|
267
|
+
resp = await self.request(
|
|
268
|
+
"POST",
|
|
269
|
+
"/server/api/user/GetMultipleUsersForUser",
|
|
270
|
+
json={}
|
|
271
|
+
)
|
|
272
|
+
data = resp.json()
|
|
273
|
+
if data.get("status") is not True:
|
|
274
|
+
logger.error(f"Failed to get linked students: {data}")
|
|
275
|
+
raise WebtopRequestError(f"Failed to get linked students: {data}")
|
|
276
|
+
|
|
277
|
+
students = data.get("data", [])
|
|
278
|
+
logger.info(f"Found {len(students)} linked students")
|
|
279
|
+
return students
|
|
280
|
+
except Exception as e:
|
|
281
|
+
logger.error(f"Failed to get linked students: {e}")
|
|
282
|
+
raise
|
|
283
|
+
|
|
205
284
|
# -----------------------------
|
|
206
285
|
# Endpoints
|
|
207
286
|
# -----------------------------
|
|
@@ -219,7 +298,18 @@ class WebtopClient:
|
|
|
219
298
|
json={},
|
|
220
299
|
)
|
|
221
300
|
data = resp.json()
|
|
222
|
-
|
|
301
|
+
if isinstance(data, dict) and 'data' in data:
|
|
302
|
+
# Check if 'data' is the new structure (dict) or old (list)
|
|
303
|
+
payload = data['data']
|
|
304
|
+
if isinstance(payload, dict):
|
|
305
|
+
# New format: It returns a config object. The actual students might be in
|
|
306
|
+
# specific apps or the structure completely changed.
|
|
307
|
+
# For now, return the raw payload so the user can parse it,
|
|
308
|
+
# but log the structure for debugging.
|
|
309
|
+
logger.info(f"Dashboard returned dictionary payload with keys: {payload.keys()}")
|
|
310
|
+
elif isinstance(payload, list):
|
|
311
|
+
logger.info(f"Dashboard returned list of {len(payload)} students")
|
|
312
|
+
|
|
223
313
|
return data
|
|
224
314
|
except Exception as e:
|
|
225
315
|
logger.error(f"Failed to get students: {e}")
|
pywebtop-0.0.1.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
pywebtop-0.0.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
2
|
-
webtop/__init__.py,sha256=7ZxOqQrUn_zGb2ssl6wHwEq5-bC0z7UPzQrODTbHzCs,265
|
|
3
|
-
webtop/client.py,sha256=sNa4EJCa8PHmJLyRal0dV5pEKC58RuyS9faJ325HEqQ,14897
|
|
4
|
-
webtop/exceptions.py,sha256=MXCrWO3oCPYvjOi3FRw0hHA5wrt_GECtu4LH1gHrVsU,248
|
|
5
|
-
webtop/models.py,sha256=eeLE6KIWtfirQv2kOpkwY3-XAijjxaLiSPc2MRxC_hU,557
|
|
6
|
-
pywebtop-0.0.1.dist-info/METADATA,sha256=YS0czfrTfgbBpj7QtcclUg5Ma7PNRRH1Btt0oJYri8s,13106
|
|
7
|
-
pywebtop-0.0.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
8
|
-
pywebtop-0.0.1.dist-info/top_level.txt,sha256=_bDSmWeeECbJ-EVE_vSOQjwJIC-EJkFxNOBDpiBLP1c,7
|
|
9
|
-
pywebtop-0.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|