-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #177 from DigiKlausur/refactor_e2xgrader_apps
Refactor e2xgrader apps
- Loading branch information
Showing
23 changed files
with
870 additions
and
183 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from .activatemodeapp import ActivateModeApp | ||
from .baseapp import E2xGrader | ||
from .deactivatemodeapp import DeactivateModeApp | ||
from .e2xgraderapp import E2xGraderApp | ||
from .showmodeapp import ShowModeApp | ||
from .togglemodeapp import ToggleModeApp | ||
|
||
__all__ = [ | ||
"E2xGrader", | ||
"E2xGraderApp", | ||
"ActivateModeApp", | ||
"DeactivateModeApp", | ||
"ShowModeApp", | ||
"ToggleModeApp", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from ..utils.mode import E2xGraderMode | ||
from .togglemodeapp import ToggleModeApp | ||
|
||
|
||
class ActivateModeApp(ToggleModeApp): | ||
description = "Activate a specific mode (teacher, student, student_exam)" | ||
|
||
def start(self) -> None: | ||
super().start() | ||
if len(self.extra_args) != 1: | ||
self.fail("Exactly one mode has to be specified") | ||
if self.extra_args[0] not in [ | ||
E2xGraderMode.TEACHER.value, | ||
E2xGraderMode.STUDENT.value, | ||
E2xGraderMode.STUDENT_EXAM.value, | ||
]: | ||
self.fail( | ||
f"Mode {self.extra_args[0]} is not a valid mode that can be activated." | ||
) | ||
self.mode = self.extra_args[0] | ||
|
||
self.activate_mode() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from textwrap import dedent | ||
|
||
from jupyter_core.application import JupyterApp | ||
from traitlets import Enum | ||
|
||
from ..utils.mode import E2xGraderMode, infer_e2xgrader_mode | ||
|
||
|
||
class E2xGrader(JupyterApp): | ||
|
||
mode = Enum( | ||
values=[mode.value for mode in E2xGraderMode], | ||
default_value=E2xGraderMode.INACTIVE.value, | ||
help=dedent( | ||
""" | ||
Which mode is activated, can be teacher, student, student_exam or deactivated. | ||
Is set to invalid if the mode cannot be inferred. | ||
""" | ||
), | ||
) | ||
|
||
def fail(self, msg, *args): | ||
self.log.error(msg, *args) | ||
self.exit(1) | ||
|
||
def initialize(self, argv=None): | ||
try: | ||
mode = infer_e2xgrader_mode() | ||
self.mode = mode | ||
except ValueError as e: | ||
self.log.error(str(e)) | ||
self.mode = E2xGraderMode.INVALID.value | ||
super().initialize(argv) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from ..utils.mode import E2xGraderMode | ||
from .togglemodeapp import ToggleModeApp | ||
|
||
|
||
class DeactivateModeApp(ToggleModeApp): | ||
description = "Deactivate all e2xgrader extensions" | ||
|
||
def start(self) -> None: | ||
super().start() | ||
if len(self.extra_args) != 0: | ||
self.fail("e2xgrader deactivate does not take any arguments.") | ||
self.mode = E2xGraderMode.INACTIVE.value | ||
self.activate_mode() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,105 +1,48 @@ | ||
import sys | ||
from argparse import ArgumentParser | ||
from textwrap import dedent | ||
|
||
from ..extensions import E2xExtensionManager | ||
from .activatemodeapp import ActivateModeApp | ||
from .baseapp import E2xGrader | ||
from .deactivatemodeapp import DeactivateModeApp | ||
from .showmodeapp import ShowModeApp | ||
|
||
|
||
class Manager: | ||
def __init__(self): | ||
self.extension_manager = E2xExtensionManager() | ||
parser = ArgumentParser( | ||
description="E2X extension manager.", | ||
usage=dedent( | ||
""" | ||
e2xgrader <command> [<args>] | ||
Available sub commands are: | ||
activate activate a specific mode (teacher, student, student-exam) | ||
deactivate deactivate all extensions""" | ||
), | ||
) | ||
|
||
parser.add_argument("command", help="Subcommand to run") | ||
class E2xGraderApp(E2xGrader): | ||
|
||
args = parser.parse_args(sys.argv[1:2]) | ||
if not hasattr(self, args.command): | ||
print("Unrecognized command") | ||
parser.print_help() | ||
exit(1) | ||
getattr(self, args.command)() | ||
|
||
def activate(self): | ||
parser = ArgumentParser( | ||
description="Activate different modes", | ||
usage=dedent( | ||
subcommands = dict( | ||
activate=( | ||
ActivateModeApp, | ||
dedent( | ||
"""\ | ||
Activate a specific mode (teacher, student, student_exam) | ||
""" | ||
e2xgrader activate <mode> [--sys-prefix] [--user] | ||
Available modes are: | ||
teacher activate the grader and all teaching extensions | ||
student activate the student extensions | ||
student_exam activate the student extensions in exam mode""" | ||
), | ||
) | ||
# prefixing the argument with -- means it's optional | ||
parser.add_argument( | ||
"mode", | ||
help="Which mode to activate, can be teacher, student or student-exam", | ||
) | ||
parser.add_argument( | ||
"--sys-prefix", | ||
action="store_true", | ||
help="If the extensions should be installed to sys.prefix", | ||
) | ||
parser.add_argument( | ||
"--user", | ||
action="store_true", | ||
help="If the extensions should be installed to the user space", | ||
) | ||
|
||
args = parser.parse_args(sys.argv[2:]) | ||
if not hasattr(self.extension_manager, f"activate_{args.mode}"): | ||
print("Unrecognized mode") | ||
parser.print_help() | ||
exit(1) | ||
sys_prefix = False | ||
user = False | ||
if args.sys_prefix: | ||
sys_prefix = True | ||
if args.user: | ||
user = True | ||
getattr(self.extension_manager, f"activate_{args.mode}")( | ||
sys_prefix=sys_prefix, user=user | ||
) | ||
|
||
def deactivate(self): | ||
parser = ArgumentParser( | ||
description="Deactivate extensions", | ||
usage=dedent("python -m e2xgrader deactivate [--sys-prefix] [--user]"), | ||
) | ||
# prefixing the argument with -- means it's optional | ||
parser.add_argument( | ||
"--sys-prefix", | ||
action="store_true", | ||
help="If the extensions should be uninstalled from sys.prefix", | ||
) | ||
parser.add_argument( | ||
"--user", | ||
action="store_true", | ||
help="If the extensions should be uninstalled from the user space", | ||
) | ||
|
||
args = parser.parse_args(sys.argv[2:]) | ||
).strip(), | ||
), | ||
deactivate=( | ||
DeactivateModeApp, | ||
dedent( | ||
"""\ | ||
Deactivate all e2xgrader extensions | ||
""" | ||
).strip(), | ||
), | ||
show_mode=( | ||
ShowModeApp, | ||
dedent( | ||
"""\ | ||
Show the currently active mode | ||
""" | ||
).strip(), | ||
), | ||
) | ||
|
||
sys_prefix = False | ||
user = False | ||
if args.sys_prefix: | ||
sys_prefix = True | ||
if args.user: | ||
user = True | ||
self.extension_manager.deactivate(sys_prefix=sys_prefix, user=user) | ||
def start(self) -> None: | ||
super().start() | ||
if self.subapp is None: | ||
print( | ||
"No subcommand given (run with --help for options). List of subcommands:\n" | ||
) | ||
self.print_subcommands() | ||
|
||
|
||
def main(): | ||
Manager() | ||
E2xGraderApp.launch_instance() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from .baseapp import E2xGrader | ||
|
||
|
||
class ShowModeApp(E2xGrader): | ||
description = "Show the currently active mode" | ||
|
||
def start(self) -> None: | ||
super().start() | ||
print(f"Current mode: {self.mode}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from traitlets import Bool | ||
|
||
from ..extensions import E2xExtensionManager | ||
from ..utils.mode import E2xGraderMode, infer_e2xgrader_mode | ||
from .baseapp import E2xGrader | ||
|
||
|
||
class ToggleModeApp(E2xGrader): | ||
|
||
sys_prefix = Bool(False, help="Install extensions to sys.prefix", config=True) | ||
|
||
user = Bool(False, help="Install extensions to the user space", config=True) | ||
|
||
flags = { | ||
"sys-prefix": ( | ||
{"ToggleModeApp": {"sys_prefix": True}}, | ||
"Install extensions to sys.prefix", | ||
), | ||
"user": ( | ||
{"ToggleModeApp": {"user": True}}, | ||
"Install extensions to the user space", | ||
), | ||
} | ||
|
||
def activate_mode(self): | ||
""" | ||
Activates the specified mode by activating the corresponding extensions | ||
using the E2xExtensionManager. | ||
If the mode is "None", it deactivates all e2xgrader extensions. | ||
""" | ||
extension_manager = E2xExtensionManager() | ||
if self.mode == E2xGraderMode.INACTIVE.value: | ||
print( | ||
f"Deactivating e2xgrader extensions with sys_prefix={self.sys_prefix} " | ||
f"and user={self.user}" | ||
) | ||
extension_manager.deactivate(sys_prefix=self.sys_prefix, user=self.user) | ||
else: | ||
print( | ||
f"Activating mode {self.mode} with sys_prefix={self.sys_prefix} " | ||
f"and user={self.user}" | ||
) | ||
getattr(extension_manager, f"activate_{self.mode}")( | ||
sys_prefix=self.sys_prefix, user=self.user | ||
) | ||
self.log.info(f"Activated mode {self.mode}. ") | ||
try: | ||
mode = infer_e2xgrader_mode() | ||
if mode != self.mode: | ||
self.log.warning( | ||
f"The activated mode {self.mode} does not match the infered mode {mode}. \n" | ||
f"The mode {mode} may be activated on a higher level." | ||
) | ||
except ValueError as e: | ||
self.log.error(str(e)) | ||
self.mode = E2xGraderMode.INVALID.value | ||
|
||
def start(self) -> None: | ||
super().start() | ||
if self.sys_prefix and self.user: | ||
self.fail("Cannot install in both sys-prefix and user space") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import unittest | ||
from unittest.mock import patch | ||
|
||
from jupyter_core.application import NoStart | ||
|
||
from e2xgrader.apps.activatemodeapp import ActivateModeApp | ||
from e2xgrader.utils.mode import E2xGraderMode | ||
|
||
|
||
class TestActivateModeApp(unittest.TestCase): | ||
|
||
def setUp(self): | ||
self.app = ActivateModeApp() | ||
self.app.initialize([]) | ||
|
||
def test_fail_without_args(self): | ||
with self.assertRaises(SystemExit): | ||
self.app.initialize([]) | ||
self.app.start() | ||
|
||
def test_fail_with_invalid_mode(self): | ||
with self.assertRaises(SystemExit): | ||
self.app.initialize(["invalid_mode"]) | ||
self.app.start() | ||
|
||
@patch("e2xgrader.apps.togglemodeapp.ToggleModeApp.activate_mode") | ||
def test_activate_mode(self, mock_activate_mode): | ||
try: | ||
self.app.initialize([E2xGraderMode.TEACHER.value]) | ||
self.app.start() | ||
except NoStart: | ||
pass | ||
finally: | ||
self.assertEqual(self.app.mode, E2xGraderMode.TEACHER.value) | ||
mock_activate_mode.assert_called_once() |
Oops, something went wrong.