From 060231ef7de4ecd84441cb1895685137f35040de Mon Sep 17 00:00:00 2001 From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de> Date: Fri, 29 Mar 2019 16:40:48 +0100 Subject: [PATCH] Resolve "Add the Option to give half points" --- core/migrations/0013_auto_20190308_1448.py | 18 ++++++++ core/models.py | 5 ++- core/serializers/feedback.py | 6 +++ core/tests/test_feedback.py | 29 ++++++++++++- .../AnnotatedSubmissionBottomToolbar.vue | 43 ++++++++++--------- grady/settings/default.py | 1 + 6 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 core/migrations/0013_auto_20190308_1448.py diff --git a/core/migrations/0013_auto_20190308_1448.py b/core/migrations/0013_auto_20190308_1448.py new file mode 100644 index 00000000..3f2e76d8 --- /dev/null +++ b/core/migrations/0013_auto_20190308_1448.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.4 on 2019-03-08 14:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0012_auto_20190306_1611'), + ] + + operations = [ + migrations.AlterField( + model_name='feedback', + name='score', + field=models.DecimalField(decimal_places=2, default=0, max_digits=5), + ), + ] diff --git a/core/models.py b/core/models.py index 224eca21..77ae5c3f 100644 --- a/core/models.py +++ b/core/models.py @@ -447,7 +447,7 @@ class Feedback(models.Model): origin : IntegerField Of whom was this feedback originally created. She below for the choices """ - score = models.PositiveIntegerField(default=0) + score = models.DecimalField(max_digits=5, decimal_places=2, default=0) created = models.DateTimeField(auto_now_add=True) is_final = models.BooleanField(default=False) @@ -456,6 +456,9 @@ class Feedback(models.Model): on_delete=models.CASCADE, related_name='feedback') + # the denominators that are allowed for the decimal score interpreted as a fraction + ALLOWED_DENOMINATORS = [1, 2] + # how was this feedback created ( WAS_EMPTY, diff --git a/core/serializers/feedback.py b/core/serializers/feedback.py index 94949fbb..808fa07d 100644 --- a/core/serializers/feedback.py +++ b/core/serializers/feedback.py @@ -163,6 +163,12 @@ class FeedbackSerializer(DynamicFieldsModelSerializer): raise serializers.ValidationError( f'Score has to be in range [0..{submission.type.full_score}].') + if score.as_integer_ratio()[1] not in Feedback.ALLOWED_DENOMINATORS: + raise serializers.ValidationError( + f'For fractional scores, the denominator must be one of ' + f'{Feedback.ALLOWED_DENOMINATORS}' + ) + has_full_score = score == submission.type.full_score has_feedback_lines = ('feedback_lines' in data and len(data['feedback_lines']) > 0 or diff --git a/core/tests/test_feedback.py b/core/tests/test_feedback.py index d82dcca2..ad541d8f 100644 --- a/core/tests/test_feedback.py +++ b/core/tests/test_feedback.py @@ -115,6 +115,7 @@ class FeedbackCreateTestCase(APITestCase): text=text) def setUp(self): + self.sub.refresh_from_db() self.client.force_authenticate(user=self.tutor) self.subscription = models.SubmissionSubscription.objects.create( owner=self.tutor, @@ -179,6 +180,32 @@ class FeedbackCreateTestCase(APITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(Feedback.objects.count(), 0) + def test_cannot_create_feedback_with_score_with_invalid_fractional_denominator(self): + data = { + 'score': 1.500000001, + 'is_final': False, + 'of_submission': self.assignment.submission.pk + } + self.assertEqual(Feedback.objects.count(), 0) + response = self.client.post(self.url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Feedback.objects.count(), 0) + + def test_can_create_feedback_with_half_points(self): + data = { + 'score': 0.5, + 'is_final': False, + 'of_submission': self.assignment.submission.pk, + 'feedback_lines': { + '2': { + 'text': 'Why you no learn how to code, man?' + } + } + } + self.client.post(self.url, data, format='json') + object_score = self.sub.feedback.score + self.assertEqual(object_score, 0.5) + def test_check_score_is_set_accordingly(self): data = { 'score': 5, @@ -200,7 +227,7 @@ class FeedbackCreateTestCase(APITestCase): 'is_final': False, 'of_submission': self.assignment.submission.pk, 'feedback_lines': { - '4': { + '3': { 'text': 'Nice meth!' } } diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue index 891c567b..2987961a 100644 --- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue +++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue @@ -12,32 +12,29 @@ <span>Skip this submission</span> </v-tooltip> <v-spacer /> - <v-alert class="score-alert ma-3" - color="error" - icon="warning" - :value="scoreError">{{ scoreError }}</v-alert> </v-flex> <v-flex> <v-layout wrap class="score-submit-container"> <v-flex xs5 class="score-flex"> <span class="mr-2">Score:</span> - <input class="score-text-field" + <input class="score-text-field" id="score-input" - type="number" - v-model="score" - @input="validateScore" + type="number" + step="0.5" + v-model="score" + @input="validateScore" @change="validateScore" /> <span> / {{fullScore}}</span> - <v-btn outline round flat + <v-btn outline round flat id="score-zero" - class="score-button" - @click="score=0" + class="score-button" + @click="score=0" color="red lighten-1">0 </v-btn> - <v-btn outline round flat + <v-btn outline round flat id="score-full" - @click="score=fullScore" - color="blue darken-3" + @click="score=fullScore" + color="blue darken-3" class="score-button">{{fullScore}} </v-btn> </v-flex> @@ -45,11 +42,11 @@ <v-layout> <v-flex xs4> <v-tooltip top v-if="showFinalCheckbox"> - <v-toolbar-items class="final-container" + <v-toolbar-items class="final-container" slot="activator"> <label>Final</label> - <v-checkbox slot="activator" - v-model="isFinal" + <v-checkbox slot="activator" + v-model="isFinal" class="final-checkbox" /> </v-toolbar-items> <span>Non final feedback will be sent to the reviewer.</span> @@ -57,10 +54,10 @@ </v-flex> <v-flex xs> <v-tooltip top> - <v-btn color="success" + <v-btn color="success" id="submit-feedback" - slot="activator" - :loading="loading" + slot="activator" + :loading="loading" @click="submit">Submit <v-icon>chevron_right</v-icon> </v-btn> @@ -71,6 +68,12 @@ </v-flex> </v-layout> </v-flex> + <v-flex v-if="scoreError"> + <v-alert class="score-alert ma-3" + color="error" + icon="warning" + :value="scoreError">{{ scoreError }}</v-alert> + </v-flex> </v-layout> </v-container> </template> diff --git a/grady/settings/default.py b/grady/settings/default.py index a147a3d8..36cca69c 100644 --- a/grady/settings/default.py +++ b/grady/settings/default.py @@ -150,6 +150,7 @@ REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'djangorestframework_camel_case.parser.CamelCaseJSONParser', ), + 'COERCE_DECIMAL_TO_STRING': False, } JSON_CAMEL_CASE = { -- GitLab