qcanvas 0.0.5a0__tar.gz → 0.0.5.2a0__tar.gz

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.

Files changed (63) hide show
  1. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/PKG-INFO +1 -1
  2. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/pyproject.toml +1 -1
  3. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/net/canvas/canvas_client.py +21 -0
  4. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/container_item.py +1 -5
  5. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/main_ui.py +40 -13
  6. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/viewer/course_list.py +1 -0
  7. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/viewer/page_list_viewer.py +3 -1
  8. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/course_indexer/data_manager.py +4 -4
  9. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/.gitignore +0 -0
  10. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/README.md +0 -0
  11. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/__init__.py +0 -0
  12. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/__main__.py +0 -0
  13. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/db/__init__.py +0 -0
  14. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/db/database.py +0 -0
  15. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/db/db_converter_helper.py +0 -0
  16. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/icons/__init__.py +0 -0
  17. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/icons/icons.qrc +0 -0
  18. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/icons/main_icon.svg +0 -0
  19. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/icons/rc_icons.py +0 -0
  20. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/net/__init__.py +0 -0
  21. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/net/canvas/__init__.py +0 -0
  22. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/net/canvas/legacy_canvas_types.py +0 -0
  23. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/net/custom_httpx_async_transport.py +0 -0
  24. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/net/self_authenticating.py +0 -0
  25. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/queries/__init__.py +0 -0
  26. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/queries/all_courses.gql +0 -0
  27. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/queries/all_courses.py +0 -0
  28. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/queries/canvas_course_data.gql +0 -0
  29. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/queries/canvas_course_data.py +0 -0
  30. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/__init__.py +0 -0
  31. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/menu_bar/__init__.py +0 -0
  32. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/menu_bar/grouping_preferences_menu.py +0 -0
  33. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/menu_bar/theme_selection_menu.py +0 -0
  34. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/setup_dialog.py +0 -0
  35. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/status_bar_reporter.py +0 -0
  36. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/viewer/__init__.py +0 -0
  37. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/viewer/file_list.py +0 -0
  38. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/ui/viewer/file_view_tab.py +0 -0
  39. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/__init__.py +0 -0
  40. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/app_settings.py +0 -0
  41. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/constants.py +0 -0
  42. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/course_indexer/__init__.py +0 -0
  43. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/course_indexer/conversion_helpers.py +0 -0
  44. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/course_indexer/resource_helpers.py +0 -0
  45. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/download_pool.py +0 -0
  46. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/helpers/__init__.py +0 -0
  47. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/helpers/canvas_sanitiser.py +0 -0
  48. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/helpers/file_icon_helper.py +0 -0
  49. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/helpers/qaction_helper.py +0 -0
  50. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/helpers/theme_helper.py +0 -0
  51. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/linkscanner/__init__.py +0 -0
  52. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/linkscanner/canvas_link_scanner.py +0 -0
  53. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/linkscanner/canvas_media_object_scanner.py +0 -0
  54. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/linkscanner/dropbox_scanner.py +0 -0
  55. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/linkscanner/resource_scanner.py +0 -0
  56. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/progress_reporter.py +0 -0
  57. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/self_updater.py +0 -0
  58. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/task_pool.py +0 -0
  59. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/tree_util/__init__.py +0 -0
  60. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/tree_util/expanding_tree.py +0 -0
  61. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/tree_util/model_helpers.py +0 -0
  62. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/qcanvas/util/tree_util/tree_model.py +0 -0
  63. {qcanvas-0.0.5a0 → qcanvas-0.0.5.2a0}/requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qcanvas
3
- Version: 0.0.5a0
3
+ Version: 0.0.5.2a0
4
4
  Summary: A canvas client
5
5
  Author: QCanvas
6
6
  Classifier: Operating System :: OS Independent
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name="qcanvas"
7
- version = "0.0.5a"
7
+ version = "0.0.5.2a"
8
8
  authors= [ {name="QCanvas"} ]
9
9
  description="A canvas client"
10
10
  requires-python=">=3.11,<=3.12"
@@ -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),
@@ -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)
@@ -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
- h_layout = QHBoxLayout()
66
- h_layout.addWidget(self.course_list)
67
- h_layout.addWidget(self.tab_widget)
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
- v_layout = QVBoxLayout()
71
- v_layout.addLayout(h_layout)
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(v_layout)
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(ThemeSelectionMenu())
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):
@@ -56,6 +56,7 @@ class CourseList(QTreeView):
56
56
  for term, courses in self.group_courses_by_term(courses):
57
57
  term_node = QStandardItem(term.name)
58
58
  term_node.setEditable(False)
59
+ term_node.setSelectable(False)
59
60
 
60
61
  for course in courses:
61
62
  course_node = CourseNode(course)
@@ -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 = canvas_garbage_remover.remove_stylesheets_from_html(item.content)
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._client = client
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._client.do_graphql_query(gql(queries.all_courses.DEFINITION), detailed=True))
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._client.get_file(file.m_id, course_id)
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._client.get_page(page.m_id, course_id)
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)
File without changes
File without changes
File without changes