We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix results view for courses where results can't be published
The results page for a course where the results can't be published because it didn't get enough votes is not displayed correctly:
- Contributors can't see any non-text answers (that's correct) - but not all contributor cards are collapsed accordingly. If at least one vote exists, the respective contributor card is open, the warning text is not shown and no useful information is shown inside the card (see screenshot 1). The card should be closed in this case.
- Staff users initially see the "public" view and can change the view to `Myself`. On results pages of courses that didn't get enough votes, the `Public` button is missing (that was by design, because a public view does not exist for these courses) - now it's confusing because the initial view is something that's not accessible via the buttons.

Screenshot 1 (Contributor):
![cardopen](https://user-images.githubusercontent.com/1781719/34488419-c82cd8d0-efd8-11e7-8f09-0c4ff0f06739.PNG)

Screenshot 2 (Staff):
![viewbtn](https://user-images.githubusercontent.com/1781719/34488426-cd684118-efd8-11e7-80ba-c05e1ff34f93.PNG)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `evap/results/views.py`
Content:
```
1 from collections import OrderedDict, namedtuple
2 
3 from django.core.exceptions import PermissionDenied
4 from django.shortcuts import get_object_or_404, render
5 from django.contrib.auth.decorators import login_required
6 
7 from evap.evaluation.models import Semester, Degree, Contribution
8 from evap.evaluation.auth import internal_required
9 from evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY
10 
11 
12 @internal_required
13 def index(request):
14     semesters = Semester.get_all_with_published_courses()
15 
16     return render(request, "results_index.html", dict(semesters=semesters))
17 
18 
19 @internal_required
20 def semester_detail(request, semester_id):
21     semester = get_object_or_404(Semester, id=semester_id)
22     if request.user.is_reviewer:
23         courses = list(semester.course_set.filter(state__in=["in_evaluation", "evaluated", "reviewed", "published"]).prefetch_related("degrees"))
24     else:
25         courses = list(semester.course_set.filter(state="published").prefetch_related("degrees"))
26 
27     courses = [course for course in courses if course.can_user_see_course(request.user)]
28 
29     # Annotate each course object with its grades.
30     for course in courses:
31         course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)
32 
33     CourseTuple = namedtuple('CourseTuple', ('courses', 'single_results'))
34 
35     courses_by_degree = OrderedDict()
36     for degree in Degree.objects.all():
37         courses_by_degree[degree] = CourseTuple([], [])
38     for course in courses:
39         if course.is_single_result:
40             for degree in course.degrees.all():
41                 section = calculate_results(course)[0]
42                 result = section.results[0]
43                 courses_by_degree[degree].single_results.append((course, result))
44         else:
45             for degree in course.degrees.all():
46                 courses_by_degree[degree].courses.append(course)
47 
48     template_data = dict(semester=semester, courses_by_degree=courses_by_degree)
49     return render(request, "results_semester_detail.html", template_data)
50 
51 
52 @login_required
53 def course_detail(request, semester_id, course_id):
54     semester = get_object_or_404(Semester, id=semester_id)
55     course = get_object_or_404(semester.course_set, id=course_id, semester=semester)
56 
57     if not course.can_user_see_results(request.user):
58         raise PermissionDenied
59 
60     sections = calculate_results(course)
61 
62     if request.user.is_reviewer:
63         public_view = request.GET.get('public_view') != 'false'  # if parameter is not given, show public view.
64     else:
65         public_view = request.GET.get('public_view') == 'true'  # if parameter is not given, show own view.
66 
67     represented_users = list(request.user.represented_users.all())
68     represented_users.append(request.user)
69 
70     # filter text answers
71     for section in sections:
72         results = []
73         for result in section.results:
74             if isinstance(result, TextResult):
75                 answers = [answer for answer in result.answers if user_can_see_text_answer(request.user, represented_users, answer, public_view)]
76                 if answers:
77                     results.append(TextResult(question=result.question, answers=answers))
78             else:
79                 results.append(result)
80         section.results[:] = results
81 
82     # filter empty headings
83     for section in sections:
84         filtered_results = []
85         for index in range(len(section.results)):
86             result = section.results[index]
87             # filter out if there are no more questions or the next question is also a heading question
88             if isinstance(result, HeadingResult):
89                 if index == len(section.results) - 1 or isinstance(section.results[index + 1], HeadingResult):
90                     continue
91             filtered_results.append(result)
92         section.results[:] = filtered_results
93 
94     # remove empty sections
95     sections = [section for section in sections if section.results]
96 
97     # group by contributor
98     course_sections = []
99     contributor_sections = OrderedDict()
100     for section in sections:
101         if not section.results:
102             continue
103         if section.contributor is None:
104             course_sections.append(section)
105         else:
106             contributor_sections.setdefault(section.contributor,
107                                             {'total_votes': 0, 'sections': []})['sections'].append(section)
108 
109             # Sum up all Sections for this contributor.
110             # If section is not a RatingResult:
111             # Add 1 as we assume it is a TextResult or something similar that should be displayed.
112             contributor_sections[section.contributor]['total_votes'] +=\
113                 sum([s.total_count if isinstance(s, RatingResult) else 1 for s in section.results])
114 
115     # Show a warning if course is still in evaluation (for reviewer preview).
116     evaluation_warning = course.state != 'published'
117 
118     # Results for a course might not be visible because there are not enough answers
119     # but it can still be "published" e.g. to show the comment results to contributors.
120     # Users who can open the results page see a warning message in this case.
121     sufficient_votes_warning = not course.can_publish_grades
122 
123     show_grades = request.user.is_reviewer or course.can_publish_grades
124 
125     course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)
126 
127     template_data = dict(
128             course=course,
129             course_sections=course_sections,
130             contributor_sections=contributor_sections,
131             evaluation_warning=evaluation_warning,
132             sufficient_votes_warning=sufficient_votes_warning,
133             show_grades=show_grades,
134             reviewer=request.user.is_reviewer,
135             contributor=course.is_user_contributor_or_delegate(request.user),
136             can_download_grades=request.user.can_download_grades,
137             public_view=public_view)
138     return render(request, "results_course_detail.html", template_data)
139 
140 
141 def user_can_see_text_answer(user, represented_users, text_answer, public_view=False):
142     if public_view:
143         return False
144     if text_answer.state not in COMMENT_STATES_REQUIRED_FOR_VISIBILITY:
145         return False
146     if user.is_reviewer:
147         return True
148 
149     contributor = text_answer.contribution.contributor
150 
151     if text_answer.is_private:
152         return contributor == user
153 
154     if text_answer.is_published:
155         if text_answer.contribution.responsible:
156             return contributor == user or user in contributor.delegates.all()
157 
158         if contributor in represented_users:
159             return True
160         if text_answer.contribution.course.contributions.filter(
161                 contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():
162             return True
163         if text_answer.contribution.is_general and text_answer.contribution.course.contributions.filter(
164                 contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():
165             return True
166 
167     return False
168
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/evap/results/views.py b/evap/results/views.py
--- a/evap/results/views.py
+++ b/evap/results/views.py
@@ -6,7 +6,8 @@
 
 from evap.evaluation.models import Semester, Degree, Contribution
 from evap.evaluation.auth import internal_required
-from evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY
+from evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, \
+    HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY, YesNoResult
 
 
 @internal_required
@@ -64,9 +65,15 @@
     else:
         public_view = request.GET.get('public_view') == 'true'  # if parameter is not given, show own view.
 
+    # If grades are not published, there is no public view
+    if not course.can_publish_grades:
+        public_view = False
+
     represented_users = list(request.user.represented_users.all())
     represented_users.append(request.user)
 
+    show_grades = request.user.is_reviewer or course.can_publish_grades
+
     # filter text answers
     for section in sections:
         results = []
@@ -106,11 +113,13 @@
             contributor_sections.setdefault(section.contributor,
                                             {'total_votes': 0, 'sections': []})['sections'].append(section)
 
-            # Sum up all Sections for this contributor.
-            # If section is not a RatingResult:
-            # Add 1 as we assume it is a TextResult or something similar that should be displayed.
-            contributor_sections[section.contributor]['total_votes'] +=\
-                sum([s.total_count if isinstance(s, RatingResult) else 1 for s in section.results])
+            for result in section.results:
+                if isinstance(result, TextResult):
+                    contributor_sections[section.contributor]['total_votes'] += 1
+                elif isinstance(result, RatingResult) or isinstance(result, YesNoResult):
+                    # Only count rating results if we show the grades.
+                    if show_grades:
+                        contributor_sections[section.contributor]['total_votes'] += result.total_count
 
     # Show a warning if course is still in evaluation (for reviewer preview).
     evaluation_warning = course.state != 'published'
@@ -120,8 +129,6 @@
     # Users who can open the results page see a warning message in this case.
     sufficient_votes_warning = not course.can_publish_grades
 
-    show_grades = request.user.is_reviewer or course.can_publish_grades
-
     course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)
 
     template_data = dict(

{"golden_diff": "diff --git a/evap/results/views.py b/evap/results/views.py\n--- a/evap/results/views.py\n+++ b/evap/results/views.py\n@@ -6,7 +6,8 @@\n \n from evap.evaluation.models import Semester, Degree, Contribution\n from evap.evaluation.auth import internal_required\n-from evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY\n+from evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, \\\n+    HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY, YesNoResult\n \n \n @internal_required\n@@ -64,9 +65,15 @@\n     else:\n         public_view = request.GET.get('public_view') == 'true'  # if parameter is not given, show own view.\n \n+    # If grades are not published, there is no public view\n+    if not course.can_publish_grades:\n+        public_view = False\n+\n     represented_users = list(request.user.represented_users.all())\n     represented_users.append(request.user)\n \n+    show_grades = request.user.is_reviewer or course.can_publish_grades\n+\n     # filter text answers\n     for section in sections:\n         results = []\n@@ -106,11 +113,13 @@\n             contributor_sections.setdefault(section.contributor,\n                                             {'total_votes': 0, 'sections': []})['sections'].append(section)\n \n-            # Sum up all Sections for this contributor.\n-            # If section is not a RatingResult:\n-            # Add 1 as we assume it is a TextResult or something similar that should be displayed.\n-            contributor_sections[section.contributor]['total_votes'] +=\\\n-                sum([s.total_count if isinstance(s, RatingResult) else 1 for s in section.results])\n+            for result in section.results:\n+                if isinstance(result, TextResult):\n+                    contributor_sections[section.contributor]['total_votes'] += 1\n+                elif isinstance(result, RatingResult) or isinstance(result, YesNoResult):\n+                    # Only count rating results if we show the grades.\n+                    if show_grades:\n+                        contributor_sections[section.contributor]['total_votes'] += result.total_count\n \n     # Show a warning if course is still in evaluation (for reviewer preview).\n     evaluation_warning = course.state != 'published'\n@@ -120,8 +129,6 @@\n     # Users who can open the results page see a warning message in this case.\n     sufficient_votes_warning = not course.can_publish_grades\n \n-    show_grades = request.user.is_reviewer or course.can_publish_grades\n-\n     course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)\n \n     template_data = dict(\n", "issue": "Fix results view for courses where results can't be published\nThe results page for a course where the results can't be published because it didn't get enough votes is not displayed correctly:\r\n- Contributors can't see any non-text answers (that's correct) - but not all contributor cards are collapsed accordingly. If at least one vote exists, the respective contributor card is open, the warning text is not shown and no useful information is shown inside the card (see screenshot 1). The card should be closed in this case.\r\n- Staff users initially see the \"public\" view and can change the view to `Myself`. On results pages of courses that didn't get enough votes, the `Public` button is missing (that was by design, because a public view does not exist for these courses) - now it's confusing because the initial view is something that's not accessible via the buttons.\r\n\r\nScreenshot 1 (Contributor):\r\n![cardopen](https://user-images.githubusercontent.com/1781719/34488419-c82cd8d0-efd8-11e7-8f09-0c4ff0f06739.PNG)\r\n\r\nScreenshot 2 (Staff):\r\n![viewbtn](https://user-images.githubusercontent.com/1781719/34488426-cd684118-efd8-11e7-80ba-c05e1ff34f93.PNG)\r\n\n", "before_files": [{"content": "from collections import OrderedDict, namedtuple\n\nfrom django.core.exceptions import PermissionDenied\nfrom django.shortcuts import get_object_or_404, render\nfrom django.contrib.auth.decorators import login_required\n\nfrom evap.evaluation.models import Semester, Degree, Contribution\nfrom evap.evaluation.auth import internal_required\nfrom evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY\n\n\n@internal_required\ndef index(request):\n    semesters = Semester.get_all_with_published_courses()\n\n    return render(request, \"results_index.html\", dict(semesters=semesters))\n\n\n@internal_required\ndef semester_detail(request, semester_id):\n    semester = get_object_or_404(Semester, id=semester_id)\n    if request.user.is_reviewer:\n        courses = list(semester.course_set.filter(state__in=[\"in_evaluation\", \"evaluated\", \"reviewed\", \"published\"]).prefetch_related(\"degrees\"))\n    else:\n        courses = list(semester.course_set.filter(state=\"published\").prefetch_related(\"degrees\"))\n\n    courses = [course for course in courses if course.can_user_see_course(request.user)]\n\n    # Annotate each course object with its grades.\n    for course in courses:\n        course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)\n\n    CourseTuple = namedtuple('CourseTuple', ('courses', 'single_results'))\n\n    courses_by_degree = OrderedDict()\n    for degree in Degree.objects.all():\n        courses_by_degree[degree] = CourseTuple([], [])\n    for course in courses:\n        if course.is_single_result:\n            for degree in course.degrees.all():\n                section = calculate_results(course)[0]\n                result = section.results[0]\n                courses_by_degree[degree].single_results.append((course, result))\n        else:\n            for degree in course.degrees.all():\n                courses_by_degree[degree].courses.append(course)\n\n    template_data = dict(semester=semester, courses_by_degree=courses_by_degree)\n    return render(request, \"results_semester_detail.html\", template_data)\n\n\n@login_required\ndef course_detail(request, semester_id, course_id):\n    semester = get_object_or_404(Semester, id=semester_id)\n    course = get_object_or_404(semester.course_set, id=course_id, semester=semester)\n\n    if not course.can_user_see_results(request.user):\n        raise PermissionDenied\n\n    sections = calculate_results(course)\n\n    if request.user.is_reviewer:\n        public_view = request.GET.get('public_view') != 'false'  # if parameter is not given, show public view.\n    else:\n        public_view = request.GET.get('public_view') == 'true'  # if parameter is not given, show own view.\n\n    represented_users = list(request.user.represented_users.all())\n    represented_users.append(request.user)\n\n    # filter text answers\n    for section in sections:\n        results = []\n        for result in section.results:\n            if isinstance(result, TextResult):\n                answers = [answer for answer in result.answers if user_can_see_text_answer(request.user, represented_users, answer, public_view)]\n                if answers:\n                    results.append(TextResult(question=result.question, answers=answers))\n            else:\n                results.append(result)\n        section.results[:] = results\n\n    # filter empty headings\n    for section in sections:\n        filtered_results = []\n        for index in range(len(section.results)):\n            result = section.results[index]\n            # filter out if there are no more questions or the next question is also a heading question\n            if isinstance(result, HeadingResult):\n                if index == len(section.results) - 1 or isinstance(section.results[index + 1], HeadingResult):\n                    continue\n            filtered_results.append(result)\n        section.results[:] = filtered_results\n\n    # remove empty sections\n    sections = [section for section in sections if section.results]\n\n    # group by contributor\n    course_sections = []\n    contributor_sections = OrderedDict()\n    for section in sections:\n        if not section.results:\n            continue\n        if section.contributor is None:\n            course_sections.append(section)\n        else:\n            contributor_sections.setdefault(section.contributor,\n                                            {'total_votes': 0, 'sections': []})['sections'].append(section)\n\n            # Sum up all Sections for this contributor.\n            # If section is not a RatingResult:\n            # Add 1 as we assume it is a TextResult or something similar that should be displayed.\n            contributor_sections[section.contributor]['total_votes'] +=\\\n                sum([s.total_count if isinstance(s, RatingResult) else 1 for s in section.results])\n\n    # Show a warning if course is still in evaluation (for reviewer preview).\n    evaluation_warning = course.state != 'published'\n\n    # Results for a course might not be visible because there are not enough answers\n    # but it can still be \"published\" e.g. to show the comment results to contributors.\n    # Users who can open the results page see a warning message in this case.\n    sufficient_votes_warning = not course.can_publish_grades\n\n    show_grades = request.user.is_reviewer or course.can_publish_grades\n\n    course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)\n\n    template_data = dict(\n            course=course,\n            course_sections=course_sections,\n            contributor_sections=contributor_sections,\n            evaluation_warning=evaluation_warning,\n            sufficient_votes_warning=sufficient_votes_warning,\n            show_grades=show_grades,\n            reviewer=request.user.is_reviewer,\n            contributor=course.is_user_contributor_or_delegate(request.user),\n            can_download_grades=request.user.can_download_grades,\n            public_view=public_view)\n    return render(request, \"results_course_detail.html\", template_data)\n\n\ndef user_can_see_text_answer(user, represented_users, text_answer, public_view=False):\n    if public_view:\n        return False\n    if text_answer.state not in COMMENT_STATES_REQUIRED_FOR_VISIBILITY:\n        return False\n    if user.is_reviewer:\n        return True\n\n    contributor = text_answer.contribution.contributor\n\n    if text_answer.is_private:\n        return contributor == user\n\n    if text_answer.is_published:\n        if text_answer.contribution.responsible:\n            return contributor == user or user in contributor.delegates.all()\n\n        if contributor in represented_users:\n            return True\n        if text_answer.contribution.course.contributions.filter(\n                contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():\n            return True\n        if text_answer.contribution.is_general and text_answer.contribution.course.contributions.filter(\n                contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():\n            return True\n\n    return False\n", "path": "evap/results/views.py"}], "after_files": [{"content": "from collections import OrderedDict, namedtuple\n\nfrom django.core.exceptions import PermissionDenied\nfrom django.shortcuts import get_object_or_404, render\nfrom django.contrib.auth.decorators import login_required\n\nfrom evap.evaluation.models import Semester, Degree, Contribution\nfrom evap.evaluation.auth import internal_required\nfrom evap.results.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult, \\\n    HeadingResult, COMMENT_STATES_REQUIRED_FOR_VISIBILITY, YesNoResult\n\n\n@internal_required\ndef index(request):\n    semesters = Semester.get_all_with_published_courses()\n\n    return render(request, \"results_index.html\", dict(semesters=semesters))\n\n\n@internal_required\ndef semester_detail(request, semester_id):\n    semester = get_object_or_404(Semester, id=semester_id)\n    if request.user.is_reviewer:\n        courses = list(semester.course_set.filter(state__in=[\"in_evaluation\", \"evaluated\", \"reviewed\", \"published\"]).prefetch_related(\"degrees\"))\n    else:\n        courses = list(semester.course_set.filter(state=\"published\").prefetch_related(\"degrees\"))\n\n    courses = [course for course in courses if course.can_user_see_course(request.user)]\n\n    # Annotate each course object with its grades.\n    for course in courses:\n        course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)\n\n    CourseTuple = namedtuple('CourseTuple', ('courses', 'single_results'))\n\n    courses_by_degree = OrderedDict()\n    for degree in Degree.objects.all():\n        courses_by_degree[degree] = CourseTuple([], [])\n    for course in courses:\n        if course.is_single_result:\n            for degree in course.degrees.all():\n                section = calculate_results(course)[0]\n                result = section.results[0]\n                courses_by_degree[degree].single_results.append((course, result))\n        else:\n            for degree in course.degrees.all():\n                courses_by_degree[degree].courses.append(course)\n\n    template_data = dict(semester=semester, courses_by_degree=courses_by_degree)\n    return render(request, \"results_semester_detail.html\", template_data)\n\n\n@login_required\ndef course_detail(request, semester_id, course_id):\n    semester = get_object_or_404(Semester, id=semester_id)\n    course = get_object_or_404(semester.course_set, id=course_id, semester=semester)\n\n    if not course.can_user_see_results(request.user):\n        raise PermissionDenied\n\n    sections = calculate_results(course)\n\n    if request.user.is_reviewer:\n        public_view = request.GET.get('public_view') != 'false'  # if parameter is not given, show public view.\n    else:\n        public_view = request.GET.get('public_view') == 'true'  # if parameter is not given, show own view.\n\n    # If grades are not published, there is no public view\n    if not course.can_publish_grades:\n        public_view = False\n\n    represented_users = list(request.user.represented_users.all())\n    represented_users.append(request.user)\n\n    show_grades = request.user.is_reviewer or course.can_publish_grades\n\n    # filter text answers\n    for section in sections:\n        results = []\n        for result in section.results:\n            if isinstance(result, TextResult):\n                answers = [answer for answer in result.answers if user_can_see_text_answer(request.user, represented_users, answer, public_view)]\n                if answers:\n                    results.append(TextResult(question=result.question, answers=answers))\n            else:\n                results.append(result)\n        section.results[:] = results\n\n    # filter empty headings\n    for section in sections:\n        filtered_results = []\n        for index in range(len(section.results)):\n            result = section.results[index]\n            # filter out if there are no more questions or the next question is also a heading question\n            if isinstance(result, HeadingResult):\n                if index == len(section.results) - 1 or isinstance(section.results[index + 1], HeadingResult):\n                    continue\n            filtered_results.append(result)\n        section.results[:] = filtered_results\n\n    # remove empty sections\n    sections = [section for section in sections if section.results]\n\n    # group by contributor\n    course_sections = []\n    contributor_sections = OrderedDict()\n    for section in sections:\n        if not section.results:\n            continue\n        if section.contributor is None:\n            course_sections.append(section)\n        else:\n            contributor_sections.setdefault(section.contributor,\n                                            {'total_votes': 0, 'sections': []})['sections'].append(section)\n\n            for result in section.results:\n                if isinstance(result, TextResult):\n                    contributor_sections[section.contributor]['total_votes'] += 1\n                elif isinstance(result, RatingResult) or isinstance(result, YesNoResult):\n                    # Only count rating results if we show the grades.\n                    if show_grades:\n                        contributor_sections[section.contributor]['total_votes'] += result.total_count\n\n    # Show a warning if course is still in evaluation (for reviewer preview).\n    evaluation_warning = course.state != 'published'\n\n    # Results for a course might not be visible because there are not enough answers\n    # but it can still be \"published\" e.g. to show the comment results to contributors.\n    # Users who can open the results page see a warning message in this case.\n    sufficient_votes_warning = not course.can_publish_grades\n\n    course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)\n\n    template_data = dict(\n            course=course,\n            course_sections=course_sections,\n            contributor_sections=contributor_sections,\n            evaluation_warning=evaluation_warning,\n            sufficient_votes_warning=sufficient_votes_warning,\n            show_grades=show_grades,\n            reviewer=request.user.is_reviewer,\n            contributor=course.is_user_contributor_or_delegate(request.user),\n            can_download_grades=request.user.can_download_grades,\n            public_view=public_view)\n    return render(request, \"results_course_detail.html\", template_data)\n\n\ndef user_can_see_text_answer(user, represented_users, text_answer, public_view=False):\n    if public_view:\n        return False\n    if text_answer.state not in COMMENT_STATES_REQUIRED_FOR_VISIBILITY:\n        return False\n    if user.is_reviewer:\n        return True\n\n    contributor = text_answer.contribution.contributor\n\n    if text_answer.is_private:\n        return contributor == user\n\n    if text_answer.is_published:\n        if text_answer.contribution.responsible:\n            return contributor == user or user in contributor.delegates.all()\n\n        if contributor in represented_users:\n            return True\n        if text_answer.contribution.course.contributions.filter(\n                contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():\n            return True\n        if text_answer.contribution.is_general and text_answer.contribution.course.contributions.filter(\n                contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():\n            return True\n\n    return False\n", "path": "evap/results/views.py"}]}