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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywebtop
3
- Version: 0.0.1
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
+ [![PyPI version](https://badge.fury.io/py/pywebtop.svg)](https://badge.fury.io/py/pywebtop)
45
+ [![Python Versions](https://img.shields.io/pypi/pyversions/pywebtop.svg)](https://pypi.org/project/pywebtop/)
46
+ [![License](https://img.shields.io/github/license/t0mer/pywebtop.svg)](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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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
- logger.info("Students dashboard fetched successfully")
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}")
@@ -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,,