Skip to content

Commit

Permalink
fix: fixed the code style issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Anas12091101 committed Mar 14, 2024
1 parent 1adceb0 commit e6360a0
Show file tree
Hide file tree
Showing 152 changed files with 8,216 additions and 382 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ ignore = [
"D301",
"D400",
"E902",
"FBT003",
"N803",
"N806",
"N999",
Expand Down
1 change: 1 addition & 0 deletions run_devstack_integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pip install dist/ol-openedx-git-auto-export-0.3.1.tar.gz
pip install dist/ol-openedx-logging-0.1.0.tar.gz
pip install dist/ol-openedx-rapid-response-reports-0.3.0.tar.gz
pip install dist/ol-openedx-sentry-0.1.2.tar.gz
pip install dist/rapid-response-xblock-0.9.1.tar.gz

# Install codecov so we can upload code coverage results
pip install codecov
Expand Down
9 changes: 2 additions & 7 deletions src/ol_openedx_canvas_integration/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,7 @@ def list_canvas_grades(self, assignment_id):
"""
url = urljoin(
settings.CANVAS_BASE_URL,
"/api/v1/courses/{course_id}/assignments/{assignment_id}/submissions".format(
course_id=self.canvas_course_id,
assignment_id=assignment_id,
),
f"/api/v1/courses/{self.canvas_course_id}/assignments/{assignment_id}/submissions",
)
return self._paginate(url)

Expand All @@ -153,9 +150,7 @@ def update_assignment_grades(self, canvas_assignment_id, payload):
return self.session.post(
url=urljoin(
settings.CANVAS_BASE_URL,
"/api/v1/courses/{course_id}/assignments/{assignment_id}/submissions/update_grades".format(
course_id=self.canvas_course_id, assignment_id=canvas_assignment_id
),
f"/api/v1/courses/{self.canvas_course_id}/assignments/{canvas_assignment_id}/submissions/update_grades",
),
data=payload,
)
Expand Down
7 changes: 4 additions & 3 deletions src/rapid_response_xblock/apps.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
"""AppConfig for rapid response"""
from django.apps import AppConfig
from openedx.core.djangoapps.plugins.constants import ProjectType, SettingsType
from edx_django_utils.plugins import PluginSettings, PluginURLs
from openedx.core.djangoapps.plugins.constants import ProjectType, SettingsType


class RapidResponseAppConfig(AppConfig):
"""
AppConfig for rapid response
"""

name = "rapid_response_xblock"

plugin_app = {
PluginSettings.CONFIG: {
ProjectType.LMS: {
SettingsType.COMMON: {
PluginSettings.RELATIVE_PATH: 'settings.settings'
PluginSettings.RELATIVE_PATH: "settings.settings"
},
},
ProjectType.CMS: {
SettingsType.COMMON: {
PluginSettings.RELATIVE_PATH: 'settings.cms_settings'
PluginSettings.RELATIVE_PATH: "settings.cms_settings"
},
}
},
Expand Down
153 changes: 79 additions & 74 deletions src/rapid_response_xblock/block.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
"""Rapid-response functionality"""
from datetime import datetime
import logging
from datetime import datetime
from functools import wraps
import pkg_resources

import pkg_resources
import pytz
from django.conf import settings
from django.db import transaction
from django.db.models import Count
from django.template import Context, Template
from django.utils.translation import gettext_lazy as _
import pytz
from web_fragments.fragment import Fragment
from webob.response import Response
from xblock.core import XBlock, XBlockAside
from xblock.fields import Scope, Boolean
from xmodule.modulestore.django import modulestore

from rapid_response_xblock.models import (
RapidResponseRun,
RapidResponseSubmission,
)
from web_fragments.fragment import Fragment
from webob.response import Response
from xblock.core import XBlock, XBlockAside
from xblock.fields import Boolean, Scope
from xmodule.modulestore.django import modulestore

log = logging.getLogger(__name__)

Expand All @@ -33,9 +32,9 @@ def get_resource_bytes(path):
Returns:
unicode: The unicode contents of the resource at the given path
"""
""" # noqa: D401
resource_contents = pkg_resources.resource_string(__name__, path)
return resource_contents.decode('utf-8')
return resource_contents.decode("utf-8")


def render_template(template_path, context=None):
Expand All @@ -51,7 +50,7 @@ def render_template(template_path, context=None):
def staff_only(handler_method):
"""
Wrapper that ensures a handler method is enabled for staff users only
"""
""" # noqa: D401
@wraps(handler_method)
def wrapper(aside_instance, *args, **kwargs):
if not aside_instance.is_staff():
Expand All @@ -63,34 +62,35 @@ def wrapper(aside_instance, *args, **kwargs):
return wrapper


BLOCK_PROBLEM_CATEGORY = 'problem'
MULTIPLE_CHOICE_TYPE = 'multiplechoiceresponse'
BLOCK_PROBLEM_CATEGORY = "problem"
MULTIPLE_CHOICE_TYPE = "multiplechoiceresponse"


class RapidResponseAside(XBlockAside):
"""
XBlock aside that enables rapid-response functionality for an XBlock
"""

enabled = Boolean(
display_name=_("Rapid response enabled status"),
default=False,
scope=Scope.settings,
help=_("Indicates whether or not a problem is enabled for rapid response")
)

@XBlockAside.aside_for('student_view')
def student_view_aside(self, block, context=None): # pylint: disable=unused-argument
@XBlockAside.aside_for("student_view")
def student_view_aside(self, block, context=None): # pylint: disable=unused-argument # noqa: ARG002
"""
Renders the aside contents for the student view
"""
fragment = Fragment('')
""" # noqa: D401
fragment = Fragment("")
if not self.is_staff() or not self.enabled:
return fragment
fragment.add_content(
render_template(
"static/html/rapid.html",
{
'is_open': self.has_open_run
"is_open": self.has_open_run
}
)
)
Expand All @@ -100,25 +100,25 @@ def student_view_aside(self, block, context=None): # pylint: disable=unused-arg
fragment.initialize_js("RapidResponseAsideInit")
return fragment

@XBlockAside.aside_for('author_view')
def author_view_aside(self, block, context=None): # pylint: disable=unused-argument
@XBlockAside.aside_for("author_view")
def author_view_aside(self, block, context=None): # pylint: disable=unused-argument # noqa: ARG002
"""
Renders the aside contents for the author view
"""
""" # noqa: D401
if settings.ENABLE_RAPID_RESPONSE_AUTHOR_VIEW:
return self.get_studio_fragment()
return Fragment('')
return Fragment("")

@XBlockAside.aside_for('studio_view')
def studio_view_aside(self, block, context=None): # pylint: disable=unused-argument
@XBlockAside.aside_for("studio_view")
def studio_view_aside(self, block, context=None): # pylint: disable=unused-argument # noqa: ARG002
"""
Renders the aside contents for the studio view
"""
""" # noqa: D401
return self.get_studio_fragment()

@XBlock.handler
@staff_only
def toggle_block_open_status(self, request=None, suffix=None): # pylint: disable=unused-argument
def toggle_block_open_status(self, request=None, suffix=None): # pylint: disable=unused-argument # noqa: ARG002
"""
Toggles the open/closed status for the rapid-response-enabled block
"""
Expand All @@ -139,84 +139,88 @@ def toggle_block_open_status(self, request=None, suffix=None): # pylint: disabl
)
return Response(
json_body={
'is_open': run.open,
"is_open": run.open,
}
)

@XBlock.handler
def toggle_block_enabled(self, request=None, suffix=None): # pylint: disable=unused-argument
def toggle_block_enabled(self, request=None, suffix=None): # pylint: disable=unused-argument # noqa: ARG002
"""
Toggles the enabled status for the rapid-response-enabled block
"""
self.enabled = not self.enabled
return Response(json_body={'is_enabled': self.enabled})
return Response(json_body={"is_enabled": self.enabled})

@XBlock.handler
@staff_only
def responses(self, request=None, suffix=None): # pylint: disable=unused-argument
def responses(self, request=None, suffix=None): # pylint: disable=unused-argument # noqa: ARG002
"""
Returns student responses for rapid-response-enabled block
"""
""" # noqa: D401
run_querysets = RapidResponseRun.objects.filter(
problem_usage_key=self.wrapped_block_usage_key,
course_key=self.course_key,
)
runs = self.serialize_runs(run_querysets)
# Only the most recent run should possibly be open
# If other runs are marked open due to some race condition, look at only the first.
is_open = runs[0]['open'] if runs else False
# If other runs are marked open due to some race condition, look at only the
# first
is_open = runs[0]["open"] if runs else False
choices = self.choices
counts = self.get_counts_for_problem(
[run['id'] for run in runs],
[run["id"] for run in runs],
choices,
)

total_counts = {
run['id']: sum(
counts[choice['answer_id']][run['id']] for choice in choices
run["id"]: sum(
counts[choice["answer_id"]][run["id"]] for choice in choices
) for run in runs
}

return Response(json_body={
'is_open': is_open,
'runs': runs,
'choices': choices,
'counts': counts,
'total_counts': total_counts,
'server_now': datetime.now(tz=pytz.utc).isoformat(),
"is_open": is_open,
"runs": runs,
"choices": choices,
"counts": counts,
"total_counts": total_counts,
"server_now": datetime.now(tz=pytz.utc).isoformat(),
})

@classmethod
def should_apply_to_block(cls, block):
"""
Overrides base XBlockAside implementation. Indicates whether or not this aside should
apply to a given block.
Due to the different ways that the Studio and LMS runtimes construct XBlock instances,
the problem type of the given block needs to be retrieved in different ways.
"""
if getattr(block, 'category', None) != BLOCK_PROBLEM_CATEGORY:
Overrides base XBlockAside implementation. Indicates whether or not this aside
should apply to a given block.
Due to the different ways that the Studio and LMS runtimes construct XBlock
instances, the problem type of the given block needs to be retrieved in
different ways.
""" # noqa: D401
if getattr(block, "category", None) != BLOCK_PROBLEM_CATEGORY:
return False
block_problem_types = None
# LMS passes in the block instance with `problem_types` as a property of `descriptor`
if hasattr(block, 'descriptor'):
block_problem_types = getattr(block.descriptor, 'problem_types', None)
# Studio passes in the block instance with `problem_types` as a top-level property
elif hasattr(block, 'problem_types'):
# LMS passes in the block instance with `problem_types` as a property of
# `descriptor`
if hasattr(block, "descriptor"):
block_problem_types = getattr(block.descriptor, "problem_types", None)
# Studio passes in the block instance with `problem_types` as a top-level property # noqa: E501
elif hasattr(block, "problem_types"):
block_problem_types = block.problem_types
# We only want this aside to apply to the block if the problem is multiple choice
# AND there are not multiple problem types.
# We only want this aside to apply to the block if the problem is multiple
# choice AND there are not multiple problem types.
return block_problem_types == {MULTIPLE_CHOICE_TYPE}

def get_studio_fragment(self):
"""
Generate a Studio view based aside fragment. (Used in Studio View and Author View)
Generate a Studio view based aside fragment. (Used in Studio View and Author
View)
"""
fragment = Fragment('')
fragment = Fragment("")
fragment.add_content(
render_template(
"static/html/rapid_studio.html",
{'is_enabled': self.enabled}
{"is_enabled": self.enabled}
)
)
fragment.add_css(get_resource_bytes("static/css/rapid.css"))
Expand All @@ -235,8 +239,8 @@ def course_key(self):
return self.scope_ids.usage_id.course_key

def is_staff(self):
"""Returns True if the user has staff permissions"""
return getattr(self.runtime, 'user_is_staff', False)
"""Returns True if the user has staff permissions""" # noqa: D401
return getattr(self.runtime, "user_is_staff", False)

@property
def has_open_run(self):
Expand All @@ -246,7 +250,7 @@ def has_open_run(self):
run = RapidResponseRun.objects.filter(
problem_usage_key=self.wrapped_block_usage_key,
course_key=self.course_key,
).order_by('-created').first()
).order_by("-created").first()
return run and run.open

@property
Expand All @@ -255,15 +259,16 @@ def choices(self):
Look up choices from the problem XML
Returns:
list of dict: A list of answer id/answer text dicts, in the order the choices are listed in the XML
list of dict: A list of answer id/answer text dicts, in the order the
choices are listed in the XML
"""
problem = modulestore().get_item(self.wrapped_block_usage_key)
tree = problem.lcp.tree
choice_elements = tree.xpath('//choicegroup/choice')
choice_elements = tree.xpath("//choicegroup/choice")
return [
{
'answer_id': choice.get('name'),
'answer_text': list(choice.itertext())[0] if list(choice.itertext()) else ""
"answer_id": choice.get("name"),
"answer_text": next(iter(choice.itertext())) if list(choice.itertext()) else "" # noqa: E501
}
for choice in choice_elements
]
Expand All @@ -281,9 +286,9 @@ def serialize_runs(runs):
"""
return [
{
'id': run.id,
'created': run.created.isoformat(),
'open': run.open,
"id": run.id,
"created": run.created.isoformat(),
"open": run.open,
} for run in runs
]

Expand All @@ -302,13 +307,13 @@ def get_counts_for_problem(run_ids, choices):
"""
response_data = RapidResponseSubmission.objects.filter(
run__id__in=run_ids
).values('answer_id', 'run').annotate(count=Count('answer_id'))
).values("answer_id", "run").annotate(count=Count("answer_id"))
# Make sure every answer has a count and convert to JSON serializable format
response_counts = {(item['answer_id'], item['run']): item['count'] for item in response_data}
response_counts = {(item["answer_id"], item["run"]): item["count"] for item in response_data} # noqa: E501

return {
choice['answer_id']: {
run_id: response_counts.get((choice['answer_id'], run_id), 0)
choice["answer_id"]: {
run_id: response_counts.get((choice["answer_id"], run_id), 0)
for run_id in run_ids
} for choice in choices
}
Loading

0 comments on commit e6360a0

Please sign in to comment.