We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Exporter only uses first contributor per type
The exporter only uses the results of the first contributor per type.
It should instead compute the average of all contributors per type.


--- 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/exporters.py`
Content:
```
1 from evap.evaluation.models import Questionnaire
2 from evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, get_grade_color, get_deviation_color
3 
4 from django.utils.translation import ugettext as _
5 
6 from collections import OrderedDict
7 from collections import defaultdict
8 import datetime
9 import xlwt
10 
11 
12 class ExcelExporter(object):
13 
14     def __init__(self, semester):
15         self.semester = semester
16         self.styles = dict()
17 
18         self.CUSTOM_COLOR_START = 8
19         self.NUM_GRADE_COLORS = 21 # 1.0 to 5.0 in 0.2 steps
20         self.NUM_DEVIATION_COLORS = 13 # 0.0 to 2.4 in 0.2 steps
21         self.STEP = 0.2 # we only have a limited number of custom colors
22 
23     def normalize_number(self, number):
24         """ floors 'number' to a multiply of self.STEP """
25         rounded_number = round(number, 1) # see #302
26         return round(int(rounded_number / self.STEP + 0.0001) * self.STEP, 1)
27 
28     def create_style(self, workbook, base_style, style_name, palette_index, color):
29         color_name = style_name + "_color"
30         xlwt.add_palette_colour(color_name, palette_index)
31         workbook.set_colour_RGB(palette_index, *color)
32         self.styles[style_name] = xlwt.easyxf(base_style.format(color_name), num_format_str="0.0")
33 
34     def init_styles(self, workbook):
35         self.styles = {
36             'default':       xlwt.Style.default_style,
37             'avg':           xlwt.easyxf('alignment: horiz centre; font: bold on; borders: left medium, top medium, bottom medium'),
38             'headline':      xlwt.easyxf('font: bold on, height 400; alignment: horiz centre, vert centre, wrap on', num_format_str="0.0"),
39             'course':        xlwt.easyxf('alignment: horiz centre, wrap on, rota 90; borders: left medium, top medium'),
40             'course_unfinished': xlwt.easyxf('alignment: horiz centre, wrap on, rota 90; borders: left medium, top medium; font: italic on'),
41             'total_voters': xlwt.easyxf('alignment: horiz centre; borders: left medium, bottom medium, right medium'),
42             'bold':          xlwt.easyxf('font: bold on'),
43             'border_left':   xlwt.easyxf('borders: left medium'),
44             'border_right':  xlwt.easyxf('borders: right medium'),
45             'border_top_bottom_right': xlwt.easyxf('borders: top medium, bottom medium, right medium')}
46 
47 
48 
49         grade_base_style = 'pattern: pattern solid, fore_colour {}; alignment: horiz centre; font: bold on; borders: left medium'
50         for i in range(0, self.NUM_GRADE_COLORS):
51             grade = 1 + i*self.STEP
52             color = get_grade_color(grade)
53             palette_index = self.CUSTOM_COLOR_START + i
54             style_name = self.grade_to_style(grade)
55             self.create_style(workbook, grade_base_style, style_name, palette_index, color)
56 
57         deviation_base_style = 'pattern: pattern solid, fore_colour {}; alignment: horiz centre; borders: right medium'
58         for i in range(0, self.NUM_DEVIATION_COLORS):
59             deviation = i * self.STEP
60             color = get_deviation_color(deviation)
61             palette_index = self.CUSTOM_COLOR_START + self.NUM_GRADE_COLORS + i
62             style_name = self.deviation_to_style(deviation)
63             self.create_style(workbook, deviation_base_style, style_name, palette_index, color)
64 
65 
66     def grade_to_style(self, grade):
67         return 'grade_' + str(self.normalize_number(grade))
68 
69     def deviation_to_style(self, deviation):
70         return 'deviation_' + str(self.normalize_number(deviation))
71 
72     def export(self, response, ignore_not_enough_answers=False):
73         courses_with_results = list()
74         for course in self.semester.course_set.filter(state="published").all():
75             if course.is_single_result():
76                 continue
77             results = OrderedDict()
78             for questionnaire, contributor, label, data, section_warning in calculate_results(course):
79                 results.setdefault(questionnaire.id, []).extend(data)
80             courses_with_results.append((course, results))
81 
82         courses_with_results.sort(key=lambda cr: cr[0].type)
83 
84         qn_frequencies = defaultdict(int)
85         for course, results in courses_with_results:
86             for questionnaire, results in results.items():
87                 qn_frequencies[questionnaire] += 1
88 
89         qn_relevant = list(qn_frequencies.items())
90         qn_relevant.sort(key=lambda t: -t[1])
91 
92         questionnaires = [Questionnaire.objects.get(id=t[0]) for t in qn_relevant]
93 
94         self.workbook = xlwt.Workbook()
95         self.sheet = self.workbook.add_sheet(_("Results"))
96         self.row = 0
97         self.col = 0
98 
99 
100         self.init_styles(self.workbook)
101 
102         writec(self, _("Evaluation {0} - created on {1}").format(self.semester.name, datetime.date.today()), "headline")
103         for course, results in courses_with_results:
104             if course.state == "published":
105                 writec(self, course.name, "course", cols=2)
106             else:
107                 writec(self, course.name, "course_unfinished", cols=2)
108 
109         writen(self)
110         for course, results in courses_with_results:
111             writec(self, "Average", "avg")
112             writec(self, "Deviation", "border_top_bottom_right")
113 
114         for questionnaire in questionnaires:
115             writen(self, questionnaire.name, "bold")
116             for course, results in courses_with_results:
117                 self.write_two_empty_cells_with_borders()
118 
119             for question in questionnaire.question_set.all():
120                 if question.is_text_question:
121                     continue
122 
123                 writen(self, question.text)
124 
125                 for course, results in courses_with_results:
126                     qn_results = results.get(questionnaire.id, None)
127                     if qn_results:
128                         values = []
129                         deviations = []
130                         for grade_result in qn_results:
131                             if grade_result.question.id == question.id:
132                                 if grade_result.average:
133                                     values.append(grade_result.average)
134                                     deviations.append(grade_result.deviation)
135                                 break
136                         enough_answers = course.can_publish_grades
137                         if values and (enough_answers or ignore_not_enough_answers):
138                             avg = sum(values) / len(values)
139                             writec(self, avg, self.grade_to_style(avg))
140 
141                             dev = sum(deviations) / len(deviations)
142                             writec(self, dev, self.deviation_to_style(dev))
143                         else:
144                             self.write_two_empty_cells_with_borders()
145                     else:
146                         self.write_two_empty_cells_with_borders()
147             writen(self, None)
148             for course, results in courses_with_results:
149                 self.write_two_empty_cells_with_borders()
150 
151         writen(self, _("Overall Average Grade"), "bold")
152         for course, results in courses_with_results:
153             avg, dev = calculate_average_grades_and_deviation(course)
154             if avg:
155                 writec(self, avg, self.grade_to_style(avg), cols=2)
156             else:
157                 self.write_two_empty_cells_with_borders()
158 
159         writen(self, _("Overall Average Standard Deviation"), "bold")
160         for course, results in courses_with_results:
161             avg, dev = calculate_average_grades_and_deviation(course)
162             if dev is not None:
163                 writec(self, dev, self.deviation_to_style(dev), cols=2)
164             else:
165                 self.write_two_empty_cells_with_borders()
166 
167         writen(self, _("Total Voters/Total Participants"), "bold")
168         for course, results in courses_with_results:
169             percent_participants = float(course.num_voters)/float(course.num_participants) if course.num_participants > 0 else 0
170             writec(self, "{}/{} ({:.0%})".format(course.num_voters, course.num_participants, percent_participants), "total_voters", cols=2)
171 
172         self.workbook.save(response)
173 
174 
175     def write_two_empty_cells_with_borders(self):
176         writec(self, None, "border_left")
177         writec(self, None, "border_right")
178 
179 
180 def writen(exporter, label="", style_name="default"):
181     """Write the cell at the beginning of the next row."""
182     exporter.col = 0
183     exporter.row += 1
184     writec(exporter, label, style_name)
185 
186 def writec(exporter, label, style_name, rows=1, cols=1):
187     """Write the cell in the next column of the current line."""
188     _write(exporter, label, exporter.styles[style_name], rows, cols)
189     exporter.col += 1
190 
191 def _write(exporter, label, style, rows, cols):
192     if rows > 1 or cols > 1:
193         exporter.sheet.write_merge(exporter.row, exporter.row+rows-1, exporter.col, exporter.col+cols-1, label, style)
194         exporter.col += cols - 1
195     else:
196         exporter.sheet.write(exporter.row, exporter.col, label, style)
197
```
--- 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/exporters.py b/evap/results/exporters.py
--- a/evap/results/exporters.py
+++ b/evap/results/exporters.py
@@ -132,7 +132,6 @@
                                 if grade_result.average:
                                     values.append(grade_result.average)
                                     deviations.append(grade_result.deviation)
-                                break
                         enough_answers = course.can_publish_grades
                         if values and (enough_answers or ignore_not_enough_answers):
                             avg = sum(values) / len(values)

{"golden_diff": "diff --git a/evap/results/exporters.py b/evap/results/exporters.py\n--- a/evap/results/exporters.py\n+++ b/evap/results/exporters.py\n@@ -132,7 +132,6 @@\n                                 if grade_result.average:\n                                     values.append(grade_result.average)\n                                     deviations.append(grade_result.deviation)\n-                                break\n                         enough_answers = course.can_publish_grades\n                         if values and (enough_answers or ignore_not_enough_answers):\n                             avg = sum(values) / len(values)\n", "issue": "Exporter only uses first contributor per type\nThe exporter only uses the results of the first contributor per type.\nIt should instead compute the average of all contributors per type.\n\n", "before_files": [{"content": "from evap.evaluation.models import Questionnaire\nfrom evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, get_grade_color, get_deviation_color\n\nfrom django.utils.translation import ugettext as _\n\nfrom collections import OrderedDict\nfrom collections import defaultdict\nimport datetime\nimport xlwt\n\n\nclass ExcelExporter(object):\n\n    def __init__(self, semester):\n        self.semester = semester\n        self.styles = dict()\n\n        self.CUSTOM_COLOR_START = 8\n        self.NUM_GRADE_COLORS = 21 # 1.0 to 5.0 in 0.2 steps\n        self.NUM_DEVIATION_COLORS = 13 # 0.0 to 2.4 in 0.2 steps\n        self.STEP = 0.2 # we only have a limited number of custom colors\n\n    def normalize_number(self, number):\n        \"\"\" floors 'number' to a multiply of self.STEP \"\"\"\n        rounded_number = round(number, 1) # see #302\n        return round(int(rounded_number / self.STEP + 0.0001) * self.STEP, 1)\n\n    def create_style(self, workbook, base_style, style_name, palette_index, color):\n        color_name = style_name + \"_color\"\n        xlwt.add_palette_colour(color_name, palette_index)\n        workbook.set_colour_RGB(palette_index, *color)\n        self.styles[style_name] = xlwt.easyxf(base_style.format(color_name), num_format_str=\"0.0\")\n\n    def init_styles(self, workbook):\n        self.styles = {\n            'default':       xlwt.Style.default_style,\n            'avg':           xlwt.easyxf('alignment: horiz centre; font: bold on; borders: left medium, top medium, bottom medium'),\n            'headline':      xlwt.easyxf('font: bold on, height 400; alignment: horiz centre, vert centre, wrap on', num_format_str=\"0.0\"),\n            'course':        xlwt.easyxf('alignment: horiz centre, wrap on, rota 90; borders: left medium, top medium'),\n            'course_unfinished': xlwt.easyxf('alignment: horiz centre, wrap on, rota 90; borders: left medium, top medium; font: italic on'),\n            'total_voters': xlwt.easyxf('alignment: horiz centre; borders: left medium, bottom medium, right medium'),\n            'bold':          xlwt.easyxf('font: bold on'),\n            'border_left':   xlwt.easyxf('borders: left medium'),\n            'border_right':  xlwt.easyxf('borders: right medium'),\n            'border_top_bottom_right': xlwt.easyxf('borders: top medium, bottom medium, right medium')}\n\n\n\n        grade_base_style = 'pattern: pattern solid, fore_colour {}; alignment: horiz centre; font: bold on; borders: left medium'\n        for i in range(0, self.NUM_GRADE_COLORS):\n            grade = 1 + i*self.STEP\n            color = get_grade_color(grade)\n            palette_index = self.CUSTOM_COLOR_START + i\n            style_name = self.grade_to_style(grade)\n            self.create_style(workbook, grade_base_style, style_name, palette_index, color)\n\n        deviation_base_style = 'pattern: pattern solid, fore_colour {}; alignment: horiz centre; borders: right medium'\n        for i in range(0, self.NUM_DEVIATION_COLORS):\n            deviation = i * self.STEP\n            color = get_deviation_color(deviation)\n            palette_index = self.CUSTOM_COLOR_START + self.NUM_GRADE_COLORS + i\n            style_name = self.deviation_to_style(deviation)\n            self.create_style(workbook, deviation_base_style, style_name, palette_index, color)\n\n\n    def grade_to_style(self, grade):\n        return 'grade_' + str(self.normalize_number(grade))\n\n    def deviation_to_style(self, deviation):\n        return 'deviation_' + str(self.normalize_number(deviation))\n\n    def export(self, response, ignore_not_enough_answers=False):\n        courses_with_results = list()\n        for course in self.semester.course_set.filter(state=\"published\").all():\n            if course.is_single_result():\n                continue\n            results = OrderedDict()\n            for questionnaire, contributor, label, data, section_warning in calculate_results(course):\n                results.setdefault(questionnaire.id, []).extend(data)\n            courses_with_results.append((course, results))\n\n        courses_with_results.sort(key=lambda cr: cr[0].type)\n\n        qn_frequencies = defaultdict(int)\n        for course, results in courses_with_results:\n            for questionnaire, results in results.items():\n                qn_frequencies[questionnaire] += 1\n\n        qn_relevant = list(qn_frequencies.items())\n        qn_relevant.sort(key=lambda t: -t[1])\n\n        questionnaires = [Questionnaire.objects.get(id=t[0]) for t in qn_relevant]\n\n        self.workbook = xlwt.Workbook()\n        self.sheet = self.workbook.add_sheet(_(\"Results\"))\n        self.row = 0\n        self.col = 0\n\n\n        self.init_styles(self.workbook)\n\n        writec(self, _(\"Evaluation {0} - created on {1}\").format(self.semester.name, datetime.date.today()), \"headline\")\n        for course, results in courses_with_results:\n            if course.state == \"published\":\n                writec(self, course.name, \"course\", cols=2)\n            else:\n                writec(self, course.name, \"course_unfinished\", cols=2)\n\n        writen(self)\n        for course, results in courses_with_results:\n            writec(self, \"Average\", \"avg\")\n            writec(self, \"Deviation\", \"border_top_bottom_right\")\n\n        for questionnaire in questionnaires:\n            writen(self, questionnaire.name, \"bold\")\n            for course, results in courses_with_results:\n                self.write_two_empty_cells_with_borders()\n\n            for question in questionnaire.question_set.all():\n                if question.is_text_question:\n                    continue\n\n                writen(self, question.text)\n\n                for course, results in courses_with_results:\n                    qn_results = results.get(questionnaire.id, None)\n                    if qn_results:\n                        values = []\n                        deviations = []\n                        for grade_result in qn_results:\n                            if grade_result.question.id == question.id:\n                                if grade_result.average:\n                                    values.append(grade_result.average)\n                                    deviations.append(grade_result.deviation)\n                                break\n                        enough_answers = course.can_publish_grades\n                        if values and (enough_answers or ignore_not_enough_answers):\n                            avg = sum(values) / len(values)\n                            writec(self, avg, self.grade_to_style(avg))\n\n                            dev = sum(deviations) / len(deviations)\n                            writec(self, dev, self.deviation_to_style(dev))\n                        else:\n                            self.write_two_empty_cells_with_borders()\n                    else:\n                        self.write_two_empty_cells_with_borders()\n            writen(self, None)\n            for course, results in courses_with_results:\n                self.write_two_empty_cells_with_borders()\n\n        writen(self, _(\"Overall Average Grade\"), \"bold\")\n        for course, results in courses_with_results:\n            avg, dev = calculate_average_grades_and_deviation(course)\n            if avg:\n                writec(self, avg, self.grade_to_style(avg), cols=2)\n            else:\n                self.write_two_empty_cells_with_borders()\n\n        writen(self, _(\"Overall Average Standard Deviation\"), \"bold\")\n        for course, results in courses_with_results:\n            avg, dev = calculate_average_grades_and_deviation(course)\n            if dev is not None:\n                writec(self, dev, self.deviation_to_style(dev), cols=2)\n            else:\n                self.write_two_empty_cells_with_borders()\n\n        writen(self, _(\"Total Voters/Total Participants\"), \"bold\")\n        for course, results in courses_with_results:\n            percent_participants = float(course.num_voters)/float(course.num_participants) if course.num_participants > 0 else 0\n            writec(self, \"{}/{} ({:.0%})\".format(course.num_voters, course.num_participants, percent_participants), \"total_voters\", cols=2)\n\n        self.workbook.save(response)\n\n\n    def write_two_empty_cells_with_borders(self):\n        writec(self, None, \"border_left\")\n        writec(self, None, \"border_right\")\n\n\ndef writen(exporter, label=\"\", style_name=\"default\"):\n    \"\"\"Write the cell at the beginning of the next row.\"\"\"\n    exporter.col = 0\n    exporter.row += 1\n    writec(exporter, label, style_name)\n\ndef writec(exporter, label, style_name, rows=1, cols=1):\n    \"\"\"Write the cell in the next column of the current line.\"\"\"\n    _write(exporter, label, exporter.styles[style_name], rows, cols)\n    exporter.col += 1\n\ndef _write(exporter, label, style, rows, cols):\n    if rows > 1 or cols > 1:\n        exporter.sheet.write_merge(exporter.row, exporter.row+rows-1, exporter.col, exporter.col+cols-1, label, style)\n        exporter.col += cols - 1\n    else:\n        exporter.sheet.write(exporter.row, exporter.col, label, style)\n", "path": "evap/results/exporters.py"}], "after_files": [{"content": "from evap.evaluation.models import Questionnaire\nfrom evap.evaluation.tools import calculate_results, calculate_average_grades_and_deviation, get_grade_color, get_deviation_color\n\nfrom django.utils.translation import ugettext as _\n\nfrom collections import OrderedDict\nfrom collections import defaultdict\nimport datetime\nimport xlwt\n\n\nclass ExcelExporter(object):\n\n    def __init__(self, semester):\n        self.semester = semester\n        self.styles = dict()\n\n        self.CUSTOM_COLOR_START = 8\n        self.NUM_GRADE_COLORS = 21 # 1.0 to 5.0 in 0.2 steps\n        self.NUM_DEVIATION_COLORS = 13 # 0.0 to 2.4 in 0.2 steps\n        self.STEP = 0.2 # we only have a limited number of custom colors\n\n    def normalize_number(self, number):\n        \"\"\" floors 'number' to a multiply of self.STEP \"\"\"\n        rounded_number = round(number, 1) # see #302\n        return round(int(rounded_number / self.STEP + 0.0001) * self.STEP, 1)\n\n    def create_style(self, workbook, base_style, style_name, palette_index, color):\n        color_name = style_name + \"_color\"\n        xlwt.add_palette_colour(color_name, palette_index)\n        workbook.set_colour_RGB(palette_index, *color)\n        self.styles[style_name] = xlwt.easyxf(base_style.format(color_name), num_format_str=\"0.0\")\n\n    def init_styles(self, workbook):\n        self.styles = {\n            'default':       xlwt.Style.default_style,\n            'avg':           xlwt.easyxf('alignment: horiz centre; font: bold on; borders: left medium, top medium, bottom medium'),\n            'headline':      xlwt.easyxf('font: bold on, height 400; alignment: horiz centre, vert centre, wrap on', num_format_str=\"0.0\"),\n            'course':        xlwt.easyxf('alignment: horiz centre, wrap on, rota 90; borders: left medium, top medium'),\n            'course_unfinished': xlwt.easyxf('alignment: horiz centre, wrap on, rota 90; borders: left medium, top medium; font: italic on'),\n            'total_voters': xlwt.easyxf('alignment: horiz centre; borders: left medium, bottom medium, right medium'),\n            'bold':          xlwt.easyxf('font: bold on'),\n            'border_left':   xlwt.easyxf('borders: left medium'),\n            'border_right':  xlwt.easyxf('borders: right medium'),\n            'border_top_bottom_right': xlwt.easyxf('borders: top medium, bottom medium, right medium')}\n\n\n\n        grade_base_style = 'pattern: pattern solid, fore_colour {}; alignment: horiz centre; font: bold on; borders: left medium'\n        for i in range(0, self.NUM_GRADE_COLORS):\n            grade = 1 + i*self.STEP\n            color = get_grade_color(grade)\n            palette_index = self.CUSTOM_COLOR_START + i\n            style_name = self.grade_to_style(grade)\n            self.create_style(workbook, grade_base_style, style_name, palette_index, color)\n\n        deviation_base_style = 'pattern: pattern solid, fore_colour {}; alignment: horiz centre; borders: right medium'\n        for i in range(0, self.NUM_DEVIATION_COLORS):\n            deviation = i * self.STEP\n            color = get_deviation_color(deviation)\n            palette_index = self.CUSTOM_COLOR_START + self.NUM_GRADE_COLORS + i\n            style_name = self.deviation_to_style(deviation)\n            self.create_style(workbook, deviation_base_style, style_name, palette_index, color)\n\n\n    def grade_to_style(self, grade):\n        return 'grade_' + str(self.normalize_number(grade))\n\n    def deviation_to_style(self, deviation):\n        return 'deviation_' + str(self.normalize_number(deviation))\n\n    def export(self, response, ignore_not_enough_answers=False):\n        courses_with_results = list()\n        for course in self.semester.course_set.filter(state=\"published\").all():\n            if course.is_single_result():\n                continue\n            results = OrderedDict()\n            for questionnaire, contributor, label, data, section_warning in calculate_results(course):\n                results.setdefault(questionnaire.id, []).extend(data)\n            courses_with_results.append((course, results))\n\n        courses_with_results.sort(key=lambda cr: cr[0].type)\n\n        qn_frequencies = defaultdict(int)\n        for course, results in courses_with_results:\n            for questionnaire, results in results.items():\n                qn_frequencies[questionnaire] += 1\n\n        qn_relevant = list(qn_frequencies.items())\n        qn_relevant.sort(key=lambda t: -t[1])\n\n        questionnaires = [Questionnaire.objects.get(id=t[0]) for t in qn_relevant]\n\n        self.workbook = xlwt.Workbook()\n        self.sheet = self.workbook.add_sheet(_(\"Results\"))\n        self.row = 0\n        self.col = 0\n\n\n        self.init_styles(self.workbook)\n\n        writec(self, _(\"Evaluation {0} - created on {1}\").format(self.semester.name, datetime.date.today()), \"headline\")\n        for course, results in courses_with_results:\n            if course.state == \"published\":\n                writec(self, course.name, \"course\", cols=2)\n            else:\n                writec(self, course.name, \"course_unfinished\", cols=2)\n\n        writen(self)\n        for course, results in courses_with_results:\n            writec(self, \"Average\", \"avg\")\n            writec(self, \"Deviation\", \"border_top_bottom_right\")\n\n        for questionnaire in questionnaires:\n            writen(self, questionnaire.name, \"bold\")\n            for course, results in courses_with_results:\n                self.write_two_empty_cells_with_borders()\n\n            for question in questionnaire.question_set.all():\n                if question.is_text_question:\n                    continue\n\n                writen(self, question.text)\n\n                for course, results in courses_with_results:\n                    qn_results = results.get(questionnaire.id, None)\n                    if qn_results:\n                        values = []\n                        deviations = []\n                        for grade_result in qn_results:\n                            if grade_result.question.id == question.id:\n                                if grade_result.average:\n                                    values.append(grade_result.average)\n                                    deviations.append(grade_result.deviation)\n                        enough_answers = course.can_publish_grades\n                        if values and (enough_answers or ignore_not_enough_answers):\n                            avg = sum(values) / len(values)\n                            writec(self, avg, self.grade_to_style(avg))\n\n                            dev = sum(deviations) / len(deviations)\n                            writec(self, dev, self.deviation_to_style(dev))\n                        else:\n                            self.write_two_empty_cells_with_borders()\n                    else:\n                        self.write_two_empty_cells_with_borders()\n            writen(self, None)\n            for course, results in courses_with_results:\n                self.write_two_empty_cells_with_borders()\n\n        writen(self, _(\"Overall Average Grade\"), \"bold\")\n        for course, results in courses_with_results:\n            avg, dev = calculate_average_grades_and_deviation(course)\n            if avg:\n                writec(self, avg, self.grade_to_style(avg), cols=2)\n            else:\n                self.write_two_empty_cells_with_borders()\n\n        writen(self, _(\"Overall Average Standard Deviation\"), \"bold\")\n        for course, results in courses_with_results:\n            avg, dev = calculate_average_grades_and_deviation(course)\n            if dev is not None:\n                writec(self, dev, self.deviation_to_style(dev), cols=2)\n            else:\n                self.write_two_empty_cells_with_borders()\n\n        writen(self, _(\"Total Voters/Total Participants\"), \"bold\")\n        for course, results in courses_with_results:\n            percent_participants = float(course.num_voters)/float(course.num_participants) if course.num_participants > 0 else 0\n            writec(self, \"{}/{} ({:.0%})\".format(course.num_voters, course.num_participants, percent_participants), \"total_voters\", cols=2)\n\n        self.workbook.save(response)\n\n\n    def write_two_empty_cells_with_borders(self):\n        writec(self, None, \"border_left\")\n        writec(self, None, \"border_right\")\n\n\ndef writen(exporter, label=\"\", style_name=\"default\"):\n    \"\"\"Write the cell at the beginning of the next row.\"\"\"\n    exporter.col = 0\n    exporter.row += 1\n    writec(exporter, label, style_name)\n\ndef writec(exporter, label, style_name, rows=1, cols=1):\n    \"\"\"Write the cell in the next column of the current line.\"\"\"\n    _write(exporter, label, exporter.styles[style_name], rows, cols)\n    exporter.col += 1\n\ndef _write(exporter, label, style, rows, cols):\n    if rows > 1 or cols > 1:\n        exporter.sheet.write_merge(exporter.row, exporter.row+rows-1, exporter.col, exporter.col+cols-1, label, style)\n        exporter.col += cols - 1\n    else:\n        exporter.sheet.write(exporter.row, exporter.col, label, style)\n", "path": "evap/results/exporters.py"}]}