Skip to content

Commit bbe294f

Browse files
authored
edit: classroom dataclass (#431)
* edit: classroom dataclass * edit: add more attrs * fix: activity._update_from_json
1 parent 0591a25 commit bbe294f

File tree

4 files changed

+48
-34
lines changed

4 files changed

+48
-34
lines changed

scratchattach/site/_base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ class BaseSiteComponent(ABC):
1313
update_api: str
1414
_headers: dict[str, str]
1515
_cookies: dict[str, str]
16-
@abstractmethod
17-
def __init__(self):
18-
pass
16+
17+
# @abstractmethod
18+
# def __init__(self): # dataclasses do not implement __init__ directly
19+
# pass
1920

2021
def update(self):
2122
"""

scratchattach/site/activity.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ def __str__(self):
2020
return str(self.raw)
2121

2222
def __init__(self, **entries):
23-
2423
# Set attributes every Activity object needs to have:
2524
self._session = None
2625
self.raw = None
@@ -80,7 +79,7 @@ def _update_from_json(self, data: dict):
8079
else:
8180
recipient_username = None
8281

83-
default_case = True
82+
default_case = False
8483
# Even if `activity_type` is an invalid value; it will default to 'user performed an action'
8584

8685
if activity_type == 0:
@@ -283,7 +282,8 @@ def _update_from_json(self, data: dict):
283282
self.comment_obj_id = comment_obj_id
284283
self.comment_obj_title = comment_obj_title
285284
self.comment_id = comment_id
286-
285+
else:
286+
default_case = True
287287

288288
if default_case:
289289
# This is coded in the scratch HTML, haven't found an example of it though

scratchattach/site/classroom.py

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import datetime
44
import warnings
5-
from typing import Optional, TYPE_CHECKING, Any
5+
from dataclasses import dataclass, field
6+
from datetime import datetime
7+
from typing import Optional, TYPE_CHECKING, Any, Callable
68

79
import bs4
10+
from bs4 import BeautifulSoup
811

912
if TYPE_CHECKING:
1013
from scratchattach.site.session import Session
@@ -13,46 +16,50 @@
1316
from . import user, activity
1417
from ._base import BaseSiteComponent
1518
from scratchattach.utils import exceptions, commons
16-
from scratchattach.utils.commons import headers
17-
18-
from bs4 import BeautifulSoup
1919

2020

21+
@dataclass
2122
class Classroom(BaseSiteComponent):
22-
def __init__(self, **entries):
23+
title: str = None
24+
id: int = None
25+
classtoken: str = None
26+
27+
author: user.User = None
28+
about_class: str = None
29+
working_on: str = None
30+
31+
is_closed: bool = False
32+
datetime: datetime = None
33+
34+
35+
update_function: Callable = field(repr=False, default=requests.get)
36+
_session: Optional[Session] = field(repr=False, default=None)
37+
38+
def __post_init__(self):
2339
# Info on how the .update method has to fetch the data:
2440
# NOTE: THIS DOESN'T WORK WITH CLOSED CLASSES!
25-
self.update_function = requests.get
26-
if "id" in entries:
27-
self.update_api = f"https://api.scratch.mit.edu/classrooms/{entries['id']}"
28-
elif "classtoken" in entries:
29-
self.update_api = f"https://api.scratch.mit.edu/classtoken/{entries['classtoken']}"
41+
if self.id:
42+
self.update_api = f"https://api.scratch.mit.edu/classrooms/{self.id}"
43+
elif self.classtoken:
44+
self.update_api = f"https://api.scratch.mit.edu/classtoken/{self.classtoken}"
3045
else:
31-
raise KeyError(f"No class id or token provided! Entries: {entries}")
32-
33-
# Set attributes every Classroom object needs to have:
34-
self._session: Session = None
35-
self.id = None
36-
self.classtoken = None
37-
self.is_closed = False
38-
39-
self.__dict__.update(entries)
46+
raise KeyError(f"No class id or token provided! {self.__dict__ = }")
4047

4148
# Headers and cookies:
4249
if self._session is None:
43-
self._headers = headers
50+
self._headers = commons.headers
4451
self._cookies = {}
4552
else:
4653
self._headers = self._session._headers
4754
self._cookies = self._session._cookies
4855

4956
# Headers for operations that require accept and Content-Type fields:
50-
self._json_headers = dict(self._headers)
51-
self._json_headers["accept"] = "application/json"
52-
self._json_headers["Content-Type"] = "application/json"
57+
self._json_headers = {**self._headers,
58+
"accept": "application/json",
59+
"Content-Type": "application/json"}
5360

54-
def __repr__(self) -> str:
55-
return f"classroom called {self.title!r}"
61+
def __str__(self) -> str:
62+
return f"<Classroom {self.title!r}, id={self.id!r}>"
5663

5764
def update(self):
5865
try:
@@ -305,7 +312,8 @@ def close(self) -> None:
305312
warnings.warn(f"{self._session} may not be authenticated to edit {self}")
306313
raise e
307314

308-
def register_student(self, username: str, password: str = '', birth_month: Optional[int] = None, birth_year: Optional[int] = None,
315+
def register_student(self, username: str, password: str = '', birth_month: Optional[int] = None,
316+
birth_year: Optional[int] = None,
309317
gender: Optional[str] = None, country: Optional[str] = None, is_robot: bool = False) -> None:
310318
return register_by_token(self.id, self.classtoken, username, password, birth_month, birth_year, gender, country,
311319
is_robot)
@@ -346,7 +354,8 @@ def public_activity(self, *, limit=20):
346354

347355
return activities
348356

349-
def activity(self, student: str = "all", mode: str = "Last created", page: Optional[int] = None) -> list[dict[str, Any]]:
357+
def activity(self, student: str = "all", mode: str = "Last created", page: Optional[int] = None) -> list[
358+
dict[str, Any]]:
350359
"""
351360
Get a list of private activity, only available to the class owner.
352361
Returns:
@@ -421,7 +430,7 @@ def register_by_token(class_id: int, class_token: str, username: str, password:
421430
"is_robot": is_robot}
422431

423432
response = requests.post("https://scratch.mit.edu/classes/register_new_student/",
424-
data=data, headers=headers, cookies={"scratchcsrftoken": 'a'})
433+
data=data, headers=commons.headers, cookies={"scratchcsrftoken": 'a'})
425434
ret = response.json()[0]
426435

427436
if "username" in ret:

scratchattach/site/session.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ def __init__(self, **entries):
9898
self.username = None
9999
self.xtoken = None
100100
self.new_scratcher = None
101+
self.is_teacher = None
101102

102103
# Set attributes that Session object may get
103104
self._user: user.User = None
@@ -704,6 +705,9 @@ def mystuff_studios(self, filter_arg: str = "all", *, page: int = 1, sort_by: st
704705
raise exceptions.FetchError()
705706

706707
def mystuff_classes(self, mode: str = "Last created", page: Optional[int] = None) -> list[classroom.Classroom]:
708+
if self.is_teacher is None:
709+
self.update()
710+
707711
if not self.is_teacher:
708712
raise exceptions.Unauthorized(f"{self.username} is not a teacher; can't have classes")
709713
ascsort, descsort = get_class_sort_mode(mode)

0 commit comments

Comments
 (0)