prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  collapse contributors with no answers in course detail pages
  Contributors who didn't get any answers should be collapsed on the results pages, so that the empty answer lines are not shown.
  This should also happen if there are answers in the database, but none of them can be seen by the current user.
  
  ![2015 04 02 17_37_57-000018](https://cloud.githubusercontent.com/assets/1891915/6967376/2b406e48-d95f-11e4-99da-37d2f04ec3e1.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 django.core.exceptions import PermissionDenied
  2 from django.shortcuts import get_object_or_404, render
  3 from django.contrib.auth.decorators import login_required
  4 
  5 from evap.evaluation.models import Semester, Degree, Contribution
  6 from evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, TextResult
  7 
  8 
  9 from collections import OrderedDict, namedtuple
  10 
  11 
  12 @login_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 @login_required
  20 def semester_detail(request, semester_id):
  21     semester = get_object_or_404(Semester, id=semester_id)
  22     courses = list(semester.course_set.filter(state="published").prefetch_related("degrees"))
  23 
  24     # annotate each course object with its grades
  25     for course in courses:
  26         course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)
  27 
  28     CourseTuple = namedtuple('CourseTuple', ('courses', 'single_results'))
  29 
  30     courses_by_degree = OrderedDict()
  31     for degree in Degree.objects.all():
  32         courses_by_degree[degree] = CourseTuple([], [])
  33     for course in courses:
  34         if course.is_single_result():
  35             for degree in course.degrees.all():
  36                 section = calculate_results(course)[0]
  37                 result = section.results[0]
  38                 courses_by_degree[degree].single_results.append((course, result))
  39         else:
  40             for degree in course.degrees.all():
  41                 courses_by_degree[degree].courses.append(course)
  42 
  43     template_data = dict(semester=semester, courses_by_degree=courses_by_degree, staff=request.user.is_staff)
  44     return render(request, "results_semester_detail.html", template_data)
  45 
  46 
  47 @login_required
  48 def course_detail(request, semester_id, course_id):
  49     semester = get_object_or_404(Semester, id=semester_id)
  50     course = get_object_or_404(semester.course_set, id=course_id)
  51 
  52     if not course.can_user_see_results(request.user):
  53         raise PermissionDenied
  54 
  55     sections = calculate_results(course)
  56 
  57     public_view = request.GET.get('public_view', 'false') # default: show own view
  58     public_view = {'true': True, 'false': False}.get(public_view.lower()) # convert parameter to boolean
  59 
  60     represented_users = list(request.user.represented_users.all())
  61     represented_users.append(request.user)
  62 
  63     for section in sections:
  64         results = []
  65         for result in section.results:
  66             if isinstance(result, TextResult):
  67                 answers = [answer for answer in result.answers if user_can_see_text_answer(request.user, represented_users, answer, public_view)]
  68                 if answers:
  69                     results.append(TextResult(question=result.question, answers=answers))
  70             else:
  71                 results.append(result)
  72         section.results[:] = results
  73 
  74     # filter empty sections and group by contributor
  75     course_sections = []
  76     contributor_sections = OrderedDict()
  77     for section in sections:
  78         if not section.results:
  79             continue
  80         if section.contributor is None:
  81             course_sections.append(section)
  82         else:
  83             contributor_sections.setdefault(section.contributor, []).append(section)
  84 
  85     # show a warning if course is still in evaluation (for staff preview)
  86     evaluation_warning = course.state != 'published'
  87 
  88     # results for a course might not be visible because there are not enough answers
  89     # but it can still be "published" e.g. to show the comment results to contributors.
  90     # users who can open the results page see a warning message in this case
  91     sufficient_votes_warning = not course.can_publish_grades
  92 
  93     show_grades = request.user.is_staff or course.can_publish_grades
  94 
  95     course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)
  96 
  97     template_data = dict(
  98             course=course,
  99             course_sections=course_sections,
  100             contributor_sections=contributor_sections,
  101             evaluation_warning=evaluation_warning,
  102             sufficient_votes_warning=sufficient_votes_warning,
  103             show_grades=show_grades,
  104             staff=request.user.is_staff,
  105             contributor=course.is_user_contributor_or_delegate(request.user),
  106             can_download_grades=request.user.can_download_grades,
  107             public_view=public_view)
  108     return render(request, "results_course_detail.html", template_data)
  109 
  110 def user_can_see_text_answer(user, represented_users, text_answer, public_view=False):
  111     if public_view:
  112         return False
  113     if user.is_staff:
  114         return True
  115     contributor = text_answer.contribution.contributor
  116     if text_answer.is_private:
  117         return contributor == user
  118     if text_answer.is_published:
  119         if contributor in represented_users:
  120             return True
  121         if text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():
  122             return True
  123         if text_answer.contribution.is_general and \
  124             text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():
  125             return True
  126 
  127     return False
  128
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/evap/results/views.py b/evap/results/views.py
  --- a/evap/results/views.py
  +++ b/evap/results/views.py
  @@ -3,8 +3,7 @@
   from django.contrib.auth.decorators import login_required
   
   from evap.evaluation.models import Semester, Degree, Contribution
  -from evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, TextResult
  -
  +from evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult
   
   from collections import OrderedDict, namedtuple
   
  @@ -21,7 +20,7 @@
       semester = get_object_or_404(Semester, id=semester_id)
       courses = list(semester.course_set.filter(state="published").prefetch_related("degrees"))
   
  -    # annotate each course object with its grades
  +    # Annotate each course object with its grades.
       for course in courses:
           course.avg_grade, course.avg_deviation = calculate_average_grades_and_deviation(course)
   
  @@ -54,8 +53,8 @@
   
       sections = calculate_results(course)
   
  -    public_view = request.GET.get('public_view', 'false') # default: show own view
  -    public_view = {'true': True, 'false': False}.get(public_view.lower()) # convert parameter to boolean
  +    public_view = request.GET.get('public_view', 'false')  # Default: show own view.
  +    public_view = {'true': True, 'false': False}.get(public_view.lower())  # Convert parameter to boolean.
   
       represented_users = list(request.user.represented_users.all())
       represented_users.append(request.user)
  @@ -71,7 +70,7 @@
                   results.append(result)
           section.results[:] = results
   
  -    # filter empty sections and group by contributor
  +    # Filter empty sections and group by contributor.
       course_sections = []
       contributor_sections = OrderedDict()
       for section in sections:
  @@ -80,14 +79,21 @@
           if section.contributor is None:
               course_sections.append(section)
           else:
  -            contributor_sections.setdefault(section.contributor, []).append(section)
  +            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])
   
  -    # show a warning if course is still in evaluation (for staff preview)
  +    # Show a warning if course is still in evaluation (for staff preview).
       evaluation_warning = course.state != 'published'
   
  -    # results for a course might not be visible because there are not enough answers
  +    # Results for a course might not be visible because there are not enough answers
       # but it can still be "published" e.g. to show the comment results to contributors.
  -    # users who can open the results page see a warning message in this case
  +    # 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_staff or course.can_publish_grades
  @@ -107,6 +113,7 @@
               public_view=public_view)
       return render(request, "results_course_detail.html", template_data)
   
  +
   def user_can_see_text_answer(user, represented_users, text_answer, public_view=False):
       if public_view:
           return False
  @@ -118,10 +125,11 @@
       if text_answer.is_published:
           if contributor in represented_users:
               return True
  -        if text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():
  +        if text_answer.contribution.course.contributions.filter(
  +                contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():
               return True
  -        if text_answer.contribution.is_general and \
  -            text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():
  +        if text_answer.contribution.is_general and text_answer.contribution.course.contributions.filter(
  +                contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():
               return True
   
       return False
  
verification_info: {"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@@ -3,8 +3,7 @@\n from django.contrib.auth.decorators import login_required\n \n from evap.evaluation.models import Semester, Degree, Contribution\n-from evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, TextResult\n-\n+from evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult\n \n from collections import OrderedDict, namedtuple\n \n@@ -21,7 +20,7 @@\n     semester = get_object_or_404(Semester, id=semester_id)\n     courses = list(semester.course_set.filter(state=\"published\").prefetch_related(\"degrees\"))\n \n-    # annotate each course object with its grades\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@@ -54,8 +53,8 @@\n \n     sections = calculate_results(course)\n \n-    public_view = request.GET.get('public_view', 'false') # default: show own view\n-    public_view = {'true': True, 'false': False}.get(public_view.lower()) # convert parameter to boolean\n+    public_view = request.GET.get('public_view', 'false')  # Default: show own view.\n+    public_view = {'true': True, 'false': False}.get(public_view.lower())  # Convert parameter to boolean.\n \n     represented_users = list(request.user.represented_users.all())\n     represented_users.append(request.user)\n@@ -71,7 +70,7 @@\n                 results.append(result)\n         section.results[:] = results\n \n-    # filter empty sections and group by contributor\n+    # Filter empty sections and group by contributor.\n     course_sections = []\n     contributor_sections = OrderedDict()\n     for section in sections:\n@@ -80,14 +79,21 @@\n         if section.contributor is None:\n             course_sections.append(section)\n         else:\n-            contributor_sections.setdefault(section.contributor, []).append(section)\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 staff preview)\n+    # Show a warning if course is still in evaluation (for staff preview).\n     evaluation_warning = course.state != 'published'\n \n-    # results for a course might not be visible because there are not enough answers\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+    # 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_staff or course.can_publish_grades\n@@ -107,6 +113,7 @@\n             public_view=public_view)\n     return render(request, \"results_course_detail.html\", template_data)\n \n+\n def user_can_see_text_answer(user, represented_users, text_answer, public_view=False):\n     if public_view:\n         return False\n@@ -118,10 +125,11 @@\n     if text_answer.is_published:\n         if contributor in represented_users:\n             return True\n-        if text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():\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 \\\n-            text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.COURSE_COMMENTS).exists():\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", "issue": "collapse contributors with no answers in course detail pages\nContributors who didn't get any answers should be collapsed on the results pages, so that the empty answer lines are not shown.\nThis should also happen if there are answers in the database, but none of them can be seen by the current user.\n\n![2015 04 02 17_37_57-000018](https://cloud.githubusercontent.com/assets/1891915/6967376/2b406e48-d95f-11e4-99da-37d2f04ec3e1.png)\n\n", "before_files": [{"content": "from 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.tools import calculate_results, calculate_average_grades_and_deviation, TextResult\n\n\nfrom collections import OrderedDict, namedtuple\n\n\n@login_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@login_required\ndef semester_detail(request, semester_id):\n    semester = get_object_or_404(Semester, id=semester_id)\n    courses = list(semester.course_set.filter(state=\"published\").prefetch_related(\"degrees\"))\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, staff=request.user.is_staff)\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)\n\n    if not course.can_user_see_results(request.user):\n        raise PermissionDenied\n\n    sections = calculate_results(course)\n\n    public_view = request.GET.get('public_view', 'false') # default: show own view\n    public_view = {'true': True, 'false': False}.get(public_view.lower()) # convert parameter to boolean\n\n    represented_users = list(request.user.represented_users.all())\n    represented_users.append(request.user)\n\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 sections and 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, []).append(section)\n\n    # show a warning if course is still in evaluation (for staff 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_staff 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            staff=request.user.is_staff,\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\ndef user_can_see_text_answer(user, represented_users, text_answer, public_view=False):\n    if public_view:\n        return False\n    if user.is_staff:\n        return True\n    contributor = text_answer.contribution.contributor\n    if text_answer.is_private:\n        return contributor == user\n    if text_answer.is_published:\n        if contributor in represented_users:\n            return True\n        if text_answer.contribution.course.contributions.filter(contributor__in=represented_users, comment_visibility=Contribution.ALL_COMMENTS).exists():\n            return True\n        if text_answer.contribution.is_general and \\\n            text_answer.contribution.course.contributions.filter(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 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.tools import calculate_results, calculate_average_grades_and_deviation, TextResult, RatingResult\n\nfrom collections import OrderedDict, namedtuple\n\n\n@login_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@login_required\ndef semester_detail(request, semester_id):\n    semester = get_object_or_404(Semester, id=semester_id)\n    courses = list(semester.course_set.filter(state=\"published\").prefetch_related(\"degrees\"))\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, staff=request.user.is_staff)\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)\n\n    if not course.can_user_see_results(request.user):\n        raise PermissionDenied\n\n    sections = calculate_results(course)\n\n    public_view = request.GET.get('public_view', 'false')  # Default: show own view.\n    public_view = {'true': True, 'false': False}.get(public_view.lower())  # Convert parameter to boolean.\n\n    represented_users = list(request.user.represented_users.all())\n    represented_users.append(request.user)\n\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 sections and 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 staff 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_staff 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            staff=request.user.is_staff,\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 user.is_staff:\n        return True\n    contributor = text_answer.contribution.contributor\n    if text_answer.is_private:\n        return contributor == user\n    if text_answer.is_published:\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"}]}