Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix reports #64

Merged
merged 3 commits into from
Sep 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 44 additions & 21 deletions quizApp/views/experiments.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Views that handle CRUD for experiments and rendering questions for
participants.
"""
from collections import defaultdict
from collections import defaultdict, Counter
from datetime import datetime
import json
import os
Expand Down Expand Up @@ -420,7 +420,7 @@ def results_experiment(experiment_id):
num_participants = Participant.query.count()
num_finished = AssignmentSet.query.\
filter_by(experiment_id=experiment.id).\
filter_by(progress=-1).count()
filter_by(complete=True).count()

percent_finished = num_finished / float(num_participants)

Expand Down Expand Up @@ -471,6 +471,28 @@ def export_results_experiment(experiment_id):
attachment_filename="experiment_{}_report.xlsx".format(experiment.id))


def get_activity_column_index(activity, activity_column_mapping,
activity_counter, headers):
"""Find the column index for this occurrence of the given activity. This
will update headers, counter, and mapping if necessary.
"""
activity_occurrence = activity_counter[activity.id]
activity_counter[activity.id] += 1
try:
return activity_column_mapping[activity.id][activity_occurrence]
except KeyError:
activity_column_mapping[activity.id] = [len(headers) + 1]
headers.append("{}: {}".format(activity.id, activity))
headers.append("Correct?")
headers.append("Points")
except IndexError:
activity_column_mapping[activity.id].append(len(headers) + 1)
headers.append("{}: {}".format(activity.id, activity))
headers.append("Correct?")
headers.append("Points")
return activity_column_mapping[activity.id][activity_occurrence]


def get_results_workbook(experiment):
"""Analyze the assignment sets in the experiment and return an excel
workbook.
Expand All @@ -481,11 +503,19 @@ def get_results_workbook(experiment):
next_participant_row = 2
participant_row_mapping = {}

# The same activity can appear multiple times in an assignment set. To
# display them properly, we keep a list of their ocurrences in
# activity_column_mapping, like so:
# {1: [3, 7, 10], ...} means activity 1 occurs in columns 3, 7, and 10
# When populating a row, we will use the earliest occurrence of the
# activity possible.

workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = "Experiment {} - Report".format(experiment.id)

for assignment_set in assignment_sets:
activity_counter = Counter()
participant = assignment_set.participant

if not participant:
Expand All @@ -501,35 +531,28 @@ def get_results_workbook(experiment):

for assignment in assignment_set.assignments:
activity = assignment.activity

if activity.id not in activity_column_mapping:
activity_column_mapping[activity.id] = len(headers) + 1
headers.append("{}/{}: {}".format(assignment.id,
activity.id, activity))
headers.append("Correct?")
headers.append("Points")
activity_column_index = get_activity_column_index(
activity,
activity_column_mapping,
activity_counter,
headers)

if not assignment.result:
continue
row = ["_BLANK_"] * 3
else:
row = ["{}:{}".format(assignment.id, assignment.result),
assignment.correct,
assignment.score]

populate_row_segment(
sheet,
participant_row_mapping[participant.id],
activity_column_mapping[activity.id],
[str(assignment.result),
assignment.correct,
assignment.score]
activity_column_index,
row
)

populate_row_segment(sheet, 1, 1, headers)

# Put a special token in all blank spaces
for r in range(1, next_participant_row):
for c in range(1, len(headers) + 1):
value = sheet.cell(row=r, column=c).value
if value is None:
sheet.cell(row=r, column=c).value = "_BLANK_"

# Specify experiment ID
sheet.cell(row=1, column=len(headers) + 1).value = "Experiment ID"
sheet.cell(row=2, column=len(headers) + 1).value = experiment.id
Expand Down
28 changes: 26 additions & 2 deletions tests/test_views_experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""
from __future__ import unicode_literals
from builtins import str
from collections import Counter
import json
import random
import mock
Expand All @@ -11,9 +12,10 @@
import openpyxl

from quizApp import db
from quizApp.models import AssignmentSet
from quizApp.models import AssignmentSet, Activity
from quizApp.views.experiments import get_next_assignment_url, \
POST_FINALIZE_HANDLERS, validate_assignment_set, populate_row_segment
POST_FINALIZE_HANDLERS, validate_assignment_set, populate_row_segment, \
get_activity_column_index
from tests.factories import ExperimentFactory, create_experiment, \
ParticipantFactory, create_result
from tests.auth import login_participant, get_participant, \
Expand Down Expand Up @@ -726,3 +728,25 @@ def test_export_experiment_results(client, users):
url = "/experiments/{}/results/export".format(exp.id)
response = client.get(url)
assert response.status_code == 200


def test_get_activity_column_index():
activity = mock.MagicMock(autospec=Activity)
activity.id = 5
activity.__str__.return_value = ""
counter = Counter()
mapping = {}
headers = []

get_activity_column_index(activity, mapping, counter, headers)

assert mapping[activity.id][0] == 1 # 1-indexed due to openpyxl
assert len(mapping[activity.id]) == 1
assert counter[activity.id] == 1

get_activity_column_index(activity, mapping, counter, headers)

assert mapping[activity.id][0] == 1
assert mapping[activity.id][1] == 4
assert len(mapping[activity.id]) == 2
assert counter[activity.id] == 2