コード例 #1
0
ファイル: base.py プロジェクト: LibreTexts/ngshare_exchange
 def _new_exchange_object(self, cls, course_id, assignment_id, student_id):
     assert issubclass(cls, Exchange)
     cls.cache = str(self.cache_dir)
     coursedir = CourseDirectory()
     coursedir.root = str(self.course_dir)
     coursedir.course_id = course_id
     coursedir.assignment_id = assignment_id
     obj = cls(coursedir=coursedir)
     obj.username = student_id
     return obj
コード例 #2
0
def test_list_acknowledges_multi_marker_feature_flag(plugin_config, tmpdir, monkeypatch):
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.ExchangeList.inbound = True

    plugin = ExchangeList(coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    def api_request(*args, **kwargs):
        assert args[0] == ("assignments?course_id=no_course")
        assert "method" not in kwargs or kwargs.get("method").lower() == "get"
        return type(
            "Request",
            (object,),
            {
                "status_code": 200,
                "json": (
                    lambda: {
                        "success": True,
                        "value": [
                            {
                                "assignment_id": "assign_1_1",
                                "student_id": 1,
                                "course_id": "no_course",
                                "status": "released",
                                "path": "",
                                "notebooks": [
                                    {
                                        "notebook_id": "assignment-0.6",
                                        "has_exchange_feedback": False,
                                        "feedback_updated": False,
                                        "feedback_timestamp": None,
                                    }
                                ],
                                "timestamp": "2020-01-01 00:00:00.0 00:00",
                            }
                        ],
                    }
                ),
            },
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        plugin.start()
    assert plugin.coursedir.submitted_directory == "collected"

    monkeypatch.setenv("NAAS_FEATURE_MULTI_MARKERS", "True")
    plugin = ExchangeList(coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    with patch.object(Exchange, "api_request", side_effect=api_request):
        plugin.start()
    assert plugin.coursedir.submitted_directory == "submitted"
コード例 #3
0
def test_fetch_assignment_methods_init_dest(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = course_id
    plugin_config.CourseDirectory.assignment_id = ass_1_2

    plugin = ExchangeFetchAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    # we're good if the dir doesn't exist
    plugin.init_dest()
    assert re.search(rf"{ass_1_2}$", plugin.dest_path)
    assert os.path.isdir(plugin.dest_path)

    # we're good if the dir exists and is empty
    plugin.init_dest()
    assert re.search(rf"{ass_1_2}$", plugin.dest_path)

    # we're good if the dir exists, and has something OTHER than an ipynb file in it
    with open(f"{plugin.dest_path}/random.txt", "w") as fp:
        fp.write("Hello world")
    plugin.init_dest()
    assert re.search(rf"{ass_1_2}$", plugin.dest_path)

    # FAILS if the dir exists AND there's an ipynb file in it
    with open(f"{plugin.dest_path}/random.ipynb", "w") as fp:
        fp.write("Hello world")
    with pytest.raises(ExchangeError) as e_info:
        plugin.init_dest()
    assert (
        f"You already have notebook documents in directory: {plugin_config.CourseDirectory.assignment_id}. Please remove them before fetching again"  # noqa: E501
        in str(e_info.value))
    shutil.rmtree(plugin.dest_path)
コード例 #4
0
ファイル: handlers.py プロジェクト: tmetzl/e2xgrader
    def submit_assignment(self, course_id, assignment_id):
        with self.get_assignment_dir_config() as config:
            try:
                config = self.load_config()
                config.CourseDirectory.course_id = course_id
                config.CourseDirectory.assignment_id = assignment_id

                coursedir = CourseDirectory(config=config)
                authenticator = Authenticator(config=config)
                submit = ExchangeFactory(config=config).Submit(
                    coursedir=coursedir,
                    authenticator=authenticator,
                    config=config)

                retval = submit.start()
                hashcode = 'Exchange not set up for hashcode'
                timestamp = 'Exchange not set up for timestamp'
                if retval and len(retval) == 2:
                    hashcode, timestamp = retval

            except:
                self.log.error(traceback.format_exc())
                retvalue = {"success": False, "value": traceback.format_exc()}

            else:
                retvalue = {
                    "success": True,
                    "hashcode": hashcode,
                    "timestamp": timestamp
                }

        self.log.info(retvalue)

        return retvalue
コード例 #5
0
    def check_for_noauth_jupyterhub_formgraders(self, config):
        try:
            get_jupyterhub_user()
        except JupyterhubEnvironmentError:
            # Not running on JupyterHub.
            raise gen.Return([])

        # We are running on JupyterHub, so maybe there's a formgrader
        # service. Check if we have a course id and if so guess the path to the
        # formgrader.
        coursedir = CourseDirectory(config=config)
        if not coursedir.course_id:
            raise gen.Return([])

        url = self.get_base_url() + "/services/" + coursedir.course_id + "/formgrader"
        auth = get_jupyterhub_authorization()
        http_client = AsyncHTTPClient()
        try:
            yield http_client.fetch(url, headers=auth)
        except:
            self.log.error("Formgrader not available at URL: %s", url)
            raise gen.Return([])

        courses = [{
            'course_id': coursedir.course_id,
            'url': url,
            'kind': 'jupyterhub'
        }]
        raise gen.Return(courses)
コード例 #6
0
def test_fetch_feedback_methods(plugin_config, tmpdir):
    plugin_config.Exchange.assignment_dir = str(
        tmpdir.mkdir("feedback_test").realpath()
    )
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.CourseDirectory.assignment_id = assignment_id

    plugin = ExchangeFetchFeedback(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config
    )

    plugin.init_src()
    with pytest.raises(AttributeError) as e_info:
        foo = plugin.src_path
    assert (
        str(e_info.value)
        == "'ExchangeFetchFeedback' object has no attribute 'src_path'"
    )

    plugin.init_dest()
    print(f"plugin.dest_path:{plugin.dest_path}")
    assert re.search(
        r"test_fetch_feedback_methods0/feedback_test/assign_1/feedback$",
        plugin.dest_path,
    )
    assert os.path.isdir(plugin.dest_path)
コード例 #7
0
def test_release_assignment_methods_the_rest(plugin_config, tmpdir, caplog):
    plugin_config.CourseDirectory.root = "/"

    plugin_config.CourseDirectory.release_directory = str(tmpdir.mkdir(release_dir).realpath())
    plugin_config.CourseDirectory.assignment_id = "assign_1"

    plugin = ExchangeReleaseAssignment(coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    os.makedirs(
        os.path.join(plugin_config.CourseDirectory.release_directory, "assign_1"),
        exist_ok=True,
    )
    copyfile(
        notebook1_filename,
        os.path.join(plugin_config.CourseDirectory.release_directory, "assign_1", "release.ipynb"),
    )
    with open(
        os.path.join(plugin_config.CourseDirectory.release_directory, "assign_1", "timestamp.txt"),
        "w",
    ) as fp:
        fp.write("2020-01-01 00:00:00.0 UTC")

    plugin.init_src()
    with pytest.raises(AttributeError) as e_info:
        plugin.dest_path
        assert str(e_info.value) == "'ExchangeReleaseAssignment' object has no attribute 'dest_path'"

    file = plugin.tar_source()
    assert len(file) > 1000
    plugin.get_notebooks()
    assert plugin.notebooks == ["release"]
コード例 #8
0
def test_fetch_assignment_handles_500_failure(plugin_config):
    http_error = "blown op"
    plugin_config.CourseDirectory.course_id = course_id
    plugin_config.CourseDirectory.assignment_id = ass_1_2

    plugin = ExchangeFetchAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    try:

        def api_request(*args, **kwargs):
            return type(
                "Response",
                (object, ),
                {
                    "status_code": 500,
                    "headers": {
                        "content-type": "application/gzip"
                    },
                    "content": http_error,
                },
            )

        with patch.object(Exchange, "api_request", side_effect=api_request):
            with pytest.raises(ExchangeError) as e_info:
                plugin.start()
            assert (
                str(e_info.value) ==
                f"Error failing to fetch assignment {ass_1_2} on course {course_id}: status code 500: error {http_error}"  # noqa: E501
            )
    finally:
        shutil.rmtree(plugin.dest_path)
コード例 #9
0
def test_fetch_feedback_dir_created(plugin_config, tmpdir):
    plugin_config.Exchange.assignment_dir = str(
        tmpdir.mkdir("feedback_test").realpath()
    )
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.CourseDirectory.assignment_id = assignment_id

    assert not os.path.isdir(
        os.path.join(plugin_config.Exchange.assignment_dir, student_id, "feedback")
    )

    plugin = ExchangeFetchFeedback(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config
    )

    def api_request(*args, **kwargs):
        assert args[0] == (f"feedback?course_id=no_course&assignment_id=assign_1")
        return type(
            "Response",
            (object,),
            {
                "status_code": 200,
                "headers": {"content-type": "text/json"},
                "json": lambda: {"success": True, "feedback": []},
            },
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        called = plugin.start()
        assert os.path.isdir(
            os.path.join(
                plugin_config.Exchange.assignment_dir, assignment_id, "feedback"
            )
        )
コード例 #10
0
ファイル: coursedir.py プロジェクト: DigiKlausur/nbassignment
class CourseDirectory(LoggingConfigurable):

    _instance = None

    def initialize(self):
        app = NbGrader()
        app.load_config_file()
        self.coursedir = NbGraderCourseDirectory()
        self.coursedir.config = app.config
        self.log.info('Loaded nbassignment coursedir')

    def __new__(cls):
        if not cls._instance:
            print('Creating new instance')
            cls._instance = super(CourseDirectory, cls).__new__(cls)
            cls._instance.initialize()
        return cls._instance

    def format_path(self,
                    nbgrader_step: str,
                    student_id: str,
                    assignment_id: str,
                    escape: bool = False) -> str:
        return self.coursedir.format_path(nbgrader_step=nbgrader_step,
                                          student_id=student_id,
                                          assignment_id=assignment_id,
                                          escape=escape)
コード例 #11
0
def test_release_assignment_several_normal(plugin_config, tmpdir):
    plugin_config.CourseDirectory.root = "/"

    plugin_config.CourseDirectory.release_directory = str(tmpdir.mkdir(release_dir).realpath())
    plugin_config.CourseDirectory.assignment_id = "assign_1"
    os.makedirs(
        os.path.join(plugin_config.CourseDirectory.release_directory, "assign_1"),
        exist_ok=True,
    )
    copyfile(
        notebook1_filename,
        os.path.join(
            plugin_config.CourseDirectory.release_directory,
            "assign_1",
            "release1.ipynb",
        ),
    )
    with open(
        os.path.join(plugin_config.CourseDirectory.release_directory, "assign_1", "timestamp.txt"),
        "w",
    ) as fp:
        fp.write("2020-01-01 00:00:00.0 UTC")

    copyfile(
        notebook1_filename,
        os.path.join(
            plugin_config.CourseDirectory.release_directory,
            "assign_1",
            "release1.ipynb",
        ),
    )

    copyfile(
        notebook2_filename,
        os.path.join(
            plugin_config.CourseDirectory.release_directory,
            "assign_1",
            "release2.ipynb",
        ),
    )

    plugin = ExchangeReleaseAssignment(coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    def api_request(*args, **kwargs):
        assert args[0] == ("assignment?course_id=no_course&assignment_id=assign_1")
        assert kwargs.get("method").lower() == "post"
        assert kwargs.get("data").get("notebooks") == ["release1", "release2"]
        assert "assignment" in kwargs.get("files")
        assert "assignment.tar.gz" == kwargs.get("files").get("assignment")[0]
        assert len(kwargs.get("files").get("assignment")[1]) > 0

        return type(
            "Request",
            (object,),
            {"status_code": 200, "json": (lambda: {"success": True})},
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        plugin.start()
コード例 #12
0
def test_list_normal(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = "no_course"

    plugin = ExchangeList(coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    def api_request(*args, **kwargs):
        assert args[0] == ("assignments?course_id=no_course")
        assert "method" not in kwargs or kwargs.get("method").lower() == "get"
        return type(
            "Request",
            (object,),
            {
                "status_code": 200,
                "json": (
                    lambda: {
                        "success": True,
                        "value": [
                            {
                                "assignment_id": "assign_1_1",
                                "student_id": 1,
                                "course_id": "no_course",
                                "status": "released",
                                "path": "",
                                "notebooks": [
                                    {
                                        "notebook_id": "assignment-0.6",
                                        "has_exchange_feedback": False,
                                        "feedback_updated": False,
                                        "feedback_timestamp": None,
                                    }
                                ],
                                "timestamp": "2020-01-01 00:00:00.0 00:00",
                            }
                        ],
                    }
                ),
            },
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        called = plugin.start()
        assert called == [
            {
                "assignment_id": "assign_1_1",
                "course_id": "no_course",
                "student_id": 1,
                "status": "released",
                "notebooks": [
                    {
                        "notebook_id": "assignment-0.6",
                        "has_exchange_feedback": False,
                        "feedback_updated": False,
                        "feedback_timestamp": None,
                    }
                ],
                "path": "",
                "timestamp": "2020-01-01 00:00:00.0 00:00",
            }
        ]
コード例 #13
0
def test_release_assignment_methods_init_src(plugin_config, tmpdir, caplog):
    plugin_config.CourseDirectory.root = "/"

    plugin_config.CourseDirectory.source_directory = str(
        tmpdir.mkdir(source_dir).realpath())
    plugin_config.CourseDirectory.release_directory = str(
        tmpdir.mkdir(release_dir).realpath())
    plugin_config.CourseDirectory.assignment_id = "assign_1"

    plugin = ExchangeReleaseAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    # No release file, no source file
    with pytest.raises(ExchangeError) as e_info:
        plugin.init_src()
    assert "Assignment not found at:" in str(e_info.value)

    # No release, source file exists
    os.makedirs(
        os.path.join(plugin_config.CourseDirectory.source_directory,
                     "assign_1"),
        exist_ok=True,
    )
    copyfile(
        notebook1_filename,
        os.path.join(plugin_config.CourseDirectory.source_directory,
                     "assign_1", "release.ipynb"),
    )
    with pytest.raises(ExchangeError) as e_info:
        plugin.init_src()
    assert re.match(
        r"Assignment found in '.+' but not '.+', run `nbgrader assign` first.",
        str(e_info.value),
    )

    # release file exists
    os.makedirs(
        os.path.join(plugin_config.CourseDirectory.release_directory,
                     "assign_1"),
        exist_ok=True,
    )
    copyfile(
        notebook1_filename,
        os.path.join(plugin_config.CourseDirectory.release_directory,
                     "assign_1", "release.ipynb"),
    )
    with open(
            os.path.join(plugin_config.CourseDirectory.release_directory,
                         "assign_1", "timestamp.txt"),
            "w",
    ) as fp:
        fp.write("2020-01-01 00:00:00.0 UTC")
    plugin.init_src()
    assert re.search(
        r"test_release_assignment_method0/release_test/./assign_1$",
        plugin.src_path)
    assert os.path.isdir(plugin.src_path)
コード例 #14
0
def test_release_oversize_blocked(plugin_config, tmpdir):
    plugin_config.CourseDirectory.root = "/"

    plugin_config.CourseDirectory.release_directory = str(
        tmpdir.mkdir(release_dir).realpath())
    plugin_config.CourseDirectory.assignment_id = "assign_1"
    os.makedirs(
        os.path.join(plugin_config.CourseDirectory.release_directory,
                     "assign_1"),
        exist_ok=True,
    )
    copyfile(
        notebook1_filename,
        os.path.join(plugin_config.CourseDirectory.release_directory,
                     "assign_1", "release.ipynb"),
    )
    with open(
            os.path.join(plugin_config.CourseDirectory.release_directory,
                         "assign_1", "timestamp.txt"),
            "w",
    ) as fp:
        fp.write("2020-01-01 00:00:00.0 UTC")

    plugin = ExchangeReleaseAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    # Set the max-buffer-size to 50 bytes
    plugin.max_buffer_size = 50

    def api_request(*args, **kwargs):
        assert args[0] == (f"assignment?course_id=no_course"
                           f"&assignment_id=assign_1")
        assert kwargs.get("method").lower() == "post"
        assert kwargs.get("data").get("notebooks") == ["release"]
        assert "assignment" in kwargs.get("files")
        assert "assignment.tar.gz" == kwargs.get("files").get("assignment")[0]
        assert len(kwargs.get("files").get("assignment")[1]) > 0

        return type(
            "Request",
            (object, ),
            {
                "status_code": 200,
                "json": (lambda: {
                    "success": True
                })
            },
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        with pytest.raises(ExchangeError) as e_info:
            plugin.start()
        assert (
            str(e_info.value) ==
            "Assignment assign_1 not released. The contents of your assignment are too large:\nYou may have data files, temporary files, and/or working files that should not be included - try deleting them."
        )
コード例 #15
0
def test_release_assignment_methods_init_dest(plugin_config, tmpdir, caplog):
    plugin_config.CourseDirectory.root = "/"

    plugin_config.CourseDirectory.release_directory = str(tmpdir.mkdir(release_dir).realpath())
    plugin_config.CourseDirectory.assignment_id = "assign_1"

    plugin = ExchangeReleaseAssignment(coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    with pytest.raises(ExchangeError) as e_info:
        plugin.init_dest()
        assert str(e_info.value) == "No course id specified. Re-run with --course flag."
コード例 #16
0
def test_fetch_feedback_fetch_several_normal(plugin_config, tmpdir):
    plugin_config.Exchange.assignment_dir = str(
        tmpdir.mkdir("feedback_test").realpath()
    )
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.CourseDirectory.assignment_id = assignment_id

    plugin = ExchangeFetchFeedback(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config
    )

    def api_request(*args, **kwargs):
        assert args[0] == (f"feedback?course_id=no_course&assignment_id=assign_1")
        assert "method" not in kwargs or kwargs.get("method").lower() == "get"
        return type(
            "Response",
            (object,),
            {
                "status_code": 200,
                "headers": {"content-type": "text/json"},
                "json": lambda: {
                    "success": True,
                    "feedback": [
                        {
                            "filename": "test_feedback1.html",
                            "content": feedback_file,
                            "timestamp": "2020-01-01 00:00:01 00:00",
                        },
                        {
                            "filename": "test_feedback2.html",
                            "content": feedback_file,
                            "timestamp": "2020-01-01 00:00:00 00:00",
                        },
                    ],
                },
            },
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        called = plugin.start()

        assert os.path.exists(
            os.path.join(
                plugin.dest_path, "2020-01-01 00:00:01 00:00", "test_feedback1.html"
            )
        )
        assert os.path.exists(
            os.path.join(
                plugin.dest_path, "2020-01-01 00:00:00 00:00", "test_feedback2.html"
            )
        )
コード例 #17
0
    def list_released_assignments(self, course_id=None):
        with self.get_assignment_dir_config() as config:
            try:
                if course_id:
                    config.CourseDirectory.course_id = course_id

                coursedir = CourseDirectory(config=config)
                authenticator = Authenticator(config=config)
                lister = ExchangeFactory(config=config).List(
                    coursedir=coursedir,
                    authenticator=authenticator,
                    config=config)
                assignments = lister.start()

            except Exception as e:
                self.log.error(traceback.format_exc())
                if isinstance(e, ExchangeError):
                    retvalue = {
                        "success":
                        False,
                        "value":
                        """The exchange directory does not exist and could
                                    not be created. The "release" and "collect" functionality will not be available.
                                    Please see the documentation on
                                    http://nbgrader.readthedocs.io/en/stable/user_guide/managing_assignment_files.html#setting-up-the-exchange
                                    for instructions.
                                """
                    }
                else:
                    retvalue = {
                        "success": False,
                        "value": traceback.format_exc()
                    }
            else:
                for assignment in assignments:
                    if assignment['status'] == 'fetched':
                        assignment['path'] = os.path.relpath(
                            assignment['path'], self.parent.notebook_dir)
                        for notebook in assignment['notebooks']:
                            notebook['path'] = os.path.relpath(
                                notebook['path'], self.parent.notebook_dir)
                retvalue = {
                    "success":
                    True,
                    "value":
                    sorted(assignments,
                           key=lambda x: (x['course_id'], x['assignment_id']))
                }

        return retvalue
コード例 #18
0
def test_fetch_assignment_methods_rest(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = course_id
    plugin_config.CourseDirectory.assignment_id = ass_1_2

    plugin = ExchangeFetchAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    plugin.init_src()
    assert re.search(rf"{course_id}/{ass_1_2}/assignment.tar.gz$",
                     plugin.src_path)
    plugin.init_dest()

    try:

        def api_request(*args, **kwargs):
            tar_file = io.BytesIO()

            with tarfile.open(fileobj=tar_file, mode="w:gz") as tar_handle:
                tar_handle.add(notebook1_filename,
                               arcname=os.path.basename(notebook1_filename))
            tar_file.seek(0)

            assert args[0] == (
                f"assignment?course_id={course_id}&assignment_id={ass_1_2}")
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            return type(
                "Response",
                (object, ),
                {
                    "status_code": 200,
                    "headers": {
                        "content-type": "application/gzip"
                    },
                    "content": tar_file.read(),
                },
            )

        with patch.object(Exchange, "api_request", side_effect=api_request):
            plugin.download()
            assert os.path.exists(
                os.path.join(plugin.src_path, "assignment-0.6.ipynb"))
            shutil.rmtree(plugin.dest_path)

            # do_copy includes a download()
            plugin.do_copy(plugin.src_path, plugin.dest_path)
            assert os.path.exists(
                os.path.join(plugin.dest_path, "assignment-0.6.ipynb"))
    finally:
        shutil.rmtree(plugin.dest_path)
コード例 #19
0
def test_release_assignment_fail(plugin_config, tmpdir):
    plugin_config.CourseDirectory.root = "/"

    plugin_config.CourseDirectory.release_directory = str(
        tmpdir.mkdir(release_dir).realpath())
    plugin_config.CourseDirectory.assignment_id = "assign_1"
    os.makedirs(
        os.path.join(plugin_config.CourseDirectory.release_directory,
                     "assign_1"),
        exist_ok=True,
    )
    copyfile(
        notebook1_filename,
        os.path.join(
            plugin_config.CourseDirectory.release_directory,
            "assign_1",
            "feedback.ipynb",
        ),
    )
    with open(
            os.path.join(plugin_config.CourseDirectory.release_directory,
                         "assign_1", "timestamp.txt"),
            "w",
    ) as fp:
        fp.write("2020-01-01 00:00:00.0 UTC")

    plugin = ExchangeReleaseAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    def api_request(*args, **kwargs):
        return type(
            "Request",
            (object, ),
            {
                "status_code": 200,
                "json": (lambda: {
                    "success": False,
                    "note": "failure note"
                }),
            },
        )

    with patch.object(Exchange, "api_request", side_effect=api_request):
        with pytest.raises(ExchangeError) as e_info:
            plugin.start()
        assert str(e_info.value) == "failure note"
コード例 #20
0
def test_list_delete(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.CourseDirectory.assignment_id = "assign_1_1"
    plugin_config.ExchangeList.remove = True

    plugin = ExchangeList(coursedir=CourseDirectory(config=plugin_config),
                          config=plugin_config)

    def api_request(*args, **kwargs):
        assert args[0] == (
            "assignment?course_id=no_course&assignment_id=assign_1_1")
        assert "method" not in kwargs or kwargs.get(
            "method").lower() == "delete"
        return type("Request", (object, ), {"status_code": 200})

    with patch.object(Exchange, "api_request", side_effect=api_request):
        called = plugin.start()
コード例 #21
0
def test_fetch_folder_exists_with_ipynb(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = course_id
    plugin_config.CourseDirectory.assignment_id = ass_1_3

    plugin = ExchangeFetchAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    os.makedirs(ass_1_3)
    with open("assign_1_3/decoy.ipynb", "w") as f:
        f.write(" ")
    try:

        def api_request(*args, **kwargs):
            tar_file = io.BytesIO()

            with tarfile.open(fileobj=tar_file, mode="w:gz") as tar_handle:
                tar_handle.add(notebook1_filename,
                               arcname=os.path.basename(notebook1_filename))
                tar_handle.add(notebook2_filename,
                               arcname=os.path.basename(notebook2_filename))
            tar_file.seek(0)

            assert args[0] == (
                f"assignment?course_id={course_id}&assignment_id=assign_1_3")
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            return type(
                "Response",
                (object, ),
                {
                    "status_code": 200,
                    "headers": {
                        "content-type": "application/gzip"
                    },
                    "content": tar_file.read(),
                },
            )

        with patch.object(Exchange, "api_request", side_effect=api_request):
            with pytest.raises(ExchangeError) as e_info:
                plugin.start()
            assert (
                str(e_info.value) ==
                "You already have notebook documents in directory: assign_1_3. Please remove them before fetching again"  # noqa: E501
            )
    finally:
        shutil.rmtree(plugin.dest_path)
コード例 #22
0
def test_fetch_folder_exists_with_other_file(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = course_id
    plugin_config.CourseDirectory.assignment_id = ass_1_3

    plugin = ExchangeFetchAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    os.makedirs(ass_1_3)
    with open("assign_1_3/decoy.txt", "w") as f:
        f.write(" ")
    try:

        def api_request(*args, **kwargs):
            tar_file = io.BytesIO()

            with tarfile.open(fileobj=tar_file, mode="w:gz") as tar_handle:
                tar_handle.add(notebook1_filename,
                               arcname=os.path.basename(notebook1_filename))
                tar_handle.add(notebook2_filename,
                               arcname=os.path.basename(notebook2_filename))
            tar_file.seek(0)

            assert args[0] == (
                f"assignment?course_id={course_id}&assignment_id=assign_1_3")
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            return type(
                "Response",
                (object, ),
                {
                    "status_code": 200,
                    "headers": {
                        "content-type": "application/gzip"
                    },
                    "content": tar_file.read(),
                },
            )

        with patch.object(Exchange, "api_request", side_effect=api_request):
            plugin.start()
            assert os.path.exists(
                os.path.join(plugin.dest_path, "assignment-0.6.ipynb"))
            assert os.path.exists(
                os.path.join(plugin.dest_path, "assignment-0.6-2.ipynb"))
    finally:
        shutil.rmtree(plugin.dest_path)
コード例 #23
0
    def list_submitted_assignments(self, course_id=None):
        with self.get_assignment_dir_config() as config:
            try:
                config.ExchangeList.cached = True
                if course_id:
                    config.CourseDirectory.course_id = course_id

                coursedir = CourseDirectory(config=config)
                authenticator = Authenticator(config=config)
                lister = ExchangeFactory(config=config).List(
                    coursedir=coursedir,
                    authenticator=authenticator,
                    config=config)
                assignments = lister.start()

            except Exception as e:
                self.log.error(traceback.format_exc())
                if isinstance(e, ExchangeError):
                    retvalue = {
                        "success":
                        False,
                        "value":
                        """The exchange directory does not exist and could
                                    not be created. The "release" and "collect" functionality will not be available.
                                    Please see the documentation on
                                    http://nbgrader.readthedocs.io/en/stable/user_guide/managing_assignment_files.html#setting-up-the-exchange
                                    for instructions.
                                """
                    }
                else:
                    retvalue = {
                        "success": False,
                        "value": traceback.format_exc()
                    }
            else:
                for assignment in assignments:
                    assignment["submissions"] = sorted(
                        assignment["submissions"],
                        key=lambda x: x["timestamp"])
                assignments = sorted(assignments,
                                     key=lambda x: x["assignment_id"])
                retvalue = {"success": True, "value": assignments}

        return retvalue
コード例 #24
0
ファイル: helpers.py プロジェクト: IllumiDesk/illumidesk
def get_nbgrader_api(notebook_dir: str, course_id: str = None) -> NbGraderAPI:
    """Returns an instance of the NbraderAPI based on the notebook directory and
    course_id.

    Args:
        notebook_dir ([str]): the notebook directory path and filename.
        course_id ([str], optional): The course _id, defaults to None.

    Returns:
        NbGraderAPI: and instance of the nbrader API.
    """
    with get_assignment_dir_config(notebook_dir) as config:
        if course_id:
            config.CourseDirectory.course_id = course_id

        coursedir = CourseDirectory(config=config)
        authenticator = Authenticator(config=config)
        api = NbGraderAPI(coursedir, authenticator)
        return api
コード例 #25
0
def test_fetch_assignment_fetch_normal_with_path_includes_course(
        plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.CourseDirectory.assignment_id = "assign_1_2"
    plugin_config.Exchange.path_includes_course = True

    plugin = ExchangeFetchAssignment(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)

    try:

        def api_request(*args, **kwargs):
            tar_file = io.BytesIO()

            with tarfile.open(fileobj=tar_file, mode="w:gz") as tar_handle:
                tar_handle.add(notebook1_filename,
                               arcname=os.path.basename(notebook1_filename))
            tar_file.seek(0)

            assert args[0] == (
                f"assignment?course_id=no_course&assignment_id=assign_1_2")
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            return type(
                "Response",
                (object, ),
                {
                    "status_code": 200,
                    "headers": {
                        "content-type": "application/x-tar"
                    },
                    "content": tar_file.read(),
                },
            )

        with patch.object(Exchange, "api_request", side_effect=api_request):
            plugin.start()
            assert os.path.exists(
                os.path.join(plugin.dest_path, "assignment-0.6.ipynb"))
    finally:
        shutil.rmtree(plugin.dest_path)
コード例 #26
0
    def submit_assignment(self, course_id, assignment_id):
        with self.get_assignment_dir_config() as config:
            try:
                config = self.load_config()
                config.CourseDirectory.course_id = course_id
                config.CourseDirectory.assignment_id = assignment_id

                coursedir = CourseDirectory(config=config)
                authenticator = Authenticator(config=config)
                submit = ExchangeFactory(config=config).Submit(
                    coursedir=coursedir,
                    authenticator=authenticator,
                    config=config)
                submit.start()

            except:
                self.log.error(traceback.format_exc())
                retvalue = {"success": False, "value": traceback.format_exc()}

            else:
                retvalue = {"success": True}

        return retvalue
コード例 #27
0
def test_release_feedback_methods(plugin_config, tmpdir):
    plugin_config.CourseDirectory.root = "/"
    plugin_config.CourseDirectory.feedback_directory = str(
        tmpdir.mkdir("feedback_test").realpath())
    plugin_config.CourseDirectory.assignment_id = assignment_id

    plugin = ExchangeReleaseFeedback(
        coursedir=CourseDirectory(config=plugin_config), config=plugin_config)
    plugin.init_src()
    print(f"asserting plugin.src_path: {plugin.src_path}")
    assert re.search(
        r"test_release_feedback_methods0/feedback_test/\*/assign_1$",
        plugin.src_path)
    plugin.coursedir.student_id = student_id
    plugin.init_src()
    assert re.search(
        r"test_release_feedback_methods0/feedback_test/1/assign_1$",
        plugin.src_path)

    with pytest.raises(ExchangeError) as e_info:
        plugin.init_dest()
        assert str(e_info.value
                   ) == "No course id specified. Re-run with --course flag."
コード例 #28
0
def test_list_feedback_available_with_path_includes_course(
        plugin_config, tmpdir):
    try:
        course_code = "no_course"
        assignment_id = "assign_1_1"
        plugin_config.CourseDirectory.course_id = course_code
        plugin_config.CourseDirectory.assignment_id = assignment_id

        plugin_config.ExchangeList.inbound = True
        plugin_config.Exchange.path_includes_course = True

        my_feedback_dir = f"{course_code}/{assignment_id}/feedback/2020-01-01 00:02:00.2 00:00"
        os.makedirs(my_feedback_dir, exist_ok=True)
        copyfile(
            feedback1_filename,
            os.path.join(
                my_feedback_dir,
                basename(feedback1_filename),
            ),
        )

        plugin = ExchangeList(coursedir=CourseDirectory(config=plugin_config),
                              config=plugin_config)

        def api_request(*args, **kwargs):
            assert args[0] == ("assignments?course_id=no_course")
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            return type(
                "Request",
                (object, ),
                {
                    "status_code":
                    200,
                    "json": (lambda: {
                        "success":
                        True,
                        "value": [
                            {
                                "assignment_id":
                                assignment_id,
                                "student_id":
                                1,
                                "course_id":
                                course_code,
                                "status":
                                "submitted",
                                "path":
                                "",
                                "notebooks": [{
                                    "notebook_id":
                                    root_notebook_name,
                                    "has_exchange_feedback":
                                    True,
                                    "feedback_updated":
                                    False,
                                    "feedback_timestamp":
                                    "2020-01-01 00:02:00.2 00:00",
                                }],
                                "timestamp":
                                "2020-01-01 00:00:00.2 00:00",
                            },
                        ],
                    }),
                },
            )

        with patch.object(Exchange, "api_request", side_effect=api_request):
            called = plugin.start()
            assert called == [{
                "assignment_id":
                assignment_id,
                "course_id":
                course_code,
                "student_id":
                1,
                "status":
                "submitted",
                "submissions": [{
                    "assignment_id":
                    assignment_id,
                    "course_id":
                    course_code,
                    "path":
                    "",
                    "status":
                    "submitted",
                    "student_id":
                    1,
                    "notebooks": [{
                        "feedback_timestamp": "2020-01-01 00:02:00.2 00:00",
                        "has_exchange_feedback": True,
                        "has_local_feedback": True,
                        "local_feedback_path":
                        f"{course_code}/{assignment_id}/feedback/2020-01-01%2000%3A02%3A00.2%2000%3A00/{root_notebook_name}.html",  # noqa: E501
                        "feedback_updated": False,
                        "notebook_id": root_notebook_name,
                    }],
                    "timestamp":
                    "2020-01-01 00:00:00.2 00:00",
                    "feedback_updated":
                    False,
                    "has_exchange_feedback":
                    True,
                    "has_local_feedback":
                    True,
                    "local_feedback_path":
                    f"{course_code}/{assignment_id}/feedback/2020-01-01%2000%3A02%3A00.2%2000%3A00",  # noqa: E501
                }],
            }]
    finally:
        shutil.rmtree(course_code)
コード例 #29
0
def test_collect_normal_several_full_name_none(plugin_config, tmpdir):
    plugin_config.CourseDirectory.course_id = "no_course"
    plugin_config.CourseDirectory.assignment_id = ass_1_1
    plugin_config.CourseDirectory.submitted_directory = str(
        tmpdir.mkdir("submitted").realpath())
    plugin = ExchangeCollect(coursedir=CourseDirectory(config=plugin_config),
                             config=plugin_config)
    collections = False
    collection = False
    gradebook_called = False
    student_ids = ["1", "2"]
    collection_number = 0

    def gradebook_update(*args, **kwargs):
        nonlocal gradebook_called, collection_number
        gradebook_called = True
        assert kwargs.get("first_name") == ""
        assert kwargs.get("last_name") == ""
        assert args[0] == student_ids[collection_number]
        collection_number += 1

    def api_request(*args, **kwargs):
        nonlocal collections, collection
        tar_file = io.BytesIO()
        if "collections" in args[0]:
            collections = True
            assert args[0] == (
                f"collections?course_id=no_course&assignment_id={ass_1_1}")
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            return type(
                "Response",
                (object, ),
                {
                    "status_code": 200,
                    "headers": {
                        "content-type": "application/x-tar"
                    },
                    "json": lambda: {
                        "success":
                        True,
                        "value": [
                            {
                                "student_id": student_ids[0],
                                "full_name": None,
                                "path": f"/submitted/no_course/{ass_1_1}/1/",
                                "timestamp": "2020-01-01 00:00:00.0 UTC",
                            },
                            {
                                "student_id": student_ids[1],
                                "full_name": None,
                                "path": f"/submitted/no_course/{ass_1_1}/2/",
                                "timestamp": "2020-01-01 00:00:00.1 UTC",
                            },
                        ],
                    },
                },
            )
        else:
            num = "2" if collection else "1"
            assert args[0] == (
                f"collection?course_id=no_course&assignment_id={ass_1_1}&path=%2Fsubmitted%2Fno_course%2F{ass_1_1}%2F{num}%2F"
            )
            collection = True
            assert "method" not in kwargs or kwargs.get(
                "method").lower() == "get"
            with tarfile.open(fileobj=tar_file, mode="w:gz") as tar_handle:
                tar_handle.add(notebook1_filename,
                               arcname=os.path.basename(notebook1_filename))
            tar_file.seek(0)

            return type(
                "Response",
                (object, ),
                {
                    "status_code": 200,
                    "headers": {
                        "content-type": "application/x-tar"
                    },
                    "content": tar_file.read(),
                },
            )

    with patch.object(Exchange, "api_request",
                      side_effect=api_request), patch.object(
                          Gradebook,
                          "update_or_create_student",
                          side_effect=gradebook_update):
        plugin.start()
        assert gradebook_called
        assert collection_number == 2
        assert collections and collection
コード例 #30
0
ファイル: coursedir.py プロジェクト: DigiKlausur/nbassignment
 def initialize(self):
     app = NbGrader()
     app.load_config_file()
     self.coursedir = NbGraderCourseDirectory()
     self.coursedir.config = app.config
     self.log.info('Loaded nbassignment coursedir')