Skip to content

Commit a74dbcc

Browse files
committed
feat!: use activity types enum
Closes #482
1 parent e588b74 commit a74dbcc

File tree

4 files changed

+99
-59
lines changed

4 files changed

+99
-59
lines changed

scratchattach/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
ProjectAuthenticationWarning,
2020
UserAuthenticationWarning)
2121

22-
from .site.activity import Activity, ActvityTypes
22+
from .site.activity import Activity, ActivityTypes
2323
from .site.backpack_asset import BackpackAsset
2424
from .site.comment import Comment, CommentSource
2525
from .site.cloud_activity import CloudActivity

scratchattach/site/activity.py

Lines changed: 95 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
from dataclasses import dataclass
88
from typing import Optional, Any
9+
from enum import Enum
910

1011
from bs4 import Tag
1112

1213
from . import user, project, studio, session, forum
1314
from ._base import BaseSiteComponent
1415
from scratchattach.utils import exceptions
1516

16-
class ActvityTypes:
17-
# not an enum to preserve backwards compatability. Can be changed though.
17+
class ActivityTypes(Enum):
1818
loveproject = "loveproject"
1919
favoriteproject = "favoriteproject"
2020
becomecurator = "becomecurator"
@@ -74,7 +74,7 @@ class Activity(BaseSiteComponent):
7474

7575
datetime_created: Optional[str] = None
7676
time: Any = None
77-
type: Optional[str] = None
77+
type: Optional[ActivityTypes] = None
7878

