feat(numericalInput): endpoint added to validate a numerical input (#37677)
To be used in the visual Problem editor to validate numeric input. Part of: https://github.com/openedx/frontend-app-authoring/issues/1680
This commit is contained in:
17
cms/djangoapps/contentstore/rest_api/v2/serializers/utils.py
Normal file
17
cms/djangoapps/contentstore/rest_api/v2/serializers/utils.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Serializers for the contentstore v2 utils views module.
|
||||
|
||||
This module contains DRF serializers for different utils like validations.
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class NumericalInputValidationRequestSerializer(serializers.Serializer):
|
||||
formula = serializers.CharField()
|
||||
|
||||
|
||||
class NumericalInputValidationReponseSerializer(serializers.Serializer):
|
||||
preview = serializers.CharField()
|
||||
is_valid = serializers.BooleanField()
|
||||
error = serializers.CharField(allow_null=True)
|
||||
@@ -3,7 +3,7 @@
|
||||
from django.conf import settings
|
||||
from django.urls import path, re_path
|
||||
|
||||
from cms.djangoapps.contentstore.rest_api.v2.views import downstreams, home
|
||||
from cms.djangoapps.contentstore.rest_api.v2.views import downstreams, home, utils
|
||||
|
||||
app_name = "v2"
|
||||
|
||||
@@ -33,4 +33,8 @@ urlpatterns = [
|
||||
downstreams.SyncFromUpstreamView.as_view(),
|
||||
name="sync_from_upstream"
|
||||
),
|
||||
re_path(
|
||||
'^validate/numerical-input/$',
|
||||
utils.NumericalInputValidationView.as_view(),
|
||||
name='numerical_input_validation'),
|
||||
]
|
||||
|
||||
22
cms/djangoapps/contentstore/rest_api/v2/views/utils.py
Normal file
22
cms/djangoapps/contentstore/rest_api/v2/views/utils.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
Common utilities for V2 APIs.
|
||||
"""
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework import permissions
|
||||
from cms.djangoapps.contentstore.rest_api.v2.serializers.utils import NumericalInputValidationRequestSerializer
|
||||
from xmodule.capa.inputtypes import preview_numeric_input
|
||||
|
||||
|
||||
class NumericalInputValidationView(GenericAPIView):
|
||||
"""Class in charge of NumericalInputValidations"""
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
serializer_class = NumericalInputValidationRequestSerializer
|
||||
|
||||
def post(self, request):
|
||||
"""function to validate a math expression (formula) and return of the numeric input is valid or not"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
formula = serializer.validated_data['formula']
|
||||
result = preview_numeric_input(formula)
|
||||
return Response(result, status=200)
|
||||
@@ -49,9 +49,9 @@ from datetime import datetime
|
||||
|
||||
import html5lib
|
||||
import nh3
|
||||
import pyparsing
|
||||
import six
|
||||
from calc.preview import latex_preview
|
||||
import pyparsing
|
||||
from chem import chemcalc
|
||||
from lxml import etree
|
||||
|
||||
@@ -1300,22 +1300,47 @@ class FormulaEquationInput(InputTypeBase):
|
||||
|
||||
result["request_start"] = int(get.get("request_start", 0))
|
||||
|
||||
# TODO add references to valid variables and functions
|
||||
# At some point, we might want to mark invalid variables as red
|
||||
# or something, and this is where we would need to pass those in.
|
||||
try:
|
||||
# TODO add references to valid variables and functions
|
||||
# At some point, we might want to mark invalid variables as red
|
||||
# or something, and this is where we would need to pass those in.
|
||||
result["preview"] = latex_preview(formula)
|
||||
numeric_result = preview_numeric_input(formula)
|
||||
# Map results into the correct format
|
||||
result["preview"] = numeric_result["preview"]
|
||||
if numeric_result["error"]:
|
||||
result["error"] = numeric_result["error"]
|
||||
# if formula is invalid return formula
|
||||
if not numeric_result["is_valid"]:
|
||||
result["formula"] = formula
|
||||
except pyparsing.ParseException:
|
||||
result["error"] = _("Sorry, couldn't parse formula")
|
||||
result["formula"] = formula
|
||||
result['error'] = _("Sorry, couldn't parse formula")
|
||||
result['formula'] = formula
|
||||
except Exception: # lint-amnesty, pylint: disable=broad-except
|
||||
# this is unexpected, so log
|
||||
log.warning("Error while previewing formula", exc_info=True)
|
||||
result["error"] = _("Error while rendering preview")
|
||||
return result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def preview_numeric_input(formula):
|
||||
"""
|
||||
Handles numeric validations, validates that the formula provided is a valid formula.
|
||||
"""
|
||||
result = {'preview': '', 'is_valid': True, 'error': ''}
|
||||
try:
|
||||
result['preview'] = latex_preview(formula)
|
||||
except pyparsing.ParseException:
|
||||
result["error"] = "Sorry, couldn't parse formula"
|
||||
result['is_valid'] = False
|
||||
return result
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
log.warning("Error while previewing formula", exc_info=True)
|
||||
result['error'] = "Error while rendering preview"
|
||||
result['is_valid'] = False
|
||||
return result
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@@ -1361,7 +1361,7 @@ class FormulaEquationTest(unittest.TestCase):
|
||||
With parse errors, FormulaEquationInput should give an error message
|
||||
"""
|
||||
# Simulate answering a problem that raises the exception
|
||||
with patch("xmodule.capa.inputtypes.latex_preview") as mock_preview:
|
||||
with patch('xmodule.capa.inputtypes.preview_numeric_input') as mock_preview:
|
||||
mock_preview.side_effect = ParseException("Oopsie")
|
||||
response = self.the_input.handle_ajax(
|
||||
"preview_formcalc",
|
||||
@@ -1379,7 +1379,7 @@ class FormulaEquationTest(unittest.TestCase):
|
||||
"""
|
||||
With other errors, test that FormulaEquationInput also logs it
|
||||
"""
|
||||
with patch("xmodule.capa.inputtypes.latex_preview") as mock_preview:
|
||||
with patch('xmodule.capa.inputtypes.preview_numeric_input') as mock_preview:
|
||||
mock_preview.side_effect = Exception()
|
||||
response = self.the_input.handle_ajax(
|
||||
"preview_formcalc",
|
||||
|
||||
Reference in New Issue
Block a user