Repository: e-valuation/EvaP

Issue:
Missing cache refresh on course state change
You can unpublish a course that is in the state "published". Then, you can change the courses questionnaires. If the course gets published after changing the questionnaires, the old cache will not be invalidated. If someone opens the courses result page, the old questionnaires will still be shown.
The cache entry should be deleted when a staff user unpublishes it.
Missing cache refresh on course state change
You can unpublish a course that is in the state "published". Then, you can change the courses questionnaires. If the course gets published after changing the questionnaires, the old cache will not be invalidated. If someone opens the courses result page, the old questionnaires will still be shown.
The cache entry should be deleted when a staff user unpublishes it.


Discussion:

Resolving patch:
diff --git a/evap/evaluation/models.py b/evap/evaluation/models.py
--- a/evap/evaluation/models.py
+++ b/evap/evaluation/models.py
@@ -6,6 +6,7 @@
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, Group, PermissionsMixin
+from django.core.cache import cache
 from django.core.exceptions import ValidationError
 from django.core.mail import EmailMessage
 from django.db import models, transaction
@@ -388,7 +389,8 @@ def publish(self):
 
     @transition(field=state, source='published', target='reviewed')
     def unpublish(self):
-        pass
+        from evap.results.tools import get_results_cache_key
+        cache.delete(get_results_cache_key(self))
 
     @property
     def student_state(self):
diff --git a/evap/results/tools.py b/evap/results/tools.py
--- a/evap/results/tools.py
+++ b/evap/results/tools.py
@@ -99,11 +99,15 @@ def get_counts(question, answer_counters):
     return counts
 
 
+def get_results_cache_key(course):
+    return 'evap.staff.results.tools.calculate_results-{:d}'.format(course.id)
+
+
 def calculate_results(course, force_recalculation=False):
     if course.state != "published":
         return _calculate_results_impl(course)
 
-    cache_key = 'evap.staff.results.tools.calculate_results-{:d}'.format(course.id)
+    cache_key = get_results_cache_key(course)
     if force_recalculation:
         cache.delete(cache_key)
     return cache.get_or_set(cache_key, partial(_calculate_results_impl, course), None)


Test patch:
diff --git a/evap/results/tests/test_tools.py b/evap/results/tests/test_tools.py
--- a/evap/results/tests/test_tools.py
+++ b/evap/results/tests/test_tools.py
@@ -7,7 +7,7 @@
 from model_mommy import mommy
 
 from evap.evaluation.models import Contribution, RatingAnswerCounter, Questionnaire, Question, Course, UserProfile
-from evap.results.tools import get_answers, get_answers_from_answer_counters, calculate_average_grades_and_deviation, calculate_results
+from evap.results.tools import get_answers, get_answers_from_answer_counters, get_results_cache_key, calculate_average_grades_and_deviation, calculate_results
 from evap.staff.tools import merge_users
 
 
@@ -15,11 +15,18 @@ class TestCalculateResults(TestCase):
     def test_caches_published_course(self):
         course = mommy.make(Course, state='published')
 
-        self.assertIsNone(cache.get('evap.staff.results.tools.calculate_results-{:d}'.format(course.id)))
+        self.assertIsNone(cache.get(get_results_cache_key(course)))
 
         calculate_results(course)
 
-        self.assertIsNotNone(cache.get('evap.staff.results.tools.calculate_results-{:d}'.format(course.id)))
+        self.assertIsNotNone(cache.get(get_results_cache_key(course)))
+
+    def test_cache_unpublished_course(self):
+        course = mommy.make(Course, state='published')
+        calculate_results(course)
+        course.unpublish()
+
+        self.assertIsNone(cache.get(get_results_cache_key(course)))
 
     def test_calculation_results(self):
         contributor1 = mommy.make(UserProfile)