7979
def __repr__(self):
8080
return f"Activity({repr(self.raw)})"
@@ -89,26 +89,26 @@ def parts(self):
8989
:return: A list of parts of the message. Join the parts to get a readable version, which is done with str(activity)
9090
"""
9191
match self.type:
92-
case "loveproject":
92+
case ActivityTypes.loveproject:
9393
return [f"{self.actor_username}", "loved", f"-P {self.title!r} ({self.project_id})"]
94-
case "favoriteproject":
94+
case ActivityTypes.favoriteproject:
9595
return [f"{self.actor_username}", "favorited", f"-P {self.project_title!r} ({self.project_id})"]
96-
case "becomecurator":
96+
case ActivityTypes.becomecurator:
9797
return [f"{self.actor_username}", "now curating", f"-S {self.title!r} ({self.gallery_id})"]
98-
case "followuser":
98+
case ActivityTypes.followuser:
9999
return [f"{self.actor_username}", "followed", f"-U {self.followed_username}"]
100-
case "followstudio":
100+
case ActivityTypes.followstudio:
101101
return [f"{self.actor_username}", "followed", f"-S {self.title!r} ({self.gallery_id})"]
102-
case "shareproject":
102+
case ActivityTypes.shareproject:
103103
return [f"{self.actor_username}", "reshared" if self.is_reshare else "shared",
104104
f"-P {self.title!r} ({self.project_id})"]
105-
case "remixproject":
105+
case ActivityTypes.remixproject:
106106
return [f"{self.actor_username}", "remixed",
107107
f"-P {self.parent_title!r} ({self.parent_id}) as -P {self.title!r} ({self.project_id})"]
108-
case "becomeownerstudio":
108+
case ActivityTypes.becomeownerstudio:
109109
return [f"{self.actor_username}", "became owner of", f"-S {self.gallery_title!r} ({self.gallery_id})"]
110110

111-
case "addcomment":
111+
case ActivityTypes.addcomment:
112112
ret = [self.actor_username, "commented on"]
113113

114114
match self.comment_type:
@@ -132,42 +132,42 @@ def parts(self):
132132

133133
return ret
134134

135-
case "curatorinvite":
135+
case ActivityTypes.curatorinvite:
136136
return [f"{self.actor_username}", "invited you to curate", f"-S {self.title!r} ({self.gallery_id})"]
137137

138-
case "userjoin":
138+
case ActivityTypes.userjoin:
139139
# This is also the first message you get - 'Welcome to Scratch'
140140
return [f"{self.actor_username}", "joined Scratch"]
141141

142-
case "studioactivity":
142+
case ActivityTypes.studioactivity:
143143
# the actor username should be systemuser
144144
return [f"{self.actor_username}", 'Studio activity', '', f"-S {self.title!r} ({self.gallery_id})"]
145145

146-
case "forumpost":
146+
case ActivityTypes.forumpost:
147147
return [f"{self.actor_username}", "posted in", f"-F {self.topic_title} ({self.topic_id})"]
148148

149-
case "updatestudio":
149+
case ActivityTypes.updatestudio:
150150
return [f"{self.actor_username}", "updated", f"-S {self.gallery_title} ({self.gallery_id})"]
151151

152-
case "createstudio":
152+
case ActivityTypes.createstudio:
153153
return [f"{self.actor_username}", "created", f"-S {self.gallery_title} ({self.gallery_id})"]
154154

155-
case "promotetomanager":
155+
case ActivityTypes.promotetomanager:
156156
return [f"{self.actor_username}", "promoted", f"-U {self.recipient_username}", "in",
157157
f"-S {self.gallery_title} ({self.gallery_id})"]
158158

159-
case "updateprofile":
159+
case ActivityTypes.updateprofile:
160160
return [f"{self.actor_username}", "updated their profile.", f"Changed fields: {self.changed_fields}"]
161161

162-
case "removeprojectfromstudio":
162+
case ActivityTypes.removeprojectfromstudio:
163163
return [f"{self.actor_username}", "removed", f"-P {self.project_title} ({self.project_id})", "from",
164164
f"-S {self.gallery_title} ({self.gallery_id})"]
165165

166-
case "addprojecttostudio":
166+
case ActivityTypes.addprojecttostudio:
167167
return [f"{self.actor_username}", "added", f"-P {self.project_title} ({self.project_id})", "to",
168168
f"-S {self.gallery_title} ({self.gallery_id})"]
169169

170-
case "performaction":
170+
case ActivityTypes.performaction:
171171
return [f"{self.actor_username}", "performed an action"]
172172

173173
case _:
@@ -182,7 +182,45 @@ def update(self):
182182

183183
def _update_from_dict(self, data):
184184
self.raw = data
185-
self.__dict__.update(data)
185+
186+
self._session = data.get("_session", self._session)
187+
self.raw = data.get("raw", self.raw)
188+
189+
self.id = data.get("id", self.id)
190+
self.actor_username = data.get("actor_username", self.actor_username)
191+
192+
self.project_id = data.get("project_id", self.project_id)
193+
self.gallery_id = data.get("gallery_id", self.gallery_id)
194+
self.username = data.get("username", self.username)
195+
self.followed_username = data.get("followed_username", self.followed_username)
196+
self.recipient_username = data.get("recipient_username", self.recipient_username)
197+
self.title = data.get("title", self.title)
198+
self.project_title = data.get("project_title", self.project_title)
199+
self.gallery_title = data.get("gallery_title", self.gallery_title)
200+
self.topic_title = data.get("topic_title", self.topic_title)
201+
self.topic_id = data.get("topic_id", self.topic_id)
202+
self.target_name = data.get("target_name", self.target_name)
203+
self.target_id = data.get("target_id", self.target_id)
204+
205+
self.parent_title = data.get("parent_title", self.parent_title)
206+
self.parent_id = data.get("parent_id", self.parent_id)
207+
208+
self.comment_type = data.get("comment_type", self.comment_type)
209+
self.comment_obj_id = data.get("comment_obj_id", self.comment_obj_id)
210+
self.comment_obj_title = data.get("comment_obj_title", self.comment_obj_title)
211+
self.comment_id = data.get("comment_id", self.comment_id)
212+
self.comment_fragment = data.get("comment_fragment", self.comment_fragment)
213+
214+
self.changed_fields = data.get("changed_fields", self.changed_fields)
215+
self.is_reshare = data.get("is_reshare", self.is_reshare)
216+
217+
self.datetime_created = data.get("datetime_created", self.datetime_created)
218+
self.time = data.get("time", self.time)
219+
220+
_type = data.get("type", self.type)
221+
if _type:
222+
self.type = ActivityTypes[_type]
223+
186224
return True
187225

188226
def _update_from_json(self, data: dict):
@@ -216,37 +254,37 @@ def _update_from_json(self, data: dict):
216254
self.raw = data
217255
self.datetime_created = _time
218256
if activity_type == 0:
219-
self.type = "followuser"
257+
self.type = ActivityTypes.followuser
220258
self.followed_username = data["followed_username"]
221259

222260
elif activity_type == 1:
223-
self.type = "followstudio"
261+
self.type = ActivityTypes.followstudio
224262
self.gallery_id = data["gallery"]
225263

226264
elif activity_type == 2:
227-
self.type = "loveproject"
265+
self.type = ActivityTypes.loveproject
228266
self.project_id = data["project"]
229267
self.recipient_username = recipient_username
230268

231269
elif activity_type == 3:
232-
self.type = "favoriteproject"
270+
self.type = ActivityTypes.favoriteproject
233271
self.project_id = data["project"]
234272
self.recipient_username = recipient_username
235273

236274
elif activity_type == 7:
237-
self.type = "addprojecttostudio"
275+
self.type = ActivityTypes.addprojecttostudio
238276
self.project_id = data["project"]
239277
self.gallery_id = data["gallery"]
240278
self.recipient_username = recipient_username
241279

242280
elif activity_type in (8, 9, 10):
243-
self.type = "shareproject"
281+
self.type = ActivityTypes.shareproject
244282
self.is_reshare = data["is_reshare"]
245283
self.project_id = data["project"]
246284
self.recipient_username = recipient_username
247285

248286
elif activity_type == 11:
249-
self.type = "remixproject"
287+
self.type = ActivityTypes.remixproject
250288
self.parent_id = data["parent"]
251289
warnings.warn(f"This may be incorrectly implemented.\n"
252290
f"Raw data: {data}\n"
@@ -256,30 +294,30 @@ def _update_from_json(self, data: dict):
256294
# type 12 does not exist in the HTML. That's why it was removed, not merged with type 13.
257295

258296
elif activity_type == 13:
259-
self.type = "createstudio"
297+
self.type = ActivityTypes.createstudio
260298
self.gallery_id = data["gallery"]
261299

262300
elif activity_type == 15:
263-
self.type = "updatestudio"
301+
self.type = ActivityTypes.updatestudio
264302
self.gallery_id = data["gallery"]
265303

266304
elif activity_type in (16, 17, 18, 19):
267-
self.type = "removeprojectfromstudio"
305+
self.type = ActivityTypes.removeprojectfromstudio
268306
self.gallery_id = data["gallery"]
269307
self.project_id = data["project"]
270308

271309
elif activity_type in (20, 21, 22):
272-
self.type = "promotetomanager"
310+
self.type = ActivityTypes.promotetomanager
273311
self.recipient_username = recipient_username
274312
self.gallery_id = data["gallery"]
275313

276314
elif activity_type in (23, 24, 25):
277-
self.type = "updateprofile"
315+
self.type = ActivityTypes.updateprofile
278316
self.changed_fields = data.get("changed_fields", {})
279317

280318
elif activity_type in (26, 27):
281319
# Comment in either project, user, or studio
282-
self.type = "addcomment"
320+
self.type = ActivityTypes.addcomment
283321
self.comment_fragment = data["comment_fragment"]
284322
self.comment_type = data["comment_type"]
285323
self.comment_obj_id = data["comment_obj_id"]
@@ -288,7 +326,7 @@ def _update_from_json(self, data: dict):
288326

289327
else:
290328
# This is coded in the scratch HTML, haven't found an example of it though
291-
self.type = "performaction"
329+
self.type = ActivityTypes.performaction
292330

293331

294332
def _update_from_html(self, data: Tag):
@@ -306,26 +344,26 @@ def _update_from_html(self, data: Tag):
306344

307345
self.target_name = data.find('div').find('span').find_next().text
308346
self.target_link = data.find('div').find('span').find_next()["href"]
309-
self.target_id = data.find('div').find('span').find_next()["href"].split("/")[-2]
347+
self.target_id = int(data.find('div').find('span').find_next()["href"].split("/")[-2])
310348

311-
self.type = data.find('div').find_all('span')[0].next_sibling.strip()
312-
if self.type == "loved":
313-
self.type = "loveproject"
349+
_type = data.find('div').find_all('span')[0].next_sibling.strip()
350+
if _type == "loved":
351+
self.type = ActivityTypes.loveproject
314352

315-
elif self.type == "favorited":
316-
self.type = "favoriteproject"
353+
elif _type == "favorited":
354+
self.type = ActivityTypes.favoriteproject
317355

318-
elif "curator" in self.type:
319-
self.type = "becomecurator"
356+
elif "curator" in _type:
357+
self.type = ActivityTypes.becomecurator
320358

321-
elif "shared" in self.type:
322-
self.type = "shareproject"
359+
elif "shared" in _type:
360+
self.type = ActivityTypes.shareproject
323361

324-
elif "is now following" in self.type:
362+
elif "is now following" in _type:
325363
if "users" in self.target_link:
326-
self.type = "followuser"
364+
self.type = ActivityTypes.followuser
327365
else:
328-
self.type = "followstudio"
366+
self.type = ActivityTypes.followstudio
329367

330368
return True
331369

@@ -340,14 +378,15 @@ def target(self):
340378
Returns the activity's target (depending on the activity, this is either a User, Project, Studio or Comment object).
341379
May also return None if the activity type is unknown.
342380
"""
381+
_type = self.type.value
343382

