e2xauthoring 0.1.0__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.
@@ -0,0 +1,68 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+
6
+ # This workflow helps you trigger a SonarCloud analysis of your code and populates
7
+ # GitHub Code Scanning alerts with the vulnerabilities found.
8
+ # Free for open source project.
9
+
10
+ # 1. Login to SonarCloud.io using your GitHub account
11
+
12
+ # 2. Import your project on SonarCloud
13
+ # * Add your GitHub organization first, then add your repository as a new project.
14
+ # * Please note that many languages are eligible for automatic analysis,
15
+ # which means that the analysis will start automatically without the need to set up GitHub Actions.
16
+ # * This behavior can be changed in Administration > Analysis Method.
17
+ #
18
+ # 3. Follow the SonarCloud in-product tutorial
19
+ # * a. Copy/paste the Project Key and the Organization Key into the args parameter below
20
+ # (You'll find this information in SonarCloud. Click on "Information" at the bottom left)
21
+ #
22
+ # * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN
23
+ # (On SonarCloud, click on your avatar on top-right > My account > Security
24
+ # or go directly to https://sonarcloud.io/account/security/)
25
+
26
+ # Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/)
27
+ # or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9)
28
+
29
+ name: SonarCloud analysis
30
+
31
+ on:
32
+ push:
33
+ branches: [ "main", "dev" ]
34
+ pull_request:
35
+ branches: [ "main", "dev" ]
36
+ workflow_dispatch:
37
+
38
+ permissions:
39
+ pull-requests: read # allows SonarCloud to decorate PRs with analysis results
40
+
41
+ jobs:
42
+ Analysis:
43
+ runs-on: ubuntu-latest
44
+
45
+ steps:
46
+ - name: Analyze with SonarCloud
47
+
48
+ # You can pin the exact commit or the version.
49
+ # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049
50
+ uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049
51
+ env:
52
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information
53
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret)
54
+ with:
55
+ # Additional arguments for the sonarcloud scanner
56
+ args:
57
+ # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu)
58
+ # mandatory
59
+ -Dsonar.projectKey=DigiKlausur_e2xauthoring
60
+ -Dsonar.organization=digiklausur
61
+ # Comma-separated paths to directories containing main source files.
62
+ #-Dsonar.sources= # optional, default is project base directory
63
+ # When you need the analysis to take place in a directory other than the one from which it was launched
64
+ #-Dsonar.projectBaseDir= # optional, default is .
65
+ # Comma-separated paths to directories containing test source files.
66
+ #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/
67
+ # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing.
68
+ #-Dsonar.verbose= # optional, default is false
@@ -0,0 +1,133 @@
1
+ # Dynamic files
2
+ e2xauthoring/app/templates
3
+
4
+ # Byte-compiled / optimized / DLL files
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+
9
+ # C extensions
10
+ *.so
11
+
12
+ # Distribution / packaging
13
+ .Python
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ pip-wheel-metadata/
27
+ share/python-wheels/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+ MANIFEST
32
+
33
+ # PyInstaller
34
+ # Usually these files are written by a python script from a template
35
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
36
+ *.manifest
37
+ *.spec
38
+
39
+ # Installer logs
40
+ pip-log.txt
41
+ pip-delete-this-directory.txt
42
+
43
+ # Unit test / coverage reports
44
+ htmlcov/
45
+ .tox/
46
+ .nox/
47
+ .coverage
48
+ .coverage.*
49
+ .cache
50
+ nosetests.xml
51
+ coverage.xml
52
+ *.cover
53
+ *.py,cover
54
+ .hypothesis/
55
+ .pytest_cache/
56
+
57
+ # Translations
58
+ *.mo
59
+ *.pot
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+ db.sqlite3
65
+ db.sqlite3-journal
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98
+ __pypackages__/
99
+
100
+ # Celery stuff
101
+ celerybeat-schedule
102
+ celerybeat.pid
103
+
104
+ # SageMath parsed files
105
+ *.sage.py
106
+
107
+ # Environments
108
+ .env
109
+ .venv
110
+ env/
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
133
+ node_modules
@@ -0,0 +1,20 @@
1
+ repos:
2
+ - repo: https://github.com/psf/black
3
+ rev: 22.6.0
4
+ hooks:
5
+ - id: black
6
+
7
+ - repo: https://github.com/PyCQA/isort
8
+ rev: 5.10.1
9
+ hooks:
10
+ - id: isort
11
+ files: \.py$
12
+ args: [--profile=black]
13
+
14
+ - repo: local
15
+ hooks:
16
+ - id: prettier
17
+ name: prettier
18
+ entry: 'npm run prettier:files'
19
+ language: node
20
+ types_or: [json, markdown, ts, tsx, javascript, jsx, css]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Tim Metzler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ recursive-include e2xauthoring/app/templates *
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.1
2
+ Name: e2xauthoring
3
+ Version: 0.1.0
4
+ Summary: Authoring app for e2xgrader
5
+ Project-URL: Source, https://github.com/Digiklausur/e2xauthoring
6
+ Author-email: Tim Metzler <tim.metzler@h-brs.de>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.7
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Requires-Python: >=3.7
15
+ Requires-Dist: e2xgrader==0.1.*
16
+ Requires-Dist: gitpython
17
+ Provides-Extra: dev
18
+ Requires-Dist: hatchling; extra == 'dev'
19
+ Requires-Dist: pre-commit; extra == 'dev'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # e2xauthoring
23
+
24
+ ---
25
+
26
+ This is an authoring component for e2xgrader
@@ -0,0 +1,5 @@
1
+ # e2xauthoring
2
+
3
+ ---
4
+
5
+ This is an authoring component for e2xgrader
File without changes
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0dev0"
@@ -0,0 +1,3 @@
1
+ from .authoring import AuthoringApp
2
+
3
+ __all__ = ["AuthoringApp"]
@@ -0,0 +1,225 @@
1
+ import json
2
+
3
+ from e2xgrader.converters import GenerateExercise
4
+ from e2xgrader.models import AssignmentModel, TemplateModel
5
+ from e2xgrader.server_extensions.grader.apps.authoring.apihandlers import (
6
+ BaseApiListHandler,
7
+ BaseApiManageHandler,
8
+ E2xApiHandler,
9
+ ExerciseModel,
10
+ KernelSpecHandler,
11
+ PresetHandler,
12
+ TemplateVariableHandler,
13
+ api_url,
14
+ assignment_regex,
15
+ name_regex,
16
+ pool_regex,
17
+ )
18
+ from e2xgrader.utils import urljoin
19
+ from nbgrader.server_extensions.formgrader.base import check_xsrf
20
+ from tornado import web
21
+
22
+ from e2xauthoring.utils import get_author, set_author
23
+
24
+ from ..models import TaskModel, TaskPoolModel
25
+
26
+
27
+ class ManagePoolsHandler(E2xApiHandler):
28
+ def initialize(self) -> None:
29
+ self.__poolmodel = TaskPoolModel(self.coursedir)
30
+
31
+ @web.authenticated
32
+ @check_xsrf
33
+ def get(self, action):
34
+ if action == "list":
35
+ self.write(json.dumps(self.__poolmodel.list()))
36
+ elif action == "get":
37
+ self.write(json.dumps(self.__poolmodel.get(pool=self.get_argument("pool"))))
38
+ else:
39
+ self.write(
40
+ json.dumps(
41
+ dict(status="error", message=f"{action} is not a valid action")
42
+ )
43
+ )
44
+
45
+ @web.authenticated
46
+ @check_xsrf
47
+ def post(self, action):
48
+ data = self.get_json_body()
49
+ name = data.get("name")
50
+ if action == "new":
51
+ create_repository = data.get("create_repository", False)
52
+ self.write(
53
+ json.dumps(
54
+ self.__poolmodel.new(name=name, create_repository=create_repository)
55
+ )
56
+ )
57
+ elif action == "remove":
58
+ self.write(json.dumps(self.__poolmodel.remove(name=name)))
59
+ elif action == "init_repo":
60
+ self.write(json.dumps(self.__poolmodel.turn_into_repository(name)))
61
+ else:
62
+ self.write(
63
+ json.dumps(
64
+ dict(status="error", message=f"{action} is not a valid action")
65
+ )
66
+ )
67
+
68
+
69
+ class ManageTasksHandler(E2xApiHandler):
70
+ def initialize(self) -> None:
71
+ self.__model = TaskModel(self.coursedir)
72
+
73
+ @web.authenticated
74
+ @check_xsrf
75
+ def get(self, action):
76
+ if action == "list":
77
+ self.write(json.dumps(self.__model.list(pool=self.get_argument("pool"))))
78
+ elif action == "list_all":
79
+ tasks = []
80
+ pool_model = TaskPoolModel(self.coursedir)
81
+ for pool in pool_model.list():
82
+ tasks.extend(self.__model.list(pool["name"]))
83
+ self.write(json.dumps(tasks))
84
+ elif action == "get":
85
+ self.write(
86
+ json.dumps(
87
+ self.__model.get(
88
+ pool=self.get_argument("pool"), name=self.get_argument("name")
89
+ )
90
+ )
91
+ )
92
+ elif action == "diff":
93
+ self.write(
94
+ json.dumps(
95
+ self.__model.git_diff(
96
+ pool=self.get_argument("pool"),
97
+ task=self.get_argument("task"),
98
+ file=self.get_argument("file"),
99
+ )
100
+ )
101
+ )
102
+ else:
103
+ self.write(
104
+ json.dumps(
105
+ dict(status="error", message=f"{action} is not a valid action")
106
+ )
107
+ )
108
+
109
+ @web.authenticated
110
+ @check_xsrf
111
+ def post(self, action):
112
+ data = self.get_json_body()
113
+ name = data.get("name")
114
+ pool = data.get("pool")
115
+ if action == "new":
116
+ self.write(json.dumps(self.__model.new(pool=pool, name=name)))
117
+ elif action == "remove":
118
+ self.write(json.dumps(self.__model.remove(pool=pool, name=name)))
119
+ elif action == "commit":
120
+ message = data.get("message", None)
121
+ self.write(
122
+ json.dumps(self.__model.commit(pool=pool, task=name, message=message))
123
+ )
124
+ else:
125
+ self.write(
126
+ json.dumps(
127
+ dict(status="error", message=f"{action} is not a valid action")
128
+ )
129
+ )
130
+
131
+
132
+ class ManageTemplatesHandler(E2xApiHandler):
133
+ def initialize(self) -> None:
134
+ self.__model = TemplateModel(self.coursedir)
135
+
136
+ @web.authenticated
137
+ @check_xsrf
138
+ def get(self, action):
139
+ if action == "list":
140
+ self.write(json.dumps(self.__model.list()))
141
+ else:
142
+ self.write(
143
+ json.dumps(
144
+ dict(status="error", message=f"{action} is not a valid action")
145
+ )
146
+ )
147
+
148
+ @web.authenticated
149
+ @check_xsrf
150
+ def post(self, action):
151
+ data = self.get_json_body()
152
+ name = data.get("name")
153
+ if action == "new":
154
+ self.write(json.dumps(self.__model.new(name=name)))
155
+ elif action == "remove":
156
+ self.write(json.dumps(self.__model.remove(name=name)))
157
+ else:
158
+ self.write(
159
+ json.dumps(
160
+ dict(status="error", message=f"{action} is not a valid action")
161
+ )
162
+ )
163
+
164
+
165
+ class GenerateExerciseHandler(E2xApiHandler):
166
+ @web.authenticated
167
+ @check_xsrf
168
+ def post(self):
169
+ GenerateExercise(coursedir=self.coursedir).convert(self.get_json_body())
170
+ self.write(dict(success=True))
171
+
172
+
173
+ class GitAuthorHandler(E2xApiHandler):
174
+ @web.authenticated
175
+ @check_xsrf
176
+ def get(self):
177
+ self.write(json.dumps(get_author()))
178
+
179
+ @web.authenticated
180
+ @check_xsrf
181
+ def post(self):
182
+ data = self.get_json_body()
183
+ name = data.get("name")
184
+ email = data.get("email")
185
+ self.write(json.dumps(set_author(name=name, email=email)))
186
+
187
+
188
+ pool_action_regex = r"(?P<action>new|list|remove|get|init_repo)"
189
+ task_action_regex = r"(?P<action>new|list|list_all|remove|commit|get|diff)"
190
+ template_action_regex = r"(?P<action>new|list|remove)"
191
+ default_handlers = [
192
+ (urljoin(api_url, "presets"), PresetHandler),
193
+ (
194
+ urljoin(api_url, "assignments", "?"),
195
+ BaseApiListHandler,
196
+ dict(model_cls=AssignmentModel),
197
+ ),
198
+ (
199
+ urljoin(api_url, "template", name_regex, "?"),
200
+ BaseApiManageHandler,
201
+ dict(model_cls=TemplateModel),
202
+ ),
203
+ (
204
+ urljoin(api_url, "templates", "?"),
205
+ BaseApiListHandler,
206
+ dict(model_cls=TemplateModel),
207
+ ),
208
+ (
209
+ urljoin(api_url, "worksheets", assignment_regex, name_regex, "?"),
210
+ BaseApiManageHandler,
211
+ dict(model_cls=ExerciseModel),
212
+ ),
213
+ (
214
+ urljoin(api_url, "worksheets", assignment_regex, "?"),
215
+ BaseApiListHandler,
216
+ dict(model_cls=ExerciseModel),
217
+ ),
218
+ (urljoin(api_url, "pools", pool_action_regex, "?"), ManagePoolsHandler),
219
+ (urljoin(api_url, "tasks", task_action_regex, "?"), ManageTasksHandler),
220
+ (urljoin(api_url, "templates", template_action_regex, "?"), ManageTemplatesHandler),
221
+ (urljoin(api_url, "templates", "variables"), TemplateVariableHandler),
222
+ (urljoin(api_url, "kernelspec"), KernelSpecHandler),
223
+ (urljoin(api_url, "generate_worksheet"), GenerateExerciseHandler),
224
+ (urljoin(api_url, "git", "author"), GitAuthorHandler),
225
+ ]
@@ -0,0 +1,33 @@
1
+ import os
2
+
3
+ from e2xgrader.server_extensions.grader.apps.base import BaseApp
4
+ from nbgrader.apps.baseapp import NbGrader
5
+ from tornado import web
6
+
7
+ from .apihandlers import default_handlers
8
+ from .handlers import default_handlers as handlers
9
+
10
+
11
+ class AuthoringApp(NbGrader, BaseApp):
12
+
13
+ template_path = os.path.join(os.path.dirname(__file__), "templates")
14
+ static_path = os.path.join(template_path, "authoring", "static")
15
+
16
+ def __init__(self, **kwargs) -> None:
17
+ NbGrader.__init__(self, **kwargs)
18
+ BaseApp.__init__(self, **kwargs)
19
+
20
+ def load_app(self) -> None:
21
+ self.log.info("Loading the e2xgrader git authoring app")
22
+ self.add_template_path(self.template_path)
23
+ self.add_handlers(default_handlers)
24
+ self.add_handlers(
25
+ [
26
+ (
27
+ r"/e2x/authoring/static/(.*)",
28
+ web.StaticFileHandler,
29
+ dict(path=self.static_path),
30
+ )
31
+ ]
32
+ )
33
+ self.add_handlers(handlers)
@@ -0,0 +1,30 @@
1
+ import os
2
+ import sys
3
+ from email.policy import default
4
+
5
+ from e2xgrader.server_extensions.grader.apps.authoring.handlers import app_url
6
+ from e2xgrader.utils import urljoin
7
+ from nbgrader.server_extensions.formgrader.base import (
8
+ BaseHandler,
9
+ check_notebook_dir,
10
+ check_xsrf,
11
+ )
12
+ from tornado import web
13
+
14
+
15
+ class AuthoringHandler(BaseHandler):
16
+ @web.authenticated
17
+ @check_xsrf
18
+ @check_notebook_dir
19
+ def get(self):
20
+ self.write(
21
+ self.render(
22
+ os.path.join("authoring", "index.html"),
23
+ url_prefix=self.url_prefix,
24
+ base_url=self.base_url,
25
+ windows=(sys.prefix == "win32"),
26
+ )
27
+ )
28
+
29
+
30
+ default_handlers = [(urljoin(app_url, "?.*"), AuthoringHandler)]