def signoff(request, locale_code, app_code): """View to show recent sign-offs and opportunities to sign off. This view is the main entry point to localizers to add sign-offs and to review what they're shipping. It's also the entry point for drivers to review existing sign-offs. """ appver = get_object_or_404(AppVersion, code=app_code) lang = get_object_or_404(Locale, code=locale_code) forest = appver.tree.l10n repo = get_object_or_404(Repository, locale=lang, forest=forest) # which pushes to show actions = list(a_id for a_id, flag in \ signoff_actions(appversions={'id': appver.id}, locales={'id': lang.id})) actions = list(Action.objects.filter(id__in=actions) .select_related('signoff__push', 'author')) # get current status of signoffs pending, rejected, accepted, initial_diff = signoff_summary(actions) pushes, currentpush, suggested_signoff = annotated_pushes(repo, appver, lang, actions, initial_diff) return render(request, 'shipping/signoffs.html', { 'appver': appver, 'language': lang, 'pushes': pushes, 'current': currentpush, 'pending': pending, 'rejected': rejected, 'accepted': accepted, 'tree': appver.tree.code, 'repo': repo, 'suggested_signoff': suggested_signoff, 'login_form_needs_reload': True, 'request': request, })
def teamsnippet(loc): runs = list(loc.run_set.filter(active__isnull=False).select_related('tree') .order_by('tree__code')) # this locale isn't active in our trees yet if not runs: return '' _application_codes = [(x.code, x) for x in Application.objects.all()] # sort their keys so that the longest application codes come first # otherwise, suppose we had these keys: # 'fe', 'foo', 'fennec' then when matching against a tree code called # 'fennec_10x' then, it would "accidentally" match on 'fe' and not # 'fennec'. _application_codes.sort(lambda x, y: -cmp(len(x[0]), len(y[0]))) # Create these two based on all appversions attached to a tree so that in # the big loop on runs we don't need to make excessive queries for # appversions and applications _trees_to_appversions = {} for appver in (AppVersion.objects .exclude(tree__isnull=True) .select_related('tree')): _trees_to_appversions[appver.tree] = appver def tree_to_application(tree): for key, app in _application_codes: if tree.code.startswith(key): return app def tree_to_appversion(tree): return _trees_to_appversions.get(tree) # find good revisions to sign-off, latest run needs to be green. # keep in sync with api.annotated_pushes suggested_runs = filter(lambda r: r.allmissing == 0 and r.errors == 0, runs) suggested_rev = dict(Run_Revisions.objects .filter(run__in=suggested_runs, changeset__repositories__locale=loc) .values_list('run_id', 'changeset__revision')) applications = defaultdict(list) pushes = set() for run_ in runs: # copy the Run instance into a fancy dict but only copy those whose # key doesn't start with an underscore run = Run(dict((k, getattr(run_, k)) for k in run_.__dict__ if not k.startswith('_'))) run.allmissing = run_.allmissing # a @property of the Run model run.tree = run_.tree # foreign key lookup application = tree_to_application(run_.tree) run.changed_ratio = run.completion run.unchanged_ratio = 100 * run.unchanged / run.total run.missing_ratio = 100 * run.allmissing / run.total # cheat a bit and make sure that the red stripe on the chart is at # least 1px wide if run.allmissing and run.missing_ratio == 0: run.missing_ratio = 1 for ratio in (run.changed_ratio, run.unchanged_ratio): if ratio: ratio -= 1 break appversion = tree_to_appversion(run.tree) # because Django templates (stupidly) swallows lookup errors we # have to apply the missing defaults too run.pending = run.rejected = run.accepted = \ run.suggested_shortrev = run.appversion = None if appversion: run.appversion = appversion actions = [action_id for action_id, flag in signoff_actions( appversions=[run.appversion], locales=[loc.id]) ] actions = Action.objects.filter(id__in=actions) \ .select_related('signoff__push') # get current status of signoffs # we really only need the shortrevs, we'll get those below run.pending, run.rejected, run.accepted, __ = \ signoff_summary(actions) pushes.update((run.pending, run.rejected, run.accepted)) # get the suggested signoff. If there are existing actions # we'll unset it when we get the shortrevs for those below if run_.id in suggested_rev: run.suggested_shortrev = suggested_rev[run_.id][:12] applications[application].append(run) # get the tip shortrevs for all our pushes pushes = map(lambda p: p.id, filter(None, pushes)) tip4push = dict(Push.objects .annotate(tc=Max('changesets')) .filter(id__in=pushes) .values_list('id', 'tc')) rev4id = dict(Changeset.objects .filter(id__in=tip4push.values()) .values_list('id', 'revision')) for runs in applications.itervalues(): for run in runs: for k in ('pending', 'rejected', 'accepted'): if run[k] is not None: run[k + '_rev'] = rev4id[tip4push[run[k].id]][:12] # unset the suggestion if there's existing signoff action if run[k + '_rev'] == run.suggested_shortrev: run.suggested_shortrev = None applications = ((k, v) for (k, v) in applications.items()) return render_to_string('shipping/team-snippet.html', {'locale': loc, 'applications': applications, })