344-
if "project" in self.type: # target is a project
383+
if "project" in _type: # target is a project
345384
if self.target_id:
346385
return self._make_linked_object("id", self.target_id, project.Project, exceptions.ProjectNotFound)
347386
if self.project_id:
348387
return self._make_linked_object("id", self.project_id, project.Project, exceptions.ProjectNotFound)
349388

350-
if self.type == "becomecurator" or self.type == "followstudio": # target is a studio
389+
if _type == "becomecurator" or _type == "followstudio": # target is a studio
351390
if self.target_id:
352391
return self._make_linked_object("id", self.target_id, studio.Studio, exceptions.StudioNotFound)
353392
if self.gallery_id:
@@ -356,7 +395,7 @@ def target(self):
356395
if self.username:
357396
return self._make_linked_object("username", self.username, user.User, exceptions.UserNotFound)
358397

359-
if self.type == "followuser" or "curator" in self.type: # target is a user
398+
if _type == "followuser" or "curator" in _type: # target is a user
360399
if self.target_name:
361400
return self._make_linked_object("username", self.target_name, user.User, exceptions.UserNotFound)
362401
if self.followed_username:
@@ -365,7 +404,7 @@ def target(self):
365404
if self.recipient_username: # the recipient_username field always indicates the target is a user
366405
return self._make_linked_object("username", self.recipient_username, user.User, exceptions.UserNotFound)
367406

