summaryrefslogtreecommitdiff
path: root/linaro_metrics/views.py
blob: 78f594965a092ae797f4761f037c571a0af4a5f4 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
import datetime
import os

import mock

import django.template.base

from django.conf import settings
from django.contrib.auth.models import User
from django.core.cache import cache
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.html import format_html

from patchwork.filters import DelegateFilter
from patchwork.models import Patch, Project
from patchwork.views import generic_list
from patchwork.templatetags.person import personify
from patchwork.templatetags.project import project_tags

from linaro_metrics.models import (
    Team,
    TeamCredit,
    TeamMembership,
)


def _get_index_context():
    key = 'index_context'
    context = cache.get(key)
    if context:
        return context

    cur_teams = []
    old_teams = []
    for t in Team.objects.order_by('display_name'):
        if t.active:
            cur_teams.append(t)
        else:
            old_teams.append(t)

    # The final distribution contains tons of stuff. Its basically any patch
    # that never got feedback for the history of Linaro. Don't show this in
    # the actual graph as it will skew things.
    distribution = TeamCredit.age_distribution_data(15, 7)
    last_distribution = distribution.pop()
    context = {
        'projects': Project.objects.all(),
        'cur_teams': cur_teams,
        'old_teams': old_teams,
        'metrics': TeamCredit.get_month_metrics(),
        'acceptance': TeamCredit.time_to_acceptance(),
        'distribution': distribution,
        'last_distribution': last_distribution,
    }
    cache.set(key, context, 60 * 60)  # cache for an hour
    return context


def index_view(request):
    return render(request, 'linaro_metrics/index.html', _get_index_context())


def faq_view(request):
    return render(request, 'linaro_metrics/faq.html')


def team_overview(request):
    context = {'teams': Team.objects.filter(active=True)}
    return render(request, 'linaro_metrics/teams.html', context)


# The patchwork version of the "personify" filter requires a project. We don't
# have a project for "team". Although personify is a simple function, we can't
# directly monkey-patch it due to the way django templates load filters. This
# tricks django into using loading our version of "personify". Our version will
# call the original if possible, but in the case there is no project, we'll
# just return the person's name
def hack_add_lib(self, lib):
    def _non_project_personify(person, project):
        if project:
            return personify(person, project)
        name = person.name
        if not name:
            name = person.email
        if not person.user:
            return format_html(name)
        # "name" is unicode, so we must do a string.format with unicode or risk
        # encoding issues for non-ascii characters.
        return format_html(
            u'<a href=/patches/{}/>{}</a>', person.user.id, name)

    def _non_project_tags(context):
        if context['project']:
            return project_tags(context)
        return ''

    if 'personify' in lib.filters:
        lib.filters['personify'] = _non_project_personify
    lib.simple_tag(_non_project_tags, takes_context=True, name='project_tags')
    return real_add_lib(self, lib)


real_add_lib = django.template.base.Parser.add_library
django.template.base.Parser.add_library = hack_add_lib


def _non_project_ctx(request, view, view_args, patches):
    # generic_list requires a Project object. A team has no specific project,
    # so we just use Mock to pass dummy object through. We set two key values
    # to influence the function:
    #  is_editable: user can't modify things
    #  tags: tags aren't applicable to this use case
    project = mock.Mock()
    project.is_editable.return_value = False
    project.tags = []

    orig = request.user.is_authenticated
    request.user.is_authenticated = mock.Mock()
    request.user.is_authenticated.return_value = False
    context = generic_list(
        request, project, view, view_args=view_args, patches=patches)
    request.user.is_authenticated = orig

    # The DelegateFilter won't work for because its tied to a project/user.
    context['filters']._filters = [
        x for x in context['filters']._filters
        if not isinstance(x, DelegateFilter)]
    return context


def team_view(request, team):
    team = get_object_or_404(Team, name=team)
    patches = Patch.objects.filter(teamcredit__team=team)
    view_args = {'team': team.name}

    month_choices = [6, 12, 18, 24]

    try:
        month = int(request.GET.get('months', '6'))
    except ValueError:
        month = 6

    context = _non_project_ctx(
        request, 'linaro_metrics.views.team_view', view_args, patches)
    context.update({
        'team': team,
        'memberships': TeamMembership.objects.filter(team=team),
        'project': None,  # Prevent trying to show project details in header.
        'metrics': TeamCredit.get_month_metrics(team=team,
                                                num_months=month),
        'acceptance': TeamCredit.time_to_acceptance(team=team),
        'patches': patches,
        'months': month_choices,
        'returned_month': month,
    })
    return render(request, 'linaro_metrics/team.html', context)


def user_view(request, user):
    user = get_object_or_404(User, id=user)
    patches = Patch.objects.filter(submitter__user=user)

    view_args = {'user': user.id}

    context = _non_project_ctx(request, 'linaro_metrics.views.user_view',
                               view_args=view_args, patches=patches)
    context.update({
        'patch_user': user,
        'memberships': TeamMembership.objects.filter(user=user),
        'project': None,  # Prevent trying to show project details in header.
    })
    return render(request, 'linaro_metrics/user.html', context)


def project_view(request, project_id):
    project = get_object_or_404(Project, linkname=project_id)
    commit = os.path.join(
        settings.REPO_DIR, project.linkname, 'patchwork-last-commit')
    try:
        with open(commit) as f:
            project.last_commit = f.read()
    except:
        project.last_commit = ''

    view_args = {'project_id': project.linkname}
    context = generic_list(
        request, project, 'patchwork.views.patch.patch_list',
        view_args=view_args)
    context.update({
        'metrics': TeamCredit.get_month_metrics(patch__project=project)
    })
    return render(request, 'linaro_metrics/project.html', context)


def _subtract_months(dt, months):
    month = dt.month - months
    if month > 0:
        return dt.replace(month=month)
    else:
        return dt.replace(month=month + 12, year=dt.year - 1)


def report_project_activity(request):
    now = datetime.datetime.now()
    last_full_month = datetime.datetime(now.year, now.month, 1)
    last_12 = _subtract_months(last_full_month, 12)
    last_6 = _subtract_months(last_full_month, 6)
    last_3 = _subtract_months(last_full_month, 3)
    last_1 = _subtract_months(last_full_month, 1)

    summary = {}
    qs = TeamCredit.patch_count_by_month(
        12, values=('month', 'patch__project__name'),
        patch__date__lte=last_full_month)
    for x in qs:
        e = summary.setdefault(
            x['patch__project__name'], {
                'last_12': 0, 'last_6': 0, 'last_3': 0, 'last_1': 0})
        if x['month'] >= last_1:
            e['last_1'] += x['patch__pk__count']
        if x['month'] >= last_3:
            e['last_3'] += x['patch__pk__count']
        if x['month'] >= last_6:
            e['last_6'] += x['patch__pk__count']
        if x['month'] >= last_12:
            e['last_12'] += x['patch__pk__count']
    items = [(x, y['last_12'], y['last_6'], y['last_3'], y['last_1'])
             for x, y in summary.iteritems()]
    summary = sorted(items, key=lambda x: x[1], reverse=True)[:20]
    context = {'summary': summary}
    return render(
        request, 'linaro_metrics/report_project_activity.html', context)


def old_patch_link(request, patch):
    patch = int(patch)
    if patch > 55493:
        # this was when we stood up the newest patches. If people saved links
        # its to the older server, so redirect them there:
        return redirect('https://patches-old.linaro.org/%d/' % patch)
    # its a patch both databases have, so show our version
    return redirect('patch-detail', patch_id=patch)


def user_register(request):
    return render(request, 'linaro_metrics/user-register.html')