Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add flag to enable turnitin submission #18

Merged
merged 11 commits into from
May 9, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
Modulestore definitions for Open edX Quince release.
"""

# pylint: disable=import-error, unused-import
from xmodule.modulestore.django import modulestore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""
Modulestore tests definitions for Open edX Quince release.
"""

modulestore = object
17 changes: 17 additions & 0 deletions platform_plugin_turnitin/edxapp_wrapper/modulestore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
Modulestore generalized definitions.
"""

from importlib import import_module

from django.conf import settings


def modulestore(*args, **kwargs):
"""
Wrapper for `xmodule.modulestore.django.modulestore`
"""
backend_function = settings.PLATFORM_PLUGIN_TURNITIN_MODULESTORE_BACKEND
backend = import_module(backend_function)

return backend.modulestore(*args, **kwargs)
26 changes: 22 additions & 4 deletions platform_plugin_turnitin/extensions/filters.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Filters for the Turnitin plugin."""

from django.conf import settings
from opaque_keys.edx.keys import UsageKey
from openedx_filters import PipelineStep

from platform_plugin_turnitin.edxapp_wrapper.modulestore import modulestore


class ORASubmissionViewTurnitinWarning(PipelineStep):
"""Add warning message about Turnitin to the ORA submission view."""

def run_filter( # pylint: disable=unused-argument, disable=arguments-differ
self, context: dict, template_name: str
) -> dict:
def run_filter(self, context: dict, template_name: str) -> dict: # pylint: disable=arguments-differ
"""
Execute filter that loads the submission template with a warning message that
notifies the user that the submission will be sent to Turnitin.
Expand All @@ -20,7 +22,23 @@ def run_filter( # pylint: disable=unused-argument, disable=arguments-differ
Returns:
dict: The context dictionary and the template name.
"""
if settings.ENABLE_TURNITIN_SUBMISSION:
return {
"context": context,
"template_name": "turnitin/oa_response.html",
}

course_key = UsageKey.from_string(context["xblock_id"]).course_key
course_block = modulestore().get_course(course_key)
enable_in_course = course_block.other_course_settings.get("ENABLE_TURNITIN_SUBMISSION", False)

if enable_in_course:
return {
"context": context,
"template_name": "turnitin/oa_response.html",
}

mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
return {
"context": context,
"template_name": "turnitin/oa_response.html",
"template_name": template_name,
}
23 changes: 23 additions & 0 deletions platform_plugin_turnitin/handlers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
"""Event handlers for the Turnitin plugin."""

from django.conf import settings
from opaque_keys.edx.keys import UsageKey

from platform_plugin_turnitin.edxapp_wrapper.modulestore import modulestore
from platform_plugin_turnitin.tasks import ora_submission_created_task


def ora_submission_created(submission, **kwargs):
"""
Handle the ORA_SUBMISSION_CREATED event.

Args:
submission (ORASubmissionData): The ORA submission data.
"""
if settings.ENABLE_TURNITIN_SUBMISSION:
call_ora_submission_created_task(submission)
return

course_key = UsageKey.from_string(submission.location).course_key
course_block = modulestore().get_course(course_key)
enable_in_course = course_block.other_course_settings.get("ENABLE_TURNITIN_SUBMISSION", False)
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved

if enable_in_course:
call_ora_submission_created_task(submission)


def call_ora_submission_created_task(submission) -> None:
"""
Call the ORA submission created task.

Args:
submission (ORASubmissionData): The ORA submission data.
"""
Expand Down
11 changes: 7 additions & 4 deletions platform_plugin_turnitin/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def plugin_settings(settings):
Set of plugin settings used by the Open Edx platform.
More info: https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/plugins/README.rst
"""

settings.ENABLE_TURNITIN_SUBMISSION = False
# Configuration variables
settings.TURNITIN_TII_API_URL = None
settings.TURNITIN_TCA_INTEGRATION_FAMILY = None
Expand Down Expand Up @@ -73,14 +73,17 @@ def plugin_settings(settings):
"exclude_submitted_works": False,
},
}
# Backend settings
settings.TURNITIN_API_TIMEOUT = 30
settings.PLATFORM_PLUGIN_TURNITIN_AUTHENTICATION_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.authentication_q_v1"
)
settings.PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.student_q_v1"
)
settings.PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND = "platform_plugin_turnitin.edxapp_wrapper.backends.student_q_v1"
settings.PLATFORM_PLUGIN_TURNITIN_COURSE_OVERVIEWS_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.course_overviews_q_v1"
)
settings.PLATFORM_PLUGIN_TURNITIN_MODULESTORE_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.modulestore_q_v1"
)
# Template settings
settings.MAKO_TEMPLATE_DIRS_BASE.append(ROOT_DIRECTORY / "templates/turnitin")
23 changes: 11 additions & 12 deletions platform_plugin_turnitin/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ def plugin_settings(settings):
Set of plugin settings used by the Open Edx platform.
More info: https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/plugins/README.rst
"""
settings.ENABLE_TURNITIN_SUBMISSION = getattr(settings, "ENV_TOKENS", {}).get(
"ENABLE_TURNITIN_SUBMISSION", settings.ENABLE_TURNITIN_SUBMISSION
)
settings.TURNITIN_TII_API_URL = getattr(settings, "ENV_TOKENS", {}).get(
"TURNITIN_TII_API_URL", settings.TURNITIN_TII_API_URL
)
Expand All @@ -26,32 +29,28 @@ def plugin_settings(settings):
"TURNITIN_TCA_API_KEY", settings.TURNITIN_TCA_API_KEY
)