368-
if self.type == "addcomment": # target is a comment
407+
if _type == "addcomment": # target is a comment
369408
if self.comment_type == 0:
370409
# we need author name, but it has not been saved in this object
371410
_proj = self._session.connect_project(self.comment_obj_id)
@@ -380,7 +419,7 @@ def target(self):
380419

381420
return _c
382421

383-
if self.type == "forumpost":
422+
if _type == "forumpost":
384423
return forum.ForumTopic(id=603418, _session=self._session, title=self.title)
385424

386425
return None

scratchattach/utils/commons.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def parse_object_list(raw, /, __class: type[C], session=None, primary_key="id")
183183
for raw_dict in raw:
184184
try:
185185
_obj = __class(**{primary_key: raw_dict[primary_key], "_session": session})
186+
# noinspection PyProtectedMember
186187
_obj._update_from_dict(raw_dict)
187188
results.append(_obj)
188189
except Exception as e:

tests/test_other_apis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import warnings
55

66

7-
def test_activity():
7+
def test_other_apis():
88
sys.path.insert(0, ".")
99
import scratchattach as sa
1010
from scratchattach.utils import exceptions
@@ -63,4 +63,4 @@ def test_featured_data(_name, data: list[sa.Project | sa.Studio]):
6363

6464

6565
if __name__ == "__main__":
66-
test_activity()
66+
test_other_apis()

0 commit comments

Comments
 (0)