|
3 | 3 |
|
4 | 4 | import json |
5 | 5 | import random |
| 6 | +import re |
6 | 7 | import string |
7 | 8 | from datetime import datetime, timezone |
8 | 9 |
|
| 10 | +from bs4 import BeautifulSoup |
| 11 | + |
| 12 | +from ._base import BaseSiteComponent |
9 | 13 | from ..eventhandlers import message_events |
10 | | -from . import project |
| 14 | + |
| 15 | +from ..utils import commons |
11 | 16 | from ..utils import exceptions |
| 17 | +from ..utils.commons import headers |
| 18 | +from ..utils.requests import Requests as requests |
| 19 | + |
| 20 | +from . import project |
12 | 21 | from . import studio |
13 | 22 | from . import forum |
14 | | -from bs4 import BeautifulSoup |
15 | | -from ._base import BaseSiteComponent |
16 | | -from ..utils.commons import headers |
17 | | -from ..utils import commons |
18 | 23 | from . import comment |
19 | 24 | from . import activity |
20 | | - |
21 | | -from ..utils.requests import Requests as requests |
| 25 | +from . import classroom |
22 | 26 |
|
23 | 27 | class Verificator: |
24 | 28 |
|
@@ -70,6 +74,10 @@ def __init__(self, **entries): |
70 | 74 | self.username = None |
71 | 75 | self.name = None |
72 | 76 |
|
| 77 | + # cache value for classroom getter method (using @property) |
| 78 | + # first value is whether the cache has actually been set (because it can be None), second is the value itself |
| 79 | + self._classroom: tuple[bool, classroom.Classroom | None] = False, None |
| 80 | + |
73 | 81 | # Update attributes from entries dict: |
74 | 82 | entries.setdefault("name", entries.get("username")) |
75 | 83 | self.__dict__.update(entries) |
@@ -120,6 +128,42 @@ def _assert_permission(self): |
120 | 128 | raise exceptions.Unauthorized( |
121 | 129 | "You need to be authenticated as the profile owner to do this.") |
122 | 130 |
|
| 131 | + @property |
| 132 | + def classroom(self) -> classroom.Classroom | None: |
| 133 | + """ |
| 134 | + Get a user's associated classroom, and return it as a `scratchattach.classroom.Classroom` object. |
| 135 | + If there is no associated classroom, returns `None` |
| 136 | + """ |
| 137 | + if not self._classroom[0]: |
| 138 | + resp = requests.get(f"https://scratch.mit.edu/users/{self.username}/") |
| 139 | + soup = BeautifulSoup(resp.text, "html.parser") |
| 140 | + |
| 141 | + details = soup.find("p", {"class": "profile-details"}) |
| 142 | + |
| 143 | + class_name, class_id, is_closed = None, None, None |
| 144 | + for a in details.find_all("a"): |
| 145 | + href = a.get("href") |
| 146 | + if re.match(r"/classes/\d*/", href): |
| 147 | + class_name = a.text.strip()[len("Student of: "):] |
| 148 | + is_closed = class_name.endswith("\n (ended)") # as this has a \n, we can be sure |
| 149 | + if is_closed: |
| 150 | + class_name = class_name[:-7].strip() |
| 151 | + |
| 152 | + class_id = href.split('/')[2] |
| 153 | + break |
| 154 | + |
| 155 | + if class_name: |
| 156 | + self._classroom = True, classroom.Classroom( |
| 157 | + _session=self, |
| 158 | + id=class_id, |
| 159 | + title=class_name, |
| 160 | + is_closed=is_closed |
| 161 | + ) |
| 162 | + else: |
| 163 | + self._classroom = True, None |
| 164 | + |
| 165 | + return self._classroom[1] |
| 166 | + |
123 | 167 | def does_exist(self): |
124 | 168 | """ |
125 | 169 | Returns: |
|
0 commit comments