settings.TURNITIN_SIMILARITY_REPORT_PAYLOAD = getattr(
settings, "ENV_TOKENS", {}
).get(
settings.TURNITIN_SIMILARITY_REPORT_PAYLOAD = getattr(settings, "ENV_TOKENS", {}).get(
"TURNITIN_SIMILARITY_REPORT_PAYLOAD",
settings.TURNITIN_SIMILARITY_REPORT_PAYLOAD,
)

settings.TURNITIN_API_TIMEOUT = getattr(settings, "ENV_TOKENS", {}).get(
"TURNITIN_API_TIMEOUT", settings.TURNITIN_API_TIMEOUT
)
settings.PLATFORM_PLUGIN_TURNITIN_AUTHENTICATION_BACKEND = getattr(
settings, "ENV_TOKENS", {}
).get(
settings.PLATFORM_PLUGIN_TURNITIN_AUTHENTICATION_BACKEND = getattr(settings, "ENV_TOKENS", {}).get(
"PLATFORM_PLUGIN_TURNITIN_AUTHENTICATION_BACKEND",
settings.PLATFORM_PLUGIN_TURNITIN_AUTHENTICATION_BACKEND,
)
settings.PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND = getattr(
settings, "ENV_TOKENS", {}
).get(
settings.PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND = getattr(settings, "ENV_TOKENS", {}).get(
"PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND",
settings.PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND,
)
settings.PLATFORM_PLUGIN_TURNITIN_COURSE_OVERVIEWS_BACKEND = getattr(
settings, "ENV_TOKENS", {}
).get(
settings.PLATFORM_PLUGIN_TURNITIN_COURSE_OVERVIEWS_BACKEND = getattr(settings, "ENV_TOKENS", {}).get(
"PLATFORM_PLUGIN_TURNITIN_COURSE_OVERVIEWS_BACKEND",
settings.PLATFORM_PLUGIN_TURNITIN_COURSE_OVERVIEWS_BACKEND,
)
settings.PLATFORM_PLUGIN_TURNITIN_MODULESTORE_BACKEND = getattr(settings, "ENV_TOKENS", {}).get(
"PLATFORM_PLUGIN_TURNITIN_MODULESTORE_BACKEND",
settings.PLATFORM_PLUGIN_TURNITIN_MODULESTORE_BACKEND,
)
settings.MAKO_TEMPLATE_DIRS_BASE.append(ROOT_DIRECTORY / "templates/turnitin")
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ <h5 class="message__title">{% trans "We could not submit your response" %}</h5>
<button type="submit" class="action action--submit step--response__submit"
text_response="{{text_response}}"
file_upload_response="{{file_upload_response}}"
allow_learner_resubmissions="{{allow_learner_resubmissions}}"
>
{% trans "Submit your response and move to the next step" %}
</button>
Expand Down
60 changes: 53 additions & 7 deletions platform_plugin_turnitin/tests/test_filters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""This module contains tests for the filters module."""

from unittest import TestCase
from unittest.mock import Mock
from unittest.mock import Mock, patch

from django.test.utils import override_settings
from opaque_keys.edx.keys import UsageKey

from platform_plugin_turnitin.extensions.filters import ORASubmissionViewTurnitinWarning

Expand All @@ -10,20 +13,63 @@ class TestORASubmissionViewTurnitinWarning(TestCase):
"""Tests for the ORASubmissionViewTurnitinWarning class."""

def setUp(self) -> None:
self.pipeline_step = ORASubmissionViewTurnitinWarning(
filter_type=Mock(), running_pipeline=Mock()
)
self.context = {"key": "value"}
self.pipeline_step = ORASubmissionViewTurnitinWarning(filter_type=Mock(), running_pipeline=Mock())
self.context = {"key": "value", "xblock_id": "test_xblock_id"}
self.template_name = "template_name"
self.new_template_name = "turnitin/oa_response.html"

def test_run_filter(self):
@patch("platform_plugin_turnitin.extensions.filters.modulestore")
@patch.object(UsageKey, "from_string")
def test_run_filter_turnitin_submission_disabled(self, mock_from_string: Mock, mock_modulestore: Mock):
"""
Test `run_filter` method when Turnitin submission is disabled.

Expected result: The dictionary contains the same context and template name.
"""
mock_from_string.return_value.course_key = "test_course_key"
mock_modulestore.return_value.get_course.return_value = Mock(
other_course_settings={"ENABLE_TURNITIN_SUBMISSION": False},
)

result = self.pipeline_step.run_filter(self.context, self.template_name)

self.assertEqual(result["context"], self.context)
self.assertEqual(result["template_name"], self.template_name)

@patch("platform_plugin_turnitin.extensions.filters.modulestore")
@patch.object(UsageKey, "from_string")
@override_settings(ENABLE_TURNITIN_SUBMISSION=True)
def test_run_filter_turnitin_submission_enabled_by_global_setting(
self, mock_from_string: Mock, mock_modulestore: Mock
):
"""
Test that the `run_filter` method returns a dictionary with the context and the new template name.
Test `run_filter` method when Turnitin submission is enabled by the global setting.

Expected result: The dictionary contains the context and the new template name.
"""
result = self.pipeline_step.run_filter(self.context, self.template_name)

self.assertEqual(result["context"], self.context)
self.assertEqual(result["template_name"], self.new_template_name)
mock_from_string.assert_not_called()
mock_modulestore.assert_not_called()

@patch("platform_plugin_turnitin.extensions.filters.modulestore")
@patch.object(UsageKey, "from_string")
def test_run_filter_turnitin_submission_enabled_by_course_setting(
self, mock_from_string: Mock, mock_modulestore: Mock
):
"""
Test `run_filter` method when Turnitin submission is enabled by the course setting.

Expected result: The dictionary contains the context and the new template name.
"""
mock_from_string.return_value.course_key = "test_course_key"
mock_modulestore.return_value.get_course.return_value = Mock(
other_course_settings={"ENABLE_TURNITIN_SUBMISSION": True},
)

result = self.pipeline_step.run_filter(self.context, self.template_name)

self.assertEqual(result["context"], self.context)
self.assertEqual(result["template_name"], self.new_template_name)
55 changes: 55 additions & 0 deletions platform_plugin_turnitin/tests/test_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Tests for the handlers module."""

from unittest.mock import Mock, patch

from django.test import TestCase
from django.test.utils import override_settings

from platform_plugin_turnitin.handlers import ora_submission_created


class TestHandlers(TestCase):
"""Tests for the handlers module."""

def setUp(self) -> None:
self.submission = Mock(location="test_block_id")

@patch("platform_plugin_turnitin.handlers.call_ora_submission_created_task")
@patch("platform_plugin_turnitin.handlers.modulestore")
@patch("platform_plugin_turnitin.handlers.UsageKey")
def test_ora_submission_created_all_disabled(
self, mock_usage_key: Mock, mock_modulestore: Mock, mock_call_task: Mock
):
"""Test `ora_submission_created` when Turnitin submission is disabled globally and for the course."""
mock_usage_key.from_string.return_value.course_key = "course_key"
mock_modulestore.return_value.get_course.return_value = Mock(
other_course_settings={"ENABLE_TURNITIN_SUBMISSION": False},
)

ora_submission_created(self.submission)

mock_call_task.assert_not_called()

@override_settings(ENABLE_TURNITIN_SUBMISSION=True)
@patch("platform_plugin_turnitin.handlers.call_ora_submission_created_task")
def test_ora_submission_created_global_enabled(self, mock_call_task: Mock):
"""Test `ora_submission_created` when Turnitin submission is enabled globally."""
ora_submission_created(self.submission)

mock_call_task.assert_called_once_with(self.submission)

@patch("platform_plugin_turnitin.handlers.call_ora_submission_created_task")
@patch("platform_plugin_turnitin.handlers.modulestore")
@patch("platform_plugin_turnitin.handlers.UsageKey")
def test_ora_submission_created_course_enabled(
self, mock_usage_key: Mock, mock_modulestore: Mock, mock_call_task: Mock
):
"""Test `ora_submission_created` when Turnitin submission is enabled for the course."""
mock_usage_key.from_string.return_value.course_key = "course_key"
mock_modulestore.return_value.get_course.return_value = Mock(
other_course_settings={"ENABLE_TURNITIN_SUBMISSION": True},
)

ora_submission_created(self.submission)

mock_call_task.assert_called_once_with(self.submission)
6 changes: 3 additions & 3 deletions test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ def root(*args):
]

# Plugin settings
ENABLE_TURNITIN_SUBMISSION = False
PLATFORM_PLUGIN_TURNITIN_AUTHENTICATION_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.authentication_q_v1_test"
)
PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.student_q_v1_test"
)
PLATFORM_PLUGIN_TURNITIN_STUDENT_BACKEND = "platform_plugin_turnitin.edxapp_wrapper.backends.student_q_v1_test"
PLATFORM_PLUGIN_TURNITIN_COURSE_OVERVIEWS_BACKEND = (
"platform_plugin_turnitin.edxapp_wrapper.backends.course_overviews_q_v1_test"
)
PLATFORM_PLUGIN_TURNITIN_MODULESTORE_BACKEND = "platform_plugin_turnitin.edxapp_wrapper.backends.modulestore_q_v1_test"
TURNITIN_SIMILARITY_REPORT_PAYLOAD = {"test_key": "test_value"}
Loading