qcanvas 0.0.5a0__py3-none-any.whl → 0.0.5.2a0__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.
Potentially problematic release.
This version of qcanvas might be problematic. Click here for more details.
- qcanvas/net/canvas/canvas_client.py +21 -0
- qcanvas/ui/container_item.py +1 -5
- qcanvas/ui/main_ui.py +40 -13
- qcanvas/ui/viewer/course_list.py +1 -0
- qcanvas/ui/viewer/page_list_viewer.py +3 -1
- qcanvas/util/course_indexer/data_manager.py +4 -4
- {qcanvas-0.0.5a0.dist-info → qcanvas-0.0.5.2a0.dist-info}/METADATA +1 -1
- {qcanvas-0.0.5a0.dist-info → qcanvas-0.0.5.2a0.dist-info}/RECORD +9 -9
- {qcanvas-0.0.5a0.dist-info → qcanvas-0.0.5.2a0.dist-info}/WHEEL +0 -0
|
@@ -132,6 +132,27 @@ class CanvasClient(SelfAuthenticating):
|
|
|
132
132
|
|
|
133
133
|
return LegacyFile.from_dict(json.loads(response.text))
|
|
134
134
|
|
|
135
|
+
@retry(
|
|
136
|
+
wait=wait_exponential(exp_base=1.2, max=10) + wait_random(0, 1),
|
|
137
|
+
retry=retry_if_exception_type(RatelimitedException),
|
|
138
|
+
stop=stop_after_attempt(8)
|
|
139
|
+
)
|
|
140
|
+
async def get_temp_session_link(self) -> str | None:
|
|
141
|
+
"""
|
|
142
|
+
Gets the link which will authenticate a browser/open canvas using a 'legacy session token'
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
str | None
|
|
146
|
+
The url as a string if the request succeeded, None if it didn't
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
token_response = await self.client.get(self.canvas_url.join("login/session_token"), **self.get_headers())
|
|
150
|
+
|
|
151
|
+
if token_response.is_success:
|
|
152
|
+
return json.loads(token_response.text)["session_url"]
|
|
153
|
+
else:
|
|
154
|
+
return None
|
|
155
|
+
|
|
135
156
|
@retry(
|
|
136
157
|
stop=stop_after_attempt(3),
|
|
137
158
|
wait=wait_fixed(5) + wait_random(0, 1),
|
qcanvas/ui/container_item.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from PySide6.QtCore import Qt
|
|
2
1
|
from PySide6.QtGui import QStandardItem
|
|
3
2
|
|
|
4
3
|
from qcanvas.util import tree_util as tree
|
|
@@ -9,7 +8,4 @@ class ContainerItem(QStandardItem):
|
|
|
9
8
|
super().__init__()
|
|
10
9
|
self.content = data
|
|
11
10
|
self.setEditable(False)
|
|
12
|
-
|
|
13
|
-
def data(self, role=257):
|
|
14
|
-
if role == Qt.ItemDataRole.DisplayRole:
|
|
15
|
-
return self.content.text
|
|
11
|
+
self.setText(data.text)
|
qcanvas/ui/main_ui.py
CHANGED
|
@@ -3,8 +3,8 @@ import sys
|
|
|
3
3
|
import traceback
|
|
4
4
|
from typing import Sequence, Optional
|
|
5
5
|
|
|
6
|
-
from PySide6.QtCore import Slot, Signal, Qt, QUrl
|
|
7
|
-
from PySide6.QtGui import QDesktopServices
|
|
6
|
+
from PySide6.QtCore import Slot, Signal, Qt, QUrl, QObject
|
|
7
|
+
from PySide6.QtGui import QDesktopServices, QKeySequence
|
|
8
8
|
from PySide6.QtWidgets import *
|
|
9
9
|
from qasync import asyncSlot
|
|
10
10
|
|
|
@@ -20,9 +20,10 @@ from qcanvas.util import self_updater
|
|
|
20
20
|
from qcanvas.util.app_settings import settings
|
|
21
21
|
from qcanvas.util.constants import app_name
|
|
22
22
|
from qcanvas.util.course_indexer import DataManager
|
|
23
|
+
from qcanvas.util.helpers.qaction_helper import create_qaction
|
|
23
24
|
|
|
24
25
|
_aux_settings = settings.auxiliary
|
|
25
|
-
|
|
26
|
+
_no_course_selected_text = "No course selected"
|
|
26
27
|
|
|
27
28
|
class AppMainWindow(QMainWindow):
|
|
28
29
|
logger = logging.getLogger()
|
|
@@ -62,17 +63,15 @@ class AppMainWindow(QMainWindow):
|
|
|
62
63
|
self.tab_widget.insertTab(1, self.assignment_viewer, "Assignments")
|
|
63
64
|
self.tab_widget.insertTab(2, self.pages_viewer, "Pages")
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
h_layout.setStretch(1, 1)
|
|
66
|
+
self.course_name_label = QLabel(_no_course_selected_text)
|
|
67
|
+
self.course_name_label.setStyleSheet("font-weight: bold;")
|
|
68
|
+
course_stack_layout = self.create_layout_and_add_widgets(QVBoxLayout, self.course_name_label, self.tab_widget)
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
v_layout.addWidget(self.sync_button)
|
|
70
|
+
h_layout = self.create_layout_and_add_widgets(QHBoxLayout, self.course_list, course_stack_layout)
|
|
71
|
+
h_layout.setStretch(1, 1)
|
|
73
72
|
|
|
74
73
|
widget = QWidget()
|
|
75
|
-
widget.setLayout(
|
|
74
|
+
widget.setLayout(self.create_layout_and_add_widgets(QVBoxLayout, h_layout, self.sync_button))
|
|
76
75
|
self.setCentralWidget(widget)
|
|
77
76
|
|
|
78
77
|
self.setup_menu_bar()
|
|
@@ -90,12 +89,26 @@ class AppMainWindow(QMainWindow):
|
|
|
90
89
|
# Set its height so it doesn't get bigger when there's a progress bar in it
|
|
91
90
|
bar.setFixedHeight(bar.height())
|
|
92
91
|
|
|
92
|
+
@staticmethod
|
|
93
|
+
def create_layout_and_add_widgets(layout_type: type, *widgets) -> QLayout:
|
|
94
|
+
layout = layout_type()
|
|
95
|
+
|
|
96
|
+
for widget in widgets:
|
|
97
|
+
if isinstance(widget, QLayout):
|
|
98
|
+
layout.addLayout(widget)
|
|
99
|
+
else:
|
|
100
|
+
layout.addWidget(widget)
|
|
101
|
+
|
|
102
|
+
return layout
|
|
103
|
+
|
|
93
104
|
def setup_menu_bar(self):
|
|
94
105
|
menu_bar = self.menuBar()
|
|
95
106
|
|
|
96
|
-
menu_bar.addMenu(
|
|
97
|
-
view_menu = menu_bar.addMenu("View")
|
|
107
|
+
app_menu: QMenu = menu_bar.addMenu("App")
|
|
108
|
+
view_menu: QMenu = menu_bar.addMenu("View")
|
|
98
109
|
|
|
110
|
+
app_menu.addAction(self.setup_quick_authentication_action(app_menu))
|
|
111
|
+
app_menu.addMenu(ThemeSelectionMenu())
|
|
99
112
|
view_menu.addMenu(self.setup_group_by_menu())
|
|
100
113
|
|
|
101
114
|
def setup_group_by_menu(self) -> QMenu:
|
|
@@ -105,6 +118,18 @@ class AppMainWindow(QMainWindow):
|
|
|
105
118
|
|
|
106
119
|
return file_grouping_menu
|
|
107
120
|
|
|
121
|
+
def setup_quick_authentication_action(self, parent: QObject):
|
|
122
|
+
return create_qaction(
|
|
123
|
+
name="Quick canvas login",
|
|
124
|
+
shortcut=QKeySequence("Ctrl+O"),
|
|
125
|
+
triggered=self.open_quick_auth_in_browser,
|
|
126
|
+
parent=parent
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@asyncSlot()
|
|
130
|
+
async def open_quick_auth_in_browser(self):
|
|
131
|
+
QDesktopServices.openUrl(await self.data_manager.client.get_temp_session_link())
|
|
132
|
+
|
|
108
133
|
def closeEvent(self, event):
|
|
109
134
|
settings.geometry = self.saveGeometry()
|
|
110
135
|
settings.window_state = self.saveState()
|
|
@@ -205,9 +230,11 @@ class AppMainWindow(QMainWindow):
|
|
|
205
230
|
self.pages_viewer.fill_tree(course)
|
|
206
231
|
self.assignment_viewer.fill_tree(course)
|
|
207
232
|
self.file_viewer.load_course_files(course)
|
|
233
|
+
self.course_name_label.setText(course.name)
|
|
208
234
|
else:
|
|
209
235
|
self.selected_course = None
|
|
210
236
|
self.file_viewer.clear()
|
|
237
|
+
self.course_name_label.setText(_no_course_selected_text)
|
|
211
238
|
|
|
212
239
|
@asyncSlot(db.CoursePreferences)
|
|
213
240
|
async def on_grouping_preference_changed(self):
|
qcanvas/ui/viewer/course_list.py
CHANGED
|
@@ -10,6 +10,7 @@ import qcanvas.db as db
|
|
|
10
10
|
from qcanvas.ui.container_item import ContainerItem
|
|
11
11
|
from qcanvas.util.constants import default_assignments_module_names
|
|
12
12
|
from qcanvas.util.course_indexer import resource_helpers
|
|
13
|
+
from qcanvas.util.helpers import canvas_sanitiser
|
|
13
14
|
from qcanvas.util.linkscanner import ResourceScanner
|
|
14
15
|
|
|
15
16
|
|
|
@@ -97,7 +98,7 @@ class PageLikeViewer(QWidget):
|
|
|
97
98
|
return
|
|
98
99
|
|
|
99
100
|
# todo when a file is finished downloading it would be nice if the page was refreshed to show the state properly
|
|
100
|
-
html =
|
|
101
|
+
html = canvas_sanitiser.remove_stylesheets_from_html(item.content)
|
|
101
102
|
self.viewer.setHtml(self.link_transformer.transform_links(html))
|
|
102
103
|
|
|
103
104
|
|
|
@@ -113,6 +114,7 @@ class PagesViewer(PageLikeViewer):
|
|
|
113
114
|
continue
|
|
114
115
|
|
|
115
116
|
module_node = ContainerItem(module)
|
|
117
|
+
module_node.setSelectable(False)
|
|
116
118
|
|
|
117
119
|
for module_item in list[db.ModuleItem](module.items):
|
|
118
120
|
module_node.appendRow(ContainerItem(module_item))
|
|
@@ -83,7 +83,7 @@ class DataManager:
|
|
|
83
83
|
sessionmaker: AsyncSessionMaker,
|
|
84
84
|
link_scanners: Sequence[ResourceScanner]):
|
|
85
85
|
|
|
86
|
-
self.
|
|
86
|
+
self.client = client
|
|
87
87
|
self._link_scanners = link_scanners
|
|
88
88
|
self._session_maker = sessionmaker
|
|
89
89
|
|
|
@@ -188,7 +188,7 @@ class DataManager:
|
|
|
188
188
|
|
|
189
189
|
async def synchronize_with_canvas(self, progress_reporter: ProgressReporter = noop_reporter):
|
|
190
190
|
section = progress_reporter.section("Loading index", 0)
|
|
191
|
-
raw_query = (await self.
|
|
191
|
+
raw_query = (await self.client.do_graphql_query(gql(queries.all_courses.DEFINITION), detailed=True))
|
|
192
192
|
section.increment_progress()
|
|
193
193
|
|
|
194
194
|
await self.load_courses_data(queries.AllCoursesQueryData(**raw_query).all_courses, progress_reporter)
|
|
@@ -343,7 +343,7 @@ class DataManager:
|
|
|
343
343
|
Fetches information about the specified file from canvas
|
|
344
344
|
"""
|
|
345
345
|
_logger.debug(f"Fetching file (for module file) %s %s", file.m_id, file.display_name)
|
|
346
|
-
result = await self.
|
|
346
|
+
result = await self.client.get_file(file.m_id, course_id)
|
|
347
347
|
resource = db.convert_file(file, result.size)
|
|
348
348
|
resource.id = f"{canvas_resource_id_prefix}:{resource.id}"
|
|
349
349
|
resource.course_id = course_id
|
|
@@ -369,7 +369,7 @@ class DataManager:
|
|
|
369
369
|
|
|
370
370
|
try:
|
|
371
371
|
# Get the page
|
|
372
|
-
result = await self.
|
|
372
|
+
result = await self.client.get_page(page.m_id, course_id)
|
|
373
373
|
except BaseException as e:
|
|
374
374
|
# Handle any errors
|
|
375
375
|
_logger.error(e)
|
|
@@ -11,7 +11,7 @@ qcanvas/net/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
11
11
|
qcanvas/net/custom_httpx_async_transport.py,sha256=1OyszJO8TrwDrq-no-angb-AIjgq_GiV67tQHuPxmm0,1227
|
|
12
12
|
qcanvas/net/self_authenticating.py,sha256=bF0lq9BgDQg5We4l0Z_JpJhI8ZGC7TTgggMWHUOYw3U,2515
|
|
13
13
|
qcanvas/net/canvas/__init__.py,sha256=EPLFN05Vx9yNHDLK-eUp97C6ymtMTnscwdn28OBlNJw,96
|
|
14
|
-
qcanvas/net/canvas/canvas_client.py,sha256=
|
|
14
|
+
qcanvas/net/canvas/canvas_client.py,sha256=eENktdmVnyd6eIh6I4dH-5mU09pfJLo-v1xQ8Oma26k,9674
|
|
15
15
|
qcanvas/net/canvas/legacy_canvas_types.py,sha256=pKuTBh1m5oSq9e7jmbHzYfKeoPSw0uHAqLwddKZpsr4,3942
|
|
16
16
|
qcanvas/queries/__init__.py,sha256=tRUwMS1OvYIWRd4WpmAHrDrcelj_dLEaesxh9NngC6s,227
|
|
17
17
|
qcanvas/queries/all_courses.gql,sha256=8U0_3VOcD66SRCfvhZ9IOTmxVgDPJdMD_LuGPbM9vVU,124
|
|
@@ -19,18 +19,18 @@ qcanvas/queries/all_courses.py,sha256=-DpMy7eDFUeYp3ffZrwXp15qo_t6Lfx8K-rFi3OK1D
|
|
|
19
19
|
qcanvas/queries/canvas_course_data.gql,sha256=KJHjdu5sMKld4L6mPGfUrhqRLx_wdgGH8fwTnmRFGrI,1115
|
|
20
20
|
qcanvas/queries/canvas_course_data.py,sha256=_ZNv9stXAyap2eBbCqmajXVpWr0920ZUcCS6Gtyyo0g,4314
|
|
21
21
|
qcanvas/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
qcanvas/ui/container_item.py,sha256=
|
|
23
|
-
qcanvas/ui/main_ui.py,sha256=
|
|
22
|
+
qcanvas/ui/container_item.py,sha256=MNGL3Z6dNlM59XXWqmXB1Ql3FTOEdA6TY2K9Do1LmTE,285
|
|
23
|
+
qcanvas/ui/main_ui.py,sha256=2vpBJ_5DZit8ZYo-fIWk7x9zRarVwO-zu1cnXm5QMMc,9824
|
|
24
24
|
qcanvas/ui/setup_dialog.py,sha256=BXDNDd5IjA0l-9teUYsOCYMRXxUl4Ryxx64GsxJX5xU,7261
|
|
25
25
|
qcanvas/ui/status_bar_reporter.py,sha256=6Qp0i-vUq9Yxowx0BSB4ieR8BO37iKjRNvPQ1AHUXtI,1318
|
|
26
26
|
qcanvas/ui/menu_bar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
27
|
qcanvas/ui/menu_bar/grouping_preferences_menu.py,sha256=nSe9qt3pI9WAiKVTOEtHN9JPW3gaDxpU8YiSGGRjR5M,2407
|
|
28
28
|
qcanvas/ui/menu_bar/theme_selection_menu.py,sha256=WdcFMEO-FRYJm3qKjYbUpRhJPVe0-RhwrVxcyckSr0Q,1248
|
|
29
29
|
qcanvas/ui/viewer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
qcanvas/ui/viewer/course_list.py,sha256=
|
|
30
|
+
qcanvas/ui/viewer/course_list.py,sha256=jg_t66uyZpWyBITRWBFHaYgYMvWJ5useMljWv4tfOx4,3413
|
|
31
31
|
qcanvas/ui/viewer/file_list.py,sha256=hqKXrLs4bLHA9himFEuE8d8Pq2TJnvuUGGJgZftiyrA,7869
|
|
32
32
|
qcanvas/ui/viewer/file_view_tab.py,sha256=DrXwHwIPBoHVjsKU3zUbOvA4tTarp29d4cMYvZnE-O8,2205
|
|
33
|
-
qcanvas/ui/viewer/page_list_viewer.py,sha256
|
|
33
|
+
qcanvas/ui/viewer/page_list_viewer.py,sha256=-5wtDEX6TMSMNYyCxWAyf2nIBuIEPSgcmSaiDNcWQrQ,5535
|
|
34
34
|
qcanvas/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
qcanvas/util/app_settings.py,sha256=B9hIfl6wQZkBflM1_UwpCEd935k7sckegXXwKMbSnYo,2940
|
|
36
36
|
qcanvas/util/constants.py,sha256=GNRlqG9rnWlW5ijKoJEsyuOWTdrXNSiWL_a5XeEZzI0,259
|
|
@@ -40,7 +40,7 @@ qcanvas/util/self_updater.py,sha256=oItk-J-CSN74RGu4P_xtJ4S8vGnaAZL23Bm4jpLbLzg,
|
|
|
40
40
|
qcanvas/util/task_pool.py,sha256=kizChooliF7O9w5LtgBd87LHT_M1aGXiPoJ-M8Lg2RY,8159
|
|
41
41
|
qcanvas/util/course_indexer/__init__.py,sha256=IqwTfB8KVk7Go_yuBL8NOANgFiBCzjt0z38D4CEgq7w,38
|
|
42
42
|
qcanvas/util/course_indexer/conversion_helpers.py,sha256=DZwx3Ct-MYzxQwuvqem5c2cARSkr_2SJvz0gaZDJq6c,2644
|
|
43
|
-
qcanvas/util/course_indexer/data_manager.py,sha256=
|
|
43
|
+
qcanvas/util/course_indexer/data_manager.py,sha256=AsDP-PLzFquL-d0l9uFyAihcfLbRGuCYoQV4uySk_uE,17045
|
|
44
44
|
qcanvas/util/course_indexer/resource_helpers.py,sha256=54XnTbcq8RG1zSDifycSYdpQ007_aQrFFXkibMO7l8k,6794
|
|
45
45
|
qcanvas/util/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
46
|
qcanvas/util/helpers/canvas_sanitiser.py,sha256=c6r6cyJomQ1zn5wFK-LX-ZsZXQZ-A2awBFtCB_JqUtE,1184
|
|
@@ -56,6 +56,6 @@ qcanvas/util/tree_util/__init__.py,sha256=yqjNHrD8SiyW_fGPfw7xdS-Rctjdu-RMWSkUxc
|
|
|
56
56
|
qcanvas/util/tree_util/expanding_tree.py,sha256=5UM9kfcHtVavzAc9nwd7lucVnjI_dkVH8qC3GvmBvtc,6622
|
|
57
57
|
qcanvas/util/tree_util/model_helpers.py,sha256=io1bHOMNgVqkPvGH2Iohug1-wx2MwC_-qlWlpVA0fFE,743
|
|
58
58
|
qcanvas/util/tree_util/tree_model.py,sha256=JhlYmGqxlul8jUUQ-KtPXIehzgG-UtszHve7Wh-XbtE,2981
|
|
59
|
-
qcanvas-0.0.
|
|
60
|
-
qcanvas-0.0.
|
|
61
|
-
qcanvas-0.0.
|
|
59
|
+
qcanvas-0.0.5.2a0.dist-info/METADATA,sha256=sYVc14zPHkIRzrCAslhOP8ABZspmiU-ciMj4sAskgQ4,676
|
|
60
|
+
qcanvas-0.0.5.2a0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
|
|
61
|
+
qcanvas-0.0.5.2a0.dist-info/RECORD,,
|
|
File without changes
|