scratchattach 2.1.15b0__py3-none-any.whl → 3.0.0b1__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.
- cli/__about__.py +1 -0
- cli/__init__.py +26 -0
- cli/cmd/__init__.py +4 -0
- cli/cmd/group.py +127 -0
- cli/cmd/login.py +60 -0
- cli/cmd/profile.py +7 -0
- cli/cmd/sessions.py +5 -0
- cli/context.py +142 -0
- cli/db.py +66 -0
- cli/namespace.py +14 -0
- {scratchattach/cloud → cloud}/_base.py +112 -87
- {scratchattach/cloud → cloud}/cloud.py +16 -16
- {scratchattach/editor → editor}/__init__.py +2 -1
- {scratchattach/editor → editor}/asset.py +26 -14
- {scratchattach/editor → editor}/backpack_json.py +3 -5
- {scratchattach/editor → editor}/base.py +2 -4
- {scratchattach/editor → editor}/block.py +27 -22
- {scratchattach/editor → editor}/blockshape.py +1 -1
- {scratchattach/editor → editor}/build_defaulting.py +2 -2
- editor/commons.py +145 -0
- {scratchattach/editor → editor}/field.py +1 -1
- {scratchattach/editor → editor}/inputs.py +6 -3
- {scratchattach/editor → editor}/meta.py +10 -7
- {scratchattach/editor → editor}/monitor.py +10 -8
- {scratchattach/editor → editor}/mutation.py +68 -11
- {scratchattach/editor → editor}/pallete.py +1 -3
- {scratchattach/editor → editor}/prim.py +4 -0
- {scratchattach/editor → editor}/project.py +118 -16
- {scratchattach/editor → editor}/sprite.py +25 -15
- {scratchattach/editor → editor}/vlb.py +2 -2
- {scratchattach/eventhandlers → eventhandlers}/_base.py +1 -0
- {scratchattach/eventhandlers → eventhandlers}/cloud_events.py +26 -6
- {scratchattach/eventhandlers → eventhandlers}/cloud_recorder.py +4 -4
- {scratchattach/eventhandlers → eventhandlers}/cloud_requests.py +139 -54
- {scratchattach/eventhandlers → eventhandlers}/cloud_server.py +6 -3
- {scratchattach/eventhandlers → eventhandlers}/cloud_storage.py +1 -2
- eventhandlers/filterbot.py +163 -0
- other/other_apis.py +598 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/METADATA +7 -11
- scratchattach-3.0.0b1.dist-info/RECORD +79 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/WHEEL +1 -1
- scratchattach-3.0.0b1.dist-info/entry_points.txt +2 -0
- scratchattach-3.0.0b1.dist-info/top_level.txt +7 -0
- {scratchattach/site → site}/_base.py +32 -5
- site/activity.py +426 -0
- {scratchattach/site → site}/alert.py +4 -5
- {scratchattach/site → site}/backpack_asset.py +2 -1
- {scratchattach/site → site}/classroom.py +80 -73
- {scratchattach/site → site}/cloud_activity.py +43 -29
- {scratchattach/site → site}/comment.py +86 -100
- {scratchattach/site → site}/forum.py +8 -4
- site/placeholder.py +132 -0
- {scratchattach/site → site}/project.py +228 -122
- {scratchattach/site → site}/session.py +156 -71
- {scratchattach/site → site}/studio.py +139 -46
- site/typed_dicts.py +151 -0
- {scratchattach/site → site}/user.py +511 -215
- {scratchattach/utils → utils}/commons.py +12 -4
- {scratchattach/utils → utils}/encoder.py +7 -4
- {scratchattach/utils → utils}/enums.py +1 -0
- {scratchattach/utils → utils}/exceptions.py +36 -2
- utils/optional_async.py +154 -0
- utils/requests.py +306 -0
- scratchattach/__init__.py +0 -29
- scratchattach/editor/commons.py +0 -273
- scratchattach/eventhandlers/filterbot.py +0 -161
- scratchattach/other/other_apis.py +0 -284
- scratchattach/site/activity.py +0 -382
- scratchattach/utils/requests.py +0 -93
- scratchattach-2.1.15b0.dist-info/RECORD +0 -66
- scratchattach-2.1.15b0.dist-info/top_level.txt +0 -1
- {scratchattach/cloud → cloud}/__init__.py +0 -0
- {scratchattach/editor → editor}/code_translation/__init__.py +0 -0
- {scratchattach/editor → editor}/code_translation/parse.py +0 -0
- {scratchattach/editor → editor}/comment.py +0 -0
- {scratchattach/editor → editor}/extension.py +0 -0
- {scratchattach/editor → editor}/twconfig.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/__init__.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/combine.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/message_events.py +0 -0
- {scratchattach/other → other}/__init__.py +0 -0
- {scratchattach/other → other}/project_json_capabilities.py +0 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {scratchattach/site → site}/__init__.py +0 -0
- {scratchattach/site → site}/browser_cookie3_stub.py +0 -0
- {scratchattach/site → site}/browser_cookies.py +0 -0
- {scratchattach/utils → utils}/__init__.py +0 -0
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
"""Studio class"""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import warnings
|
|
4
5
|
import json
|
|
5
6
|
import random
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
|
|
10
|
+
from typing_extensions import Optional
|
|
11
|
+
|
|
12
|
+
from . import user, comment, project, activity, session
|
|
13
|
+
from scratchattach.site.typed_dicts import StudioDict, StudioRoleDict
|
|
14
|
+
from ._base import BaseSiteComponent
|
|
7
15
|
from scratchattach.utils import exceptions, commons
|
|
8
16
|
from scratchattach.utils.commons import api_iterative, headers
|
|
9
|
-
from ._base import BaseSiteComponent
|
|
10
|
-
|
|
11
17
|
from scratchattach.utils.requests import requests
|
|
12
18
|
|
|
13
19
|
|
|
20
|
+
@dataclass
|
|
14
21
|
class Studio(BaseSiteComponent):
|
|
15
22
|
"""
|
|
16
23
|
Represents a Scratch studio.
|
|
@@ -44,19 +51,25 @@ class Studio(BaseSiteComponent):
|
|
|
44
51
|
:.update(): Updates the attributes
|
|
45
52
|
|
|
46
53
|
"""
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
id: int = 0
|
|
55
|
+
title: Optional[str] = None
|
|
56
|
+
description: Optional[str] = None
|
|
57
|
+
host_id: Optional[int] = None
|
|
58
|
+
follower_count: Optional[int] = None
|
|
59
|
+
manager_count: Optional[int] = None
|
|
60
|
+
project_count: Optional[int] = None
|
|
61
|
+
image_url: Optional[str] = None
|
|
62
|
+
open_to_all: Optional[bool] = None
|
|
63
|
+
comments_allowed: Optional[bool] = None
|
|
64
|
+
created: Optional[str] = None
|
|
65
|
+
modified: Optional[str] = None
|
|
66
|
+
_session: Optional[session.Session] = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def __post_init__(self):
|
|
50
70
|
# Info on how the .update method has to fetch the data:
|
|
51
71
|
self.update_function = requests.get
|
|
52
|
-
self.update_api = f"https://api.scratch.mit.edu/studios/{
|
|
53
|
-
|
|
54
|
-
# Set attributes every Project object needs to have:
|
|
55
|
-
self._session = None
|
|
56
|
-
self.id = 0
|
|
57
|
-
|
|
58
|
-
# Update attributes from entries dict:
|
|
59
|
-
self.__dict__.update(entries)
|
|
72
|
+
self.update_api = f"https://api.scratch.mit.edu/studios/{self.id}"
|
|
60
73
|
|
|
61
74
|
# Headers and cookies:
|
|
62
75
|
if self._session is None:
|
|
@@ -71,33 +84,72 @@ class Studio(BaseSiteComponent):
|
|
|
71
84
|
self._json_headers["accept"] = "application/json"
|
|
72
85
|
self._json_headers["Content-Type"] = "application/json"
|
|
73
86
|
|
|
74
|
-
def _update_from_dict(self, studio):
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
try: self.created = studio["history"]["created"]
|
|
90
|
-
except Exception: pass
|
|
91
|
-
try: self.modified = studio["history"]["modified"]
|
|
92
|
-
except Exception: pass
|
|
93
|
-
try: self.follower_count = studio["stats"]["followers"]
|
|
94
|
-
except Exception: pass
|
|
95
|
-
try: self.manager_count = studio["stats"]["managers"]
|
|
96
|
-
except Exception: pass
|
|
97
|
-
try: self.project_count = studio["stats"]["projects"]
|
|
98
|
-
except Exception: pass
|
|
87
|
+
def _update_from_dict(self, studio: StudioDict):
|
|
88
|
+
self.id = int(studio["id"])
|
|
89
|
+
self.title = studio["title"]
|
|
90
|
+
self.description = studio["description"]
|
|
91
|
+
self.host_id = studio["host"]
|
|
92
|
+
self.open_to_all = studio["open_to_all"]
|
|
93
|
+
self.comments_allowed = studio["comments_allowed"]
|
|
94
|
+
self.image_url = studio["image"] # rename/alias to thumbnail_url?
|
|
95
|
+
self.created = studio["history"]["created"]
|
|
96
|
+
self.modified = studio["history"]["modified"]
|
|
97
|
+
|
|
98
|
+
stats = studio.get("stats", {})
|
|
99
|
+
self.follower_count = stats.get("followers", self.follower_count)
|
|
100
|
+
self.manager_count = stats.get("managers", self.manager_count)
|
|
101
|
+
self.project_count = stats.get("projects", self.project_count)
|
|
99
102
|
return True
|
|
100
103
|
|
|
104
|
+
def __str__(self):
|
|
105
|
+
ret = f"-S {self.id}"
|
|
106
|
+
if self.title:
|
|
107
|
+
ret += f" ({self.title})"
|
|
108
|
+
return ret
|
|
109
|
+
|
|
110
|
+
def __rich__(self):
|
|
111
|
+
from rich.panel import Panel
|
|
112
|
+
from rich.table import Table
|
|
113
|
+
from rich import box
|
|
114
|
+
from rich.markup import escape
|
|
115
|
+
|
|
116
|
+
url = f"[link={self.url}]{escape(self.title)}[/]"
|
|
117
|
+
|
|
118
|
+
ret = Table.grid(expand=True)
|
|
119
|
+
ret.add_column(ratio=1)
|
|
120
|
+
ret.add_column(ratio=3)
|
|
121
|
+
|
|
122
|
+
info = Table(box=box.SIMPLE)
|
|
123
|
+
info.add_column(url, overflow="fold")
|
|
124
|
+
info.add_column(f"#{self.id}", overflow="fold")
|
|
125
|
+
info.add_row("Host ID", str(self.host_id))
|
|
126
|
+
info.add_row("Followers", str(self.follower_count))
|
|
127
|
+
info.add_row("Projects", str(self.project_count))
|
|
128
|
+
info.add_row("Managers", str(self.manager_count))
|
|
129
|
+
info.add_row("Comments allowed", str(self.comments_allowed))
|
|
130
|
+
info.add_row("Open", str(self.open_to_all))
|
|
131
|
+
info.add_row("Created", self.created)
|
|
132
|
+
info.add_row("Modified", self.modified)
|
|
133
|
+
|
|
134
|
+
desc = Table(box=box.SIMPLE)
|
|
135
|
+
desc.add_row("Description", escape(self.description))
|
|
136
|
+
|
|
137
|
+
ret.add_row(
|
|
138
|
+
Panel(info, title=url),
|
|
139
|
+
Panel(desc, title="Description"),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return ret
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def url(self):
|
|
146
|
+
return f"https://scratch.mit.edu/studios/{self.id}"
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def thumbnail(self) -> bytes:
|
|
150
|
+
with requests.no_error_handling():
|
|
151
|
+
return requests.get(self.image_url).content
|
|
152
|
+
|
|
101
153
|
def follow(self):
|
|
102
154
|
"""
|
|
103
155
|
You can only use this function if this object was created using :meth:`scratchattach.session.Session.connect_studio`
|
|
@@ -156,7 +208,7 @@ class Studio(BaseSiteComponent):
|
|
|
156
208
|
).json()
|
|
157
209
|
if r is None:
|
|
158
210
|
raise exceptions.CommentNotFound()
|
|
159
|
-
_comment = comment.Comment(id=r["id"], _session=self._session, source=
|
|
211
|
+
_comment = comment.Comment(id=r["id"], _session=self._session, source=comment.CommentSource.STUDIO, source_id=self.id)
|
|
160
212
|
_comment._update_from_dict(r)
|
|
161
213
|
return _comment
|
|
162
214
|
|
|
@@ -191,7 +243,7 @@ class Studio(BaseSiteComponent):
|
|
|
191
243
|
).json()
|
|
192
244
|
if "id" not in r:
|
|
193
245
|
raise exceptions.CommentPostFailure(r)
|
|
194
|
-
_comment = comment.Comment(id=r["id"], _session=self._session, source=
|
|
246
|
+
_comment = comment.Comment(id=r["id"], _session=self._session, source=comment.CommentSource.STUDIO, source_id=self.id)
|
|
195
247
|
_comment._update_from_dict(r)
|
|
196
248
|
return _comment
|
|
197
249
|
|
|
@@ -291,7 +343,7 @@ class Studio(BaseSiteComponent):
|
|
|
291
343
|
content, parent_id=parent_id, commentee_id=commentee_id
|
|
292
344
|
)
|
|
293
345
|
|
|
294
|
-
def projects(self, limit=40, offset=0):
|
|
346
|
+
def projects(self, limit=40, offset=0) -> list[project.Project]:
|
|
295
347
|
"""
|
|
296
348
|
Gets the studio projects.
|
|
297
349
|
|
|
@@ -306,7 +358,7 @@ class Studio(BaseSiteComponent):
|
|
|
306
358
|
f"https://api.scratch.mit.edu/studios/{self.id}/projects", limit=limit, offset=offset)
|
|
307
359
|
return commons.parse_object_list(response, project.Project, self._session)
|
|
308
360
|
|
|
309
|
-
def curators(self, limit=40, offset=0):
|
|
361
|
+
def curators(self, limit=40, offset=0) -> list[user.User]:
|
|
310
362
|
"""
|
|
311
363
|
Gets the studio curators.
|
|
312
364
|
|
|
@@ -443,7 +495,7 @@ class Studio(BaseSiteComponent):
|
|
|
443
495
|
f"https://api.scratch.mit.edu/studios/{self.id}/managers", limit=limit, offset=offset)
|
|
444
496
|
return commons.parse_object_list(response, user.User, self._session, "username")
|
|
445
497
|
|
|
446
|
-
def host(self):
|
|
498
|
+
def host(self) -> user.User:
|
|
447
499
|
"""
|
|
448
500
|
Gets the studio host.
|
|
449
501
|
|
|
@@ -564,7 +616,7 @@ class Studio(BaseSiteComponent):
|
|
|
564
616
|
timeout=10,
|
|
565
617
|
).json()
|
|
566
618
|
|
|
567
|
-
def your_role(self):
|
|
619
|
+
def your_role(self) -> StudioRoleDict:
|
|
568
620
|
"""
|
|
569
621
|
Returns a dict with information about your role in the studio (whether you're following, curating, managing it or are invited)
|
|
570
622
|
"""
|
|
@@ -576,6 +628,41 @@ class Studio(BaseSiteComponent):
|
|
|
576
628
|
timeout=10,
|
|
577
629
|
).json()
|
|
578
630
|
|
|
631
|
+
def get_exact_project_count(self) -> int:
|
|
632
|
+
"""
|
|
633
|
+
Get the exact project count of a studio using a binary-search-like strategy
|
|
634
|
+
"""
|
|
635
|
+
if self.project_count is not None and self.project_count < 100:
|
|
636
|
+
return self.project_count
|
|
637
|
+
|
|
638
|
+
# Get maximum possible project count before binary search
|
|
639
|
+
maximum = 100
|
|
640
|
+
minimum = 0
|
|
641
|
+
|
|
642
|
+
while True:
|
|
643
|
+
if not self.projects(offset=maximum):
|
|
644
|
+
break
|
|
645
|
+
minimum = maximum
|
|
646
|
+
maximum *= 2
|
|
647
|
+
|
|
648
|
+
# Binary search
|
|
649
|
+
while True:
|
|
650
|
+
middle = (minimum + maximum) // 2
|
|
651
|
+
projects = self.projects(limit=40, offset=middle)
|
|
652
|
+
|
|
653
|
+
if not projects:
|
|
654
|
+
# too high - no projects found
|
|
655
|
+
maximum = middle
|
|
656
|
+
elif len(projects) < 40:
|
|
657
|
+
# we are 40 within true value, and can infer the rest
|
|
658
|
+
break
|
|
659
|
+
else:
|
|
660
|
+
# too low - full project list
|
|
661
|
+
minimum = middle
|
|
662
|
+
|
|
663
|
+
return middle + len(projects)
|
|
664
|
+
|
|
665
|
+
|
|
579
666
|
|
|
580
667
|
def get_studio(studio_id) -> Studio:
|
|
581
668
|
"""
|
|
@@ -592,7 +679,13 @@ def get_studio(studio_id) -> Studio:
|
|
|
592
679
|
|
|
593
680
|
If you want to use these, get the studio with :meth:`scratchattach.session.Session.connect_studio` instead.
|
|
594
681
|
"""
|
|
595
|
-
|
|
682
|
+
warnings.warn(
|
|
683
|
+
"Warning: For methods that require authentication, use session.connect_studio instead of get_studio.\n"
|
|
684
|
+
"If you want to remove this warning, use warnings.filterwarnings('ignore', category=scratchattach.StudioAuthenticationWarning).\n"
|
|
685
|
+
"To ignore all warnings of the type GetAuthenticationWarning, which includes this warning, use "
|
|
686
|
+
"`warnings.filterwarnings('ignore', category=scratchattach.GetAuthenticationWarning)`.",
|
|
687
|
+
exceptions.StudioAuthenticationWarning
|
|
688
|
+
)
|
|
596
689
|
return commons._get_object("id", studio_id, Studio, exceptions.StudioNotFound)
|
|
597
690
|
|
|
598
691
|
def search_studios(*, query="", mode="trending", language="en", limit=40, offset=0):
|
site/typed_dicts.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing_extensions import OrderedDict
|
|
4
|
+
|
|
5
|
+
from scratchattach.cloud import _base
|
|
6
|
+
from typing import TypedDict, Union, Optional, Required, NotRequired
|
|
7
|
+
|
|
8
|
+
class SessionUserDict(TypedDict):
|
|
9
|
+
id: int
|
|
10
|
+
banned: bool
|
|
11
|
+
should_vpn: bool
|
|
12
|
+
username: str
|
|
13
|
+
token: str
|
|
14
|
+
thumbnailUrl: str
|
|
15
|
+
dateJoined: str
|
|
16
|
+
email: str
|
|
17
|
+
birthYear: int
|
|
18
|
+
birthMonth: int
|
|
19
|
+
gender: str
|
|
20
|
+
|
|
21
|
+
class SessionOffenseDict(TypedDict):
|
|
22
|
+
expiresAt: float
|
|
23
|
+
messageType: str
|
|
24
|
+
createdAt: float
|
|
25
|
+
|
|
26
|
+
class SessionOffensesDict(TypedDict):
|
|
27
|
+
offenses: list[SessionOffenseDict]
|
|
28
|
+
showWarning: bool
|
|
29
|
+
muteExpiresAt: float
|
|
30
|
+
currentMessageType: str
|
|
31
|
+
|
|
32
|
+
class SessionPermissionsDict(TypedDict):
|
|
33
|
+
admin: bool
|
|
34
|
+
scratcher: bool
|
|
35
|
+
new_scratcher: bool
|
|
36
|
+
invited_scratcher: bool
|
|
37
|
+
social: bool
|
|
38
|
+
educator: bool
|
|
39
|
+
educator_invitee: bool
|
|
40
|
+
student: bool
|
|
41
|
+
mute_status: Union[dict, SessionOffensesDict]
|
|
42
|
+
|
|
43
|
+
class SessionFlagsDict(TypedDict):
|
|
44
|
+
must_reset_password: bool
|
|
45
|
+
must_complete_registration: bool
|
|
46
|
+
has_outstanding_email_confirmation: bool
|
|
47
|
+
show_welcome: bool
|
|
48
|
+
confirm_email_banner: bool
|
|
49
|
+
unsupported_browser_banner: bool
|
|
50
|
+
with_parent_email: bool
|
|
51
|
+
project_comments_enabled: bool
|
|
52
|
+
gallery_comments_enabled: bool
|
|
53
|
+
userprofile_comments_enabled: bool
|
|
54
|
+
everything_is_totally_normal: bool
|
|
55
|
+
|
|
56
|
+
class SessionDict(TypedDict):
|
|
57
|
+
user: SessionUserDict
|
|
58
|
+
permissions: SessionPermissionsDict
|
|
59
|
+
flags: SessionFlagsDict
|
|
60
|
+
|
|
61
|
+
class OcularUserMetaDict(TypedDict):
|
|
62
|
+
updated: str
|
|
63
|
+
updatedBy: str
|
|
64
|
+
|
|
65
|
+
class OcularUserDict(TypedDict):
|
|
66
|
+
_id: str
|
|
67
|
+
name: str
|
|
68
|
+
status: str
|
|
69
|
+
color: str
|
|
70
|
+
meta: OcularUserMetaDict
|
|
71
|
+
|
|
72
|
+
class UserHistoryDict(TypedDict):
|
|
73
|
+
joined: str
|
|
74
|
+
|
|
75
|
+
class UserProfileDict(TypedDict):
|
|
76
|
+
id: int
|
|
77
|
+
status: str
|
|
78
|
+
bio: str
|
|
79
|
+
country: str
|
|
80
|
+
images: dict[str, str]
|
|
81
|
+
membership_label: NotRequired[int]
|
|
82
|
+
membership_avatar_badge: NotRequired[int]
|
|
83
|
+
|
|
84
|
+
class UserDict(TypedDict):
|
|
85
|
+
id: NotRequired[int]
|
|
86
|
+
username: NotRequired[str]
|
|
87
|
+
scratchteam: NotRequired[bool]
|
|
88
|
+
history: NotRequired[UserHistoryDict]
|
|
89
|
+
profile: NotRequired[UserProfileDict]
|
|
90
|
+
|
|
91
|
+
class CloudLogActivityDict(TypedDict):
|
|
92
|
+
user: str
|
|
93
|
+
verb: str
|
|
94
|
+
name: str
|
|
95
|
+
value: Union[str, float, int]
|
|
96
|
+
timestamp: int
|
|
97
|
+
cloud: _base.AnyCloud
|
|
98
|
+
|
|
99
|
+
class CloudActivityDict(TypedDict):
|
|
100
|
+
method: str
|
|
101
|
+
name: str
|
|
102
|
+
value: Union[str, float, int]
|
|
103
|
+
project_id: int
|
|
104
|
+
cloud: _base.AnyCloud
|
|
105
|
+
|
|
106
|
+
class ClassroomDict(TypedDict):
|
|
107
|
+
id: int
|
|
108
|
+
title: str
|
|
109
|
+
description: str
|
|
110
|
+
status: str
|
|
111
|
+
date_start: NotRequired[str]
|
|
112
|
+
date_end: NotRequired[Optional[str]]
|
|
113
|
+
images: NotRequired[dict[str, str]]
|
|
114
|
+
educator: UserDict
|
|
115
|
+
is_closed: NotRequired[bool]
|
|
116
|
+
|
|
117
|
+
class StudioHistoryDict(TypedDict):
|
|
118
|
+
created: str
|
|
119
|
+
modified: str
|
|
120
|
+
|
|
121
|
+
class StudioStatsDict(TypedDict):
|
|
122
|
+
followers: int
|
|
123
|
+
managers: int
|
|
124
|
+
projects: int
|
|
125
|
+
|
|
126
|
+
class StudioDict(TypedDict):
|
|
127
|
+
id: int
|
|
128
|
+
title: str
|
|
129
|
+
description: str
|
|
130
|
+
host: int
|
|
131
|
+
open_to_all: bool
|
|
132
|
+
comments_allowed: bool
|
|
133
|
+
image: str
|
|
134
|
+
history: StudioHistoryDict
|
|
135
|
+
stats: NotRequired[StudioStatsDict]
|
|
136
|
+
|
|
137
|
+
class StudioRoleDict(TypedDict):
|
|
138
|
+
manager: bool
|
|
139
|
+
curator: bool
|
|
140
|
+
invited: bool
|
|
141
|
+
following: bool
|
|
142
|
+
|
|
143
|
+
class PlaceholderProjectDataMetadataDict(TypedDict):
|
|
144
|
+
title: str
|
|
145
|
+
description: str
|
|
146
|
+
|
|
147
|
+
# https://github.com/GarboMuffin/placeholder/blob/e1e98953342a40abbd626a111f621711f74e783b/src/routes/projects/%5Bproject%5D/%2Bpage.server.ts#L19
|
|
148
|
+
class PlaceholderProjectDataDict(TypedDict):
|
|
149
|
+
metadata: PlaceholderProjectDataMetadataDict
|
|
150
|
+
md5extsToSha256: OrderedDict[str, str]
|
|
151
|
+
adminOwnershipToken: Optional[str]
|