Repository: e-valuation/EvaP

Issue:
Sending emails in update_course_states fails
The management command `update_course_states` fails when sending emails for courses where the evaluation started. This might be caused by changes in 36047e02235c5cca25b9519ec6bc2390cdee3e12.
```
Management command 'update_course_states' failed. Traceback follows: 

AttributeError
'str' object has no attribute 'subject'

Traceback:  

File "/opt/evap/evap/evaluation/management/commands/tools.py" in handle
  16.                 super().handle(args, options)

File "/opt/evap/evap/evaluation/management/commands/update_course_states.py" in handle
  12.         Course.update_courses()

File "/opt/evap/evap/evaluation/models.py" in update_courses
  556.                                                [EmailTemplate.ALL_PARTICIPANTS], use_cc=False, request=None)

File "/opt/evap/evap/evaluation/models.py" in send_to_users_in_courses
  1127.             cls.send_to_user(user, template, subject_params, body_params, use_cc=use_cc, request=request)

File "/opt/evap/evap/evaluation/models.py" in send_to_user
  1153.         subject = cls.__render_string(template.subject, subject_params)

Exception Type: AttributeError
Exception Value: 'str' object has no attribute 'subject'

```
Sending emails in update_course_states fails
The management command `update_course_states` fails when sending emails for courses where the evaluation started. This might be caused by changes in 36047e02235c5cca25b9519ec6bc2390cdee3e12.
```
Management command 'update_course_states' failed. Traceback follows: 

AttributeError
'str' object has no attribute 'subject'

Traceback:  

File "/opt/evap/evap/evaluation/management/commands/tools.py" in handle
  16.                 super().handle(args, options)

File "/opt/evap/evap/evaluation/management/commands/update_course_states.py" in handle
  12.         Course.update_courses()

File "/opt/evap/evap/evaluation/models.py" in update_courses
  556.                                                [EmailTemplate.ALL_PARTICIPANTS], use_cc=False, request=None)

File "/opt/evap/evap/evaluation/models.py" in send_to_users_in_courses
  1127.             cls.send_to_user(user, template, subject_params, body_params, use_cc=use_cc, request=request)

File "/opt/evap/evap/evaluation/models.py" in send_to_user
  1153.         subject = cls.__render_string(template.subject, subject_params)

Exception Type: AttributeError
Exception Value: 'str' object has no attribute 'subject'

```


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
@@ -552,8 +552,8 @@ def update_courses(cls):
             except Exception:
                 logger.exception('An error occured when updating the state of course "{}" (id {}).'.format(course, course.id))
 
-        EmailTemplate.send_to_users_in_courses(EmailTemplate.EVALUATION_STARTED, courses_new_in_evaluation,
-                                               [EmailTemplate.ALL_PARTICIPANTS], use_cc=False, request=None)
+        template = EmailTemplate.objects.get(name=EmailTemplate.EVALUATION_STARTED)
+        EmailTemplate.send_to_users_in_courses(template, courses_new_in_evaluation, [EmailTemplate.ALL_PARTICIPANTS], use_cc=False, request=None)
         send_publish_notifications(evaluation_results_courses)
         logger.info("update_courses finished.")
 


Test patch:
diff --git a/evap/evaluation/tests/test_models.py b/evap/evaluation/tests/test_models.py
--- a/evap/evaluation/tests/test_models.py
+++ b/evap/evaluation/tests/test_models.py
@@ -20,7 +20,8 @@ def test_approved_to_in_evaluation(self):
         with patch('evap.evaluation.models.EmailTemplate.send_to_users_in_courses') as mock:
             Course.update_courses()
 
-        mock.assert_called_once_with(EmailTemplate.EVALUATION_STARTED, [course], [EmailTemplate.ALL_PARTICIPANTS],
+        template = EmailTemplate.objects.get(name=EmailTemplate.EVALUATION_STARTED)
+        mock.assert_called_once_with(template, [course], [EmailTemplate.ALL_PARTICIPANTS],
                                      use_cc=False, request=None)
 
         course = Course.objects.get(pk=course.pk)
@@ -58,6 +59,17 @@ def test_in_evaluation_to_published(self):
         course = Course.objects.get(pk=course.pk)
         self.assertEqual(course.state, 'published')
 
+    def test_approved_to_in_evaluation_sends_emails(self):
+        """ Regression test for #945 """
+        participant = mommy.make(UserProfile, email='foo@example.com')
+        course = mommy.make(Course, state='approved', vote_start_date=date.today(), participants=[participant])
+
+        Course.update_courses()
+
+        course = Course.objects.get(pk=course.pk)
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertEqual(course.state, 'in_evaluation')
+
     def test_has_enough_questionnaires(self):
         # manually circumvent Course's save() method to have a Course without a general contribution
         # the semester must be specified because of https://github.com/vandersonmota/model_mommy/issues/258
@@ -73,7 +85,7 @@ def test_has_enough_questionnaires(self):
         self.assertFalse(course.has_enough_questionnaires)
 
         general_contribution = mommy.make(Contribution, course=course, contributor=None)
-        course = Course.objects.get()  # refresh because of cached properties
+        course = Course.objects.get()
         self.assertFalse(course.has_enough_questionnaires)
 
         questionnaire = mommy.make(Questionnaire)