summaryrefslogtreecommitdiff
path: root/postgresqleu/confreg/feedback.py
blob: efe173463e61c51e76c7bea55d20e21203ef2063 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
from django.shortcuts import render
from django.db.models import Count
from django.db import connection

from .models import ConferenceFeedbackAnswer
from postgresqleu.confreg.util import get_authenticated_conference
from postgresqleu.util.request import get_int_or_error

from collections import OrderedDict


def build_graphdata(answers, options):
    optionhash = OrderedDict(list(zip(options, [0] * len(options))))
    if answers:
        for a in answers:
            optionhash[a] += 1
    return optionhash


def feedback_report(request, confname):
    conference = get_authenticated_conference(request, confname)

    sections = []
    # Get the global conference feedback. Actually pusing down the counting of options would
    # make this more efficient, but at least we're down to a single query now.
    curs = connection.cursor()
    curs.execute(
        """SELECT q.id, q.newfieldset, q.question, q.isfreetext, q.textchoices,
 array_agg(a.textanswer) FILTER (WHERE a.textanswer != '') AS textanswers,
 array_agg(a.rateanswer) FILTER (WHERE a.rateanswer IS NOT NULL) AS rateanswers
FROM confreg_conferencefeedbackquestion q
LEFT JOIN confreg_conferencefeedbackanswer a ON a.question_id=q.id
WHERE q.conference_id=%(confid)s
GROUP BY q.id
ORDER BY sortkey""", {
            'confid': conference.id,
        })

    currentsection = {}
    for questionid, newfieldset, question, isfreetext, textchoices, textanswers, rateanswers in curs.fetchall():
        if newfieldset:
            if currentsection:
                sections.append(currentsection)
                currentsection = {}
            if not currentsection:
                # Either first row, or a new fieldset per above
                currentsection['title'] = newfieldset
                currentsection['questions'] = []

        r = {
            'id': questionid,
            'question': question,
        }
        if isfreetext:
            if textchoices:
                # This is actually a set of choices, even if freetext is set
                r['graphdata'] = build_graphdata(textanswers, textchoices.split(';'))
            else:
                r['textanswers'] = textanswers
        else:
            r['graphdata'] = build_graphdata(rateanswers, list(range(0, 6)))

        if 'questions' in currentsection:
            currentsection['questions'].append(r)
        else:
            currentsection['questions'] = [r, ]
    else:
        sections.append(currentsection)

    return render(request, 'confreg/admin_conference_feedback.html', {
        'conference': conference,
        'numresponses': ConferenceFeedbackAnswer.objects.filter(conference=conference).aggregate(Count('attendee', distinct=True))['attendee__count'],
        'feedback': sections,
        'helplink': 'feedback',
    })


def build_toplists(what, query):
    cursor = connection.cursor()
    for k in ('topic_importance', 'content_quality', 'speaker_knowledge', 'speaker_quality'):
        tl = {'title': '%s by %s' % (what, k.replace('_', ' ').title())}
        cursor.execute(query.replace('{{key}}', k))
        tl['list'] = cursor.fetchall()
        yield tl


def feedback_sessions(request, confname):
    conference = get_authenticated_conference(request, confname)

    # Get all sessions that have actual comments on them
    cursor = connection.cursor()
    cursor.execute("SELECT concat(s.title, ' (' || (SELECT string_agg(fullname, ', ') FROM confreg_speaker spk INNER JOIN confreg_conferencesession_speaker css ON css.speaker_id=spk.id WHERE css.conferencesession_id=s.id) || ')'), conference_feedback FROM confreg_conferencesessionfeedback fb INNER JOIN confreg_conferencesession s ON fb.session_id=s.id WHERE s.conference_id=%s AND NOT conference_feedback='' ORDER BY 1,2" % (conference.id,))
    commented_sessions = cursor.fetchall()

    # Now for all of our fancy toplists
    # The django ORM just can't do this...
    minvotes = 10
    if request.method == 'POST':
        minvotes = get_int_or_error(request.POST, 'minvotes')

    toplists = []

    # Start with top sessions
    toplists.extend(build_toplists('Sessions', "SELECT s.title || COALESCE(' (' || (SELECT string_agg(fullname, ', ') FROM confreg_speaker spk INNER JOIN confreg_conferencesession_speaker css ON css.speaker_id=spk.id WHERE css.conferencesession_id=s.id) || ')', ''), avg(fb.{{key}}), count(*), stddev(fb.{{key}}) FROM confreg_conferencesessionfeedback fb INNER JOIN confreg_conferencesession s ON fb.session_id=s.id WHERE s.conference_id=%s AND fb.{{key}}>0 GROUP BY s.id HAVING count(*)>=%s ORDER BY 2 DESC" % (conference.id, minvotes)))

    # Now let's do the speakers
    toplists.extend(build_toplists('Speakers', "SELECT (SELECT string_agg(fullname, ', ') FROM confreg_speaker spk INNER JOIN confreg_conferencesession_speaker css ON css.speaker_id=spk.id WHERE css.conferencesession_id=s.id) AS speakername, avg(fb.{{key}}), count(*), stddev(fb.{{key}}) FROM confreg_conferencesessionfeedback fb INNER JOIN confreg_conferencesession s ON fb.session_id=s.id WHERE s.conference_id=%s AND fb.{{key}}>0 GROUP BY speakername HAVING count(*)>=%s ORDER BY 2 DESC" % (conference.id, minvotes)))

    return render(request, 'confreg/admin_session_feedback.html', {
        'conference': conference,
        'toplists': toplists,
        'minvotes': minvotes,
        'commented_sessions': commented_sessions,
        'breadcrumbs': (('/events/admin/{0}/reports/feedback/'.format(conference.urlname), 'Feedback'), ),
        'helplink': 'feedback',
    })