Beispiel #1
0
 def testOneNew(self):
     """One accepted signoff on the new appversion, none on the old.
     Old appversion comes back empty.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, Action.ACCEPTED)
     eq_(repo.changesets.count(), 2)
     eq_(_actions4appversion(self.old_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     a4av, not_found = _actions4appversion(self.new_av, set([locale.id]),
                                           100)
     eq_(not_found, set())
     eq_(a4av.keys(), [locale.id])
     flag, action_id = a4av[locale.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     eq_(Signoff.objects.get(action=action_id).locale_id, locale.id)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av],
         {'da': ['fx1.1', {
             Action.ACCEPTED: self.actions[1].id
         }]})
     eq_(flagdata[self.old_av], {})
Beispiel #2
0
 def testOneOld(self):
     """One locale signed off and accepted on old appversion,
     nothing new on new, thus falling back to the old one.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, None)
     eq_(repo.changesets.count(), 2)
     flaglocs4av, not_found = _actions4appversion(self.old_av,
                                                  set([locale.id]), 100)
     eq_(not_found, set())
     eq_(flaglocs4av.keys(), [locale.id])
     flag, action_id = flaglocs4av[locale.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     eq_(Signoff.objects.get(action=action_id).locale_id, locale.id)
     eq_(_actions4appversion(self.new_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av],
         {'da': ['fx1.0', {
             Action.ACCEPTED: self.actions[1].id
         }]})
     eq_(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #3
0
 def _get_flags_and_actions(self):
     __, flags = (api.flags4appversions([self.av],
         locales=[self.locale.id])
                       .get(self.av, {})
                       .get(self.locale.code, [None, {}]))
     actions = Action.objects.filter(id__in=flags.values())
     return flags, actions
Beispiel #4
0
 def testOneOld(self):
     """One locale signed off and accepted on old appversion,
     nothing new on new, thus falling back to the old one.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, None)
     self.assertEqual(repo.changesets.count(), 2)
     flaglocs4av, not_found = _actions4appversion(self.old_av, {locale.id},
                                                  None, 100)
     self.assertEqual(not_found, set())
     self.assertListEqual(flaglocs4av.keys(), [locale.id])
     flag, action_id = flaglocs4av[locale.id].items()[0]
     self.assertEqual(flag, Action.ACCEPTED)
     self.assertEqual(
         Signoff.objects.get(action=action_id).locale_id, locale.id)
     self.assertTupleEqual(
         _actions4appversion(self.new_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(
         flagdata[self.new_av],
         {'da': ['fx1.0', {
             Action.ACCEPTED: self.actions[1].id
         }]})
     self.assertDictEqual(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #5
0
def confirm_ship_mstone(request):
    """Intermediate page when shipping a milestone.

    Gathers all data to verify when shipping.
    Ends up in ship_mstone if everything is fine.
    Redirects to milestones() in case of trouble.
    """
    if not request.GET.get('ms'):
        raise http.Http404("ms must be supplied")
    mstone = get_object_or_404(Milestone, code=request.GET['ms'])
    if mstone.status != Milestone.OPEN:
        return http.HttpResponseRedirect(reverse('shipping.views.milestones'))
    flags4loc = (flags4appversions(appversions={'id': mstone.appver.id})
                 [mstone.appver])

    pending_locs = []
    good = 0
    for loc, (real_av, flags) in flags4loc.iteritems():
        if real_av == mstone.appver.code and Action.PENDING in flags:
            # pending
            pending_locs.append(loc)
        if Action.ACCEPTED in flags:
            # good
            good += 1
    pending_locs.sort()
    return render(request, 'shipping/confirm-ship.html', {
                  'mstone': mstone,
                  'pending_locs': pending_locs,
                  'good': good,
                  'login_form_needs_reload': True,
                  'request': request,
                  })
Beispiel #6
0
    def handle(self, *args, **options):
        locflags4av = flags4appversions()
        actions = set()
        for flags4loc in locflags4av.itervalues():
            for real_av, flags in flags4loc.itervalues():
                if Action.ACCEPTED in flags:
                    actions.add(flags[Action.ACCEPTED])
        push4action = dict(
            Action.objects.filter(id__in=actions).values_list(
                'id', 'signoff__push'))
        cs4push = dict(
            Push.objects.filter(id__in=set(push4action.values())).annotate(
                tip=Max("changesets")).values_list('id', 'tip'))
        revs = dict(
            Changeset.objects.filter(id__in=cs4push.values()).values_list(
                'id', 'revision'))

        rv = defaultdict(dict)
        for av, flags4loc in locflags4av.iteritems():
            for loc, (real_av, flags) in flags4loc.iteritems():
                if Action.ACCEPTED not in flags:
                    continue
                pushid = push4action[flags[Action.ACCEPTED]]
                rv[av.code][loc] = revs[cs4push[pushid]][:12]

        print json.dumps(rv, indent=2, sort_keys=True)
Beispiel #7
0
    def get_context_data(self, lang, appver):
        # which pushes to show
        real_av, flags = (flags4appversions([appver], locales=[lang.id]).get(
            appver, {}).get(lang.code, [None, {}]))
        actions = list(
            Action.objects.filter(id__in=flags.values()).select_related(
                'signoff__push__repository', 'author'))

        # get current status of signoffs
        push4action = dict((a.id, a.signoff.push) for a in actions)
        pending = push4action.get(flags.get(Action.PENDING))
        rejected = push4action.get(flags.get(Action.REJECTED))
        accepted = push4action.get(flags.get(Action.ACCEPTED))

        if real_av != appver.code and accepted is not None:
            # we're falling back, add the accepted push to the table
            fallback = accepted
        else:
            fallback = None

        pushes_data = self.annotated_pushes(
            lang,
            appver,
            actions=actions,
            flags=flags,
            fallback=fallback,
            count=self.count,
        )

        # Check if this is the very first release.
        # Only applies to products that have a fallback (Firefox for example).
        first = appver.fallback is not None and accepted is None

        try:
            team_locale = (TeamLocaleThrough.objects.current().get(
                locale=lang).team)
        except TeamLocaleThrough.DoesNotExist:
            team_locale = lang

        if pushes_data['next_push_date']:
            next_push_date = pushes_data['next_push_date'].isoformat()
        else:
            next_push_date = None

        return {
            'appver': appver,
            'language': lang,
            'team_locale': team_locale,
            'pushes': pushes_data['pushes'],
            'pushes_left': pushes_data['pushes_left'],
            'next_push_date': next_push_date,
            'pending': pending,
            'rejected': rejected,
            'accepted': accepted,
            'first': first,
            'suggested_signoff': pushes_data['suggested_signoff'],
            'login_form_needs_reload': True,
            'fallback': fallback,
            'real_av': real_av,
        }
Beispiel #8
0
 def testOneNew(self):
     """One accepted signoff on the new appversion, none on the old.
     Old appversion comes back empty.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, Action.ACCEPTED)
     self.assertEqual(repo.changesets.count(), 2)
     self.assertTupleEqual(
         _actions4appversion(self.old_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     a4av, not_found = _actions4appversion(self.new_av,
                                           {locale.id}, None, 100)
     self.assertEqual(not_found, set())
     self.assertListEqual(list(a4av.keys()), [locale.id])
     flag, action_id = list(a4av[locale.id].items())[0]
     self.assertEqual(flag, Action.ACCEPTED)
     self.assertEqual(
         Signoff.objects.get(action=action_id).locale_id,
         locale.id)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(
         flagdata[self.new_av],
         {'da':
             ['fx1.1', {Action.ACCEPTED: self.actions[1].id}]})
     self.assertDictEqual(flagdata[self.old_av], {})
Beispiel #9
0
 def testOneNew(self):
     """One accepted signoff on the new appversion, none on the old.
     Old appversion comes back empty.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, Action.ACCEPTED)
     self.assertEqual(repo.changesets.count(), 2)
     self.assertTupleEqual(
         _actions4appversion(self.old_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     a4av, not_found = _actions4appversion(self.new_av, {locale.id}, None,
                                           100)
     self.assertEqual(not_found, set())
     self.assertListEqual(a4av.keys(), [locale.id])
     flag, action_id = a4av[locale.id].items()[0]
     self.assertEqual(flag, Action.ACCEPTED)
     self.assertEqual(
         Signoff.objects.get(action=action_id).locale_id, locale.id)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(
         flagdata[self.new_av],
         {'da': ['fx1.1', {
             Action.ACCEPTED: self.actions[1].id
         }]})
     self.assertDictEqual(flagdata[self.old_av], {})
Beispiel #10
0
    def test_signoff_annotated_pushes(self):
        view = SignoffView()
        locale = Locale.objects.get(code='de')

        real_av, flags = (api.flags4appversions(
            locales={'id': locale.id},
            appversions={'id': self.av.id})
                          .get(self.av, {})
                          .get(locale.code, [None, {}]))
        actions = list(Action.objects.filter(id__in=flags.values())
                       .select_related('signoff__push__repository', 'author'))
        fallback, = actions
        assert fallback.flag == Action.ACCEPTED, fallback.flag
        pushes, suggested_signoff = view.annotated_pushes(
            actions,
            flags,
            fallback,
            locale,
            self.av
        )
        eq_(suggested_signoff, None)
        changesets = [c for p in pushes for c in p['changes']]
        revisions = [x.revision for x in changesets]
        # only `de` changes in the right order
        eq_(revisions, [u'l10n de 0003', u'l10n de 0002'])
Beispiel #11
0
def confirm_ship_mstone(request):
    """Intermediate page when shipping a milestone.

    Gathers all data to verify when shipping.
    Ends up in ship_mstone if everything is fine.
    Redirects to milestones() in case of trouble.
    """
    if not request.GET.get('ms'):
        raise http.Http404("ms must be supplied")
    mstone = get_object_or_404(Milestone, code=request.GET['ms'])
    if mstone.status != Milestone.OPEN:
        return http.HttpResponseRedirect(reverse('shipping.views.milestones'))
    flags4loc = (flags4appversions(
        appversions={'id': mstone.appver.id})[mstone.appver])

    pending_locs = []
    good = 0
    for loc, (real_av, flags) in flags4loc.iteritems():
        if real_av == mstone.appver.code and Action.PENDING in flags:
            # pending
            pending_locs.append(loc)
        if Action.ACCEPTED in flags:
            # good
            good += 1
    pending_locs.sort()
    return render(
        request, 'shipping/confirm-ship.html', {
            'mstone': mstone,
            'pending_locs': pending_locs,
            'good': good,
            'login_form_needs_reload': True,
            'request': request,
        })
Beispiel #12
0
 def testOneOld(self):
     """One locale signed off and accepted on old appversion,
     nothing new on new, thus falling back to the old one.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, None)
     self.assertEqual(repo.changesets.count(), 2)
     flaglocs4av, not_found = _actions4appversion(self.old_av,
                                                  {locale.id},
                                                  None,
                                                  100)
     self.assertEqual(not_found, set())
     self.assertListEqual(list(flaglocs4av.keys()), [locale.id])
     flag, action_id = list(flaglocs4av[locale.id].items())[0]
     self.assertEqual(flag, Action.ACCEPTED)
     self.assertEqual(
         Signoff.objects.get(action=action_id).locale_id,
         locale.id)
     self.assertTupleEqual(
         _actions4appversion(self.new_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(
         flagdata[self.new_av],
         {'da':
             ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
          })
     self.assertDictEqual(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #13
0
    def test_signoff_annotated_pushes(self):
        view = SignoffView()
        locale = Locale.objects.get(code='de')

        real_av, flags = (api.flags4appversions(
            [self.av],
            locales=[locale.id]).get(self.av, {}).get(locale.code, [None, {}]))
        actions = list(
            Action.objects.filter(id__in=flags.values()).select_related(
                'signoff__push__repository', 'author'))
        fallback, = actions
        assert fallback.flag == Action.ACCEPTED, fallback.flag
        pushes_data = view.annotated_pushes(
            locale,
            self.av,
            actions=actions,
            flags=flags,
            fallback=fallback,
        )
        suggested_signoff = pushes_data['suggested_signoff']
        self.assertIsNone(suggested_signoff)
        pushes = pushes_data['pushes']
        changesets = [c for p in pushes for c in p['changes']]
        revisions = [x.revision for x in changesets]
        # only `de` changes in the right order
        self.assertListEqual(revisions, ['l10n de 0003', 'l10n de 0002'])
Beispiel #14
0
 def _get_flags_and_actions(self):
     __, flags = (api.flags4appversions([self.av],
         locales=[self.locale.id])
                       .get(self.av, {})
                       .get(self.locale.code, [None, {}]))
     actions = Action.objects.filter(id__in=flags.values())
     return flags, actions
Beispiel #15
0
 def testOneOld(self):
     """One locale signed off and accepted on old appversion,
     nothing new on new, thus falling back to the old one.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, None)
     eq_(repo.changesets.count(), 2)
     flaglocs4av, not_found = _actions4appversion(self.old_av,
                                                  set([locale.id]),
                                                  100)
     eq_(not_found, set())
     eq_(flaglocs4av.keys(), [locale.id])
     flag, action_id = flaglocs4av[locale.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     eq_(Signoff.objects.get(action=action_id).locale_id, locale.id)
     eq_(_actions4appversion(self.new_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {'da':
         ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
         })
     eq_(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #16
0
    def test_signoff_annotated_pushes(self):
        view = SignoffView()
        locale = Locale.objects.get(code='de')

        real_av, flags = (
            api.flags4appversions([self.av], locales=[locale.id])
            .get(self.av, {})
            .get(locale.code, [None, {}]))
        actions = list(Action.objects.filter(id__in=flags.values())
                       .select_related('signoff__push__repository', 'author'))
        fallback, = actions
        assert fallback.flag == Action.ACCEPTED, fallback.flag
        pushes_data = view.annotated_pushes(
            locale,
            self.av,
            actions=actions,
            flags=flags,
            fallback=fallback,
        )
        suggested_signoff = pushes_data['suggested_signoff']
        self.assertIsNone(suggested_signoff)
        pushes = pushes_data['pushes']
        changesets = [c for p in pushes for c in p['changes']]
        revisions = [x.revision for x in changesets]
        # only `de` changes in the right order
        self.assertListEqual(revisions, ['l10n de 0003', 'l10n de 0002'])
Beispiel #17
0
 def _get_flags_and_actions(self):
     __, flags = (api.flags4appversions(
         locales=self._locale_search,
         appversions=self._appver_search)
                       .get(self.av, {})
                       .get(self.locale.code, [None, {}]))
     actions = Action.objects.filter(id__in=flags.values())
     return flags, actions
Beispiel #18
0
 def testOneOldObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.OBSOLETED, None)
     eq_(repo.changesets.count(), 2)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {})
     eq_(flagdata[self.old_av], {})
Beispiel #19
0
 def testOneOldObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.OBSOLETED, None)
     eq_(repo.changesets.count(), 2)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {})
     eq_(flagdata[self.old_av], {})
Beispiel #20
0
 def testOneOldObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.OBSOLETED, None)
     self.assertEqual(repo.changesets.count(), 2)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {})
     self.assertDictEqual(flagdata[self.old_av], {})
Beispiel #21
0
 def test_getflags(self):
     """Test that the list returns the right flags."""
     flags = flags4appversions(appversions={"code": "fx1.0"})
     av = AppVersion.objects.get(code="fx1.0")
     eq_(flags, {av: {
         "pl": ["fx1.0", {Action.PENDING: 2}],
         "de": ["fx1.0", {Action.ACCEPTED: 3}],
         "fr": ["fx1.0", {Action.REJECTED: 5}],
         "da": ["fx1.0", {Action.ACCEPTED: 8,
                          Action.PENDING: 7}]
     }})
Beispiel #22
0
 def test_getflags(self):
     """Test that the list returns the right flags."""
     av = AppVersion.objects.get(code="fx1.0")
     flags = flags4appversions([av], locales=list(range(1, 5)))
     self.assertDictEqual(flags, {av: {
         "pl": ["fx1.0", {Action.PENDING: 2}],
         "de": ["fx1.0", {Action.ACCEPTED: 3}],
         "fr": ["fx1.0", {Action.REJECTED: 5}],
         "da": ["fx1.0", {Action.ACCEPTED: 8,
                          Action.PENDING: 7}]
     }})
Beispiel #23
0
 def test_getflags(self):
     """Test that the list returns the right flags."""
     av = AppVersion.objects.get(code="fx1.0")
     flags = flags4appversions([av], locales=range(1, 5))
     eq_(flags, {av: {
         "pl": ["fx1.0", {Action.PENDING: 2}],
         "de": ["fx1.0", {Action.ACCEPTED: 3}],
         "fr": ["fx1.0", {Action.REJECTED: 5}],
         "da": ["fx1.0", {Action.ACCEPTED: 8,
                          Action.PENDING: 7}]
     }})
Beispiel #24
0
 def testOneOldObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.OBSOLETED, None)
     self.assertEqual(repo.changesets.count(), 2)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {})
     self.assertDictEqual(flagdata[self.old_av], {})
Beispiel #25
0
 def testOneOldAndOtherNew(self):
     da = Locale.objects.get(code='da')
     de = Locale.objects.get(code='de')
     repo = self._setup(da, Action.ACCEPTED, None)
     self.assertEqual(repo.changesets.count(), 2)
     repo = self._setup(de, None, Action.ACCEPTED)
     self.assertEqual(repo.changesets.count(), 2)
     a4av, not_found = _actions4appversion(self.old_av, {da.id, de.id},
                                           None, 100)
     self.assertSetEqual(not_found, {de.id})
     self.assertListEqual(a4av.keys(), [da.id])
     flag, action_id = a4av[da.id].items()[0]
     self.assertEqual(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.new_av, {da.id, de.id},
                                           None, 100)
     self.assertSetEqual(not_found, {da.id})
     self.assertListEqual(a4av.keys(), [de.id])
     flag, action_id = a4av[de.id].items()[0]
     self.assertEqual(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.old_av, {da.id, de.id},
                                           None, 100)
     self.assertSetEqual(not_found, {de.id})
     self.assertListEqual(a4av.keys(), [da.id])
     flag, action_id = a4av[da.id].items()[0]
     self.assertEqual(flag, Action.ACCEPTED)
     a4avs = actions4appversions(appversions=[self.new_av],
                                 locales=[da.id, de.id])
     self.assertEqual(len(a4avs), 2)
     self.assertIn(self.old_av, a4avs)
     self.assertIn(self.new_av, a4avs)
     a4av = a4avs[self.new_av]
     self.assertEqual(len(a4av), 1)
     a4av = a4avs[self.old_av]
     self.assertEqual(len(a4av), 1)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(
         flagdata[self.new_av], {
             'da': ['fx1.0', {
                 Action.ACCEPTED: self.actions[1].id
             }],
             'de': ['fx1.1', {
                 Action.ACCEPTED: self.actions[3].id
             }]
         })
     self.assertDictEqual(
         flagdata[self.old_av],
         {'da': ['fx1.0', {
             Action.ACCEPTED: self.actions[1].id
         }]})
Beispiel #26
0
 def testOneOldAndNewObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, Action.OBSOLETED)
     eq_(repo.changesets.count(), 3)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {})
     eq_(flagdata[self.old_av], {'da':
         ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
         })
Beispiel #27
0
 def testOneOldAndNewObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, Action.OBSOLETED)
     eq_(repo.changesets.count(), 3)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {})
     eq_(flagdata[self.old_av],
         {'da': ['fx1.0', {
             Action.ACCEPTED: self.actions[1].id
         }]})
Beispiel #28
0
 def testOneOldAndOtherNew(self):
     da = Locale.objects.get(code='da')
     de = Locale.objects.get(code='de')
     repo = self._setup(da, Action.ACCEPTED, None)
     eq_(repo.changesets.count(), 2)
     repo = self._setup(de, None, Action.ACCEPTED)
     eq_(repo.changesets.count(), 2)
     a4av, not_found = _actions4appversion(self.old_av, set([da.id, de.id]),
                                           100)
     eq_(not_found, set([de.id]))
     eq_(a4av.keys(), [da.id])
     flag, action_id = a4av[da.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.new_av, set([da.id, de.id]),
                                           100)
     eq_(not_found, set([da.id]))
     eq_(a4av.keys(), [de.id])
     flag, action_id = a4av[de.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.old_av, set([da.id, de.id]),
                                           100)
     eq_(not_found, set([de.id]))
     eq_(a4av.keys(), [da.id])
     flag, action_id = a4av[da.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     a4avs = actions4appversions(appversions=[self.new_av],
                                 locales=[da.id, de.id])
     eq_(len(a4avs), 2)
     ok_(self.old_av in a4avs)
     ok_(self.new_av in a4avs)
     a4av = a4avs[self.new_av]
     eq_(len(a4av), 1)
     a4av = a4avs[self.old_av]
     eq_(len(a4av), 1)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(
         flagdata[self.new_av], {
             'da': ['fx1.0', {
                 Action.ACCEPTED: self.actions[1].id
             }],
             'de': ['fx1.1', {
                 Action.ACCEPTED: self.actions[3].id
             }]
         })
     eq_(flagdata[self.old_av],
         {'da': ['fx1.0', {
             Action.ACCEPTED: self.actions[1].id
         }]})
Beispiel #29
0
 def testEmpty(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, None)
     eq_(repo.changesets.count(), 1)
     eq_(_actions4appversion(self.old_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     eq_(_actions4appversion(self.new_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {})
     eq_(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #30
0
 def testEmpty(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, None)
     eq_(repo.changesets.count(), 1)
     eq_(_actions4appversion(self.old_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     eq_(_actions4appversion(self.new_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {})
     eq_(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #31
0
 def testOneOldAndNew(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, Action.ACCEPTED)
     eq_(repo.changesets.count(), 3)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {'da':
         ['fx1.1', {Action.ACCEPTED: self.actions[3].id}]
         })
     eq_(flagdata[self.old_av], {'da':
         ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
         })
Beispiel #32
0
 def testOneOldAndNewObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, Action.OBSOLETED)
     self.assertEqual(repo.changesets.count(), 3)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {})
     self.assertDictEqual(
         flagdata[self.old_av],
         {'da':
             ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
          })
Beispiel #33
0
 def testOneOldAndNew(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, Action.ACCEPTED)
     eq_(repo.changesets.count(), 3)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {'da':
         ['fx1.1', {Action.ACCEPTED: self.actions[3].id}]
         })
     eq_(flagdata[self.old_av], {'da':
         ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
         })
Beispiel #34
0
 def testOneOldAndNewObsoleted(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, Action.ACCEPTED, Action.OBSOLETED)
     self.assertEqual(repo.changesets.count(), 3)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {})
     self.assertDictEqual(
         flagdata[self.old_av],
         {'da': ['fx1.0', {
             Action.ACCEPTED: self.actions[1].id
         }]})
Beispiel #35
0
 def testOneOldAndOtherNew(self):
     da = Locale.objects.get(code='da')
     de = Locale.objects.get(code='de')
     repo = self._setup(da, Action.ACCEPTED, None)
     eq_(repo.changesets.count(), 2)
     repo = self._setup(de, None, Action.ACCEPTED)
     eq_(repo.changesets.count(), 2)
     a4av, not_found = _actions4appversion(self.old_av,
                                           {da.id, de.id}, None, 100)
     eq_(not_found, {de.id})
     eq_(a4av.keys(), [da.id])
     flag, action_id = a4av[da.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.new_av,
                                           {da.id, de.id}, None, 100)
     eq_(not_found, {da.id})
     eq_(a4av.keys(), [de.id])
     flag, action_id = a4av[de.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.old_av,
                                           {da.id, de.id}, None, 100)
     eq_(not_found, {de.id})
     eq_(a4av.keys(), [da.id])
     flag, action_id = a4av[da.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     a4avs = actions4appversions(appversions=[self.new_av],
                                 locales=[da.id, de.id])
     eq_(len(a4avs), 2)
     ok_(self.old_av in a4avs)
     ok_(self.new_av in a4avs)
     a4av = a4avs[self.new_av]
     eq_(len(a4av), 1)
     a4av = a4avs[self.old_av]
     eq_(len(a4av), 1)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {
         'da': ['fx1.0', {Action.ACCEPTED: self.actions[1].id}],
         'de': ['fx1.1', {Action.ACCEPTED: self.actions[3].id}]
         })
     eq_(flagdata[self.old_av], {
         'da': ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
         })
Beispiel #36
0
 def testOneOldAndOtherNew(self):
     da = Locale.objects.get(code='da')
     de = Locale.objects.get(code='de')
     repo = self._setup(da, Action.ACCEPTED, None)
     self.assertEqual(repo.changesets.count(), 2)
     repo = self._setup(de, None, Action.ACCEPTED)
     self.assertEqual(repo.changesets.count(), 2)
     a4av, not_found = _actions4appversion(self.old_av,
                                           {da.id, de.id}, None, 100)
     self.assertSetEqual(not_found, {de.id})
     self.assertListEqual(list(a4av.keys()), [da.id])
     flag, action_id = list(a4av[da.id].items())[0]
     self.assertEqual(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.new_av,
                                           {da.id, de.id}, None, 100)
     self.assertSetEqual(not_found, {da.id})
     self.assertListEqual(list(a4av.keys()), [de.id])
     flag, action_id = list(a4av[de.id].items())[0]
     self.assertEqual(flag, Action.ACCEPTED)
     a4av, not_found = _actions4appversion(self.old_av,
                                           {da.id, de.id}, None, 100)
     self.assertSetEqual(not_found, {de.id})
     self.assertListEqual(list(a4av.keys()), [da.id])
     flag, action_id = list(a4av[da.id].items())[0]
     self.assertEqual(flag, Action.ACCEPTED)
     a4avs = actions4appversions(appversions=[self.new_av],
                                 locales=[da.id, de.id])
     self.assertEqual(len(a4avs), 2)
     self.assertIn(self.old_av, a4avs)
     self.assertIn(self.new_av, a4avs)
     a4av = a4avs[self.new_av]
     self.assertEqual(len(a4av), 1)
     a4av = a4avs[self.old_av]
     self.assertEqual(len(a4av), 1)
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {
         'da': ['fx1.0', {Action.ACCEPTED: self.actions[1].id}],
         'de': ['fx1.1', {Action.ACCEPTED: self.actions[3].id}]
         })
     self.assertDictEqual(flagdata[self.old_av], {
         'da': ['fx1.0', {Action.ACCEPTED: self.actions[1].id}]
         })
Beispiel #37
0
 def testEmpty(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, None)
     self.assertEqual(repo.changesets.count(), 1)
     self.assertTupleEqual(
         _actions4appversion(self.old_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     self.assertTupleEqual(
         _actions4appversion(self.new_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {})
     self.assertDictEqual(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #38
0
 def testEmpty(self):
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, None)
     self.assertEqual(repo.changesets.count(), 1)
     self.assertTupleEqual(
         _actions4appversion(self.old_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     self.assertTupleEqual(
         _actions4appversion(self.new_av, {locale.id}, None, 100),
         ({}, {locale.id}))
     avs = AppVersion.objects.all()
     flagdata = flags4appversions(avs)
     self.assertIn(self.old_av, flagdata)
     self.assertIn(self.new_av, flagdata)
     self.assertEqual(len(flagdata), 2)
     self.assertDictEqual(flagdata[self.new_av], {})
     self.assertDictEqual(flagdata[self.old_av], flagdata[self.new_av])
Beispiel #39
0
    def get_context_data(self, lang, appver):
        # which pushes to show
        real_av, flags = (flags4appversions(locales={
            'id': lang.id
        },
                                            appversions={
                                                'id': appver.id
                                            }).get(appver, {}).get(
                                                lang.code, [None, {}]))
        actions = list(
            Action.objects.filter(id__in=flags.values()).select_related(
                'signoff__push__repository', 'author'))

        # get current status of signoffs
        push4action = dict((a.id, a.signoff.push) for a in actions)
        pending = push4action.get(flags.get(Action.PENDING))
        rejected = push4action.get(flags.get(Action.REJECTED))
        accepted = push4action.get(flags.get(Action.ACCEPTED))

        if real_av != appver.code and accepted is not None:
            # we're falling back, add the accepted push to the table
            fallback = accepted
        else:
            fallback = None

        pushes, suggested_signoff = self.annotated_pushes(
            actions,
            flags,
            fallback,
            lang,
            appver,
        )

        return {
            'appver': appver,
            'language': lang,
            'pushes': pushes,
            'pending': pending,
            'rejected': rejected,
            'accepted': accepted,
            'suggested_signoff': suggested_signoff,
            'login_form_needs_reload': True,
            'fallback': fallback,
            'real_av': real_av,
        }
Beispiel #40
0
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)
    # which pushes to show
    real_av, flags = (flags4appversions(
        locales={'id': lang.id},
        appversions={'id': appver.id})
                      .get(appver, {})
                      .get(lang.code, [None, {}]))
    actions = list(Action.objects.filter(id__in=flags.values())
                   .select_related('signoff__push__repository', 'author'))

    # get current status of signoffs
    push4action = dict((a.id, a.signoff.push)
        for a in actions)
    pending = push4action.get(flags.get(Action.PENDING))
    rejected = push4action.get(flags.get(Action.REJECTED))
    accepted = push4action.get(flags.get(Action.ACCEPTED))

    if real_av != appver.code and accepted is not None:
        # we're falling back, add the accepted push to the table
        fallback = accepted
    else:
        fallback = None
    pushes, suggested_signoff = annotated_pushes(appver, lang, actions, flags, fallback)

    return render(request, 'shipping/signoffs.html', {
                    'appver': appver,
                    'language': lang,
                    'pushes': pushes,
                    'pending': pending,
                    'rejected': rejected,
                    'accepted': accepted,
                    'suggested_signoff': suggested_signoff,
                    'login_form_needs_reload': True,
                    'fallback': fallback,
                    'real_av': real_av,
                  })
Beispiel #41
0
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)
    # which pushes to show
    real_av, flags = (flags4appversions(
        locales={'id': lang.id},
        appversions={'id': appver.id})
                      .get(appver, {})
                      .get(lang.code, [None, {}]))
    actions = list(Action.objects.filter(id__in=flags.values())
                   .select_related('signoff__push__repository', 'author'))

    # get current status of signoffs
    push4action = dict((a.id, a.signoff.push)
        for a in actions)
    pending = push4action.get(flags.get(Action.PENDING))
    rejected = push4action.get(flags.get(Action.REJECTED))
    accepted = push4action.get(flags.get(Action.ACCEPTED))

    if real_av != appver.code and accepted is not None:
        # we're falling back, add the accepted push to the table
        fallback = accepted
    else:
        fallback = None
    pushes, suggested_signoff = annotated_pushes(appver, lang, actions, flags, fallback)

    return render(request, 'shipping/signoffs.html', {
                    'appver': appver,
                    'language': lang,
                    'pushes': pushes,
                    'pending': pending,
                    'rejected': rejected,
                    'accepted': accepted,
                    'suggested_signoff': suggested_signoff,
                    'login_form_needs_reload': True,
                    'fallback': fallback,
                    'real_av': real_av,
                  })
Beispiel #42
0
    def get_context_data(self, lang, appver):
        # which pushes to show
        real_av, flags = (flags4appversions(
            locales={'id': lang.id},
            appversions={'id': appver.id})
                          .get(appver, {})
                          .get(lang.code, [None, {}]))
        actions = list(Action.objects.filter(id__in=flags.values())
                       .select_related('signoff__push__repository', 'author'))

        # get current status of signoffs
        push4action = dict((a.id, a.signoff.push)
            for a in actions)
        pending = push4action.get(flags.get(Action.PENDING))
        rejected = push4action.get(flags.get(Action.REJECTED))
        accepted = push4action.get(flags.get(Action.ACCEPTED))

        if real_av != appver.code and accepted is not None:
            # we're falling back, add the accepted push to the table
            fallback = accepted
        else:
            fallback = None

        pushes, suggested_signoff = self.annotated_pushes(
            actions,
            flags,
            fallback,
            lang,
            appver,
        )

        return {
            'appver': appver,
            'language': lang,
            'pushes': pushes,
            'pending': pending,
            'rejected': rejected,
            'accepted': accepted,
            'suggested_signoff': suggested_signoff,
            'login_form_needs_reload': True,
            'fallback': fallback,
            'real_av': real_av,
        }
Beispiel #43
0
 def testOneNew(self):
     """One accepted signoff on the new appversion, none on the old.
     Old appversion comes back empty.
     """
     locale = Locale.objects.get(code='da')
     repo = self._setup(locale, None, Action.ACCEPTED)
     eq_(repo.changesets.count(), 2)
     eq_(_actions4appversion(self.old_av, set([locale.id]), 100),
         ({}, set([locale.id])))
     a4av, not_found = _actions4appversion(self.new_av,
                                           set([locale.id]), 100)
     eq_(not_found, set())
     eq_(a4av.keys(), [locale.id])
     flag, action_id = a4av[locale.id].items()[0]
     eq_(flag, Action.ACCEPTED)
     eq_(Signoff.objects.get(action=action_id).locale_id, locale.id)
     flagdata = flags4appversions()
     ok_(self.old_av in flagdata)
     ok_(self.new_av in flagdata)
     eq_(len(flagdata), 2)
     eq_(flagdata[self.new_av], {'da':
         ['fx1.1', {Action.ACCEPTED: self.actions[1].id}]})
     eq_(flagdata[self.old_av], {})
Beispiel #44
0
    def test_signoff_annotated_pushes(self):
        view = SignoffView()
        locale = Locale.objects.get(code='de')

        real_av, flags = (api.flags4appversions(locales={
            'id': locale.id
        },
                                                appversions={
                                                    'id': self.av.id
                                                }).get(self.av, {}).get(
                                                    locale.code, [None, {}]))
        actions = list(
            Action.objects.filter(id__in=flags.values()).select_related(
                'signoff__push__repository', 'author'))
        fallback, = actions
        assert fallback.flag == Action.ACCEPTED, fallback.flag
        pushes, suggested_signoff = view.annotated_pushes(
            actions, flags, fallback, locale, self.av)
        eq_(suggested_signoff, None)
        changesets = [c for p in pushes for c in p['changes']]
        revisions = [x.revision for x in changesets]
        # only `de` changes in the right order
        eq_(revisions, [u'l10n de 0003', u'l10n de 0002'])
Beispiel #45
0
    def get_signoffs(self):
        avq = defaultdict(set)
        if self.avs:
            for appver in AppVersion.objects.filter(code__in=self.avs).values("id"):
                avq["id__in"].add(appver["id"])

        if self.trees:
            av_ids = (
                AppVersion.trees.through.objects.current()
                .filter(tree__code__in=self.trees)
                .values_list("appversion_id", flat=True)
            )
            avq["id__in"].update(av_ids)

        # restrict avq to building appversions open to signoffs
        currently_building = set(
            AppVersion.trees.through.objects.current()
            .filter(appversion__accepts_signoffs=True)
            .values_list("appversion_id", flat=True)
        )
        if avq:
            avq["id__in"] &= currently_building
        else:
            avq["id__in"] = currently_building

        apps = list(AppVersion.objects.filter(**avq).values_list("app", flat=True).distinct())
        if len(apps) == 1:
            given_app = Application.objects.get(id=apps[0]).code
        else:
            given_app = None
        if apps:
            appvers = AppVersion.objects.filter(app__in=apps)
        else:
            appvers = AppVersion.objects.all()
        lq = {}
        if self.locales:
            lq["code__in"] = self.locales

        locflags4av = flags4appversions(locales=lq, appversions=avq)
        tree_avs = AppVersion.trees.through.objects.current().filter(appversion__in=appvers)
        tree2av = dict(tree_avs.values_list("tree__code", "appversion__code"))
        av2tree = dict((av, tree) for tree, av in tree2av.iteritems())
        tree2app = dict(tree_avs.values_list("tree__code", "appversion__app__code"))
        values = dict(Action._meta.get_field("flag").flatchoices)
        so_items = {}
        for av in AppVersion.objects.filter(**avq):
            for loc, (real_av, flags) in locflags4av[av].iteritems():
                flag_values = [(real_av == av.code or f != Action.ACCEPTED) and values[f] or real_av for f in flags]
                so_items[(av2tree[av.code], loc)] = flag_values
        # get shipped-in data, latest milestone of all appversions for now
        shipped_in = defaultdict(list)
        for _av in appvers.select_related("app"):
            for _ms in _av.milestone_set.filter(status=2).order_by("-pk")[:1]:
                break
            else:
                continue
            app = _av.app.code
            _sos = _ms.signoffs
            if self.locales:
                _sos = _sos.filter(locale__code__in=self.locales)
            for loc in _sos.values_list("locale__code", flat=True):
                shipped_in[(app, loc)].append(_av.code)

        # make a list now
        items = [
            {
                "type": "SignOff",
                "label": "%s/%s" % (tree, locale),
                "tree": tree,
                "apploc": ("%s::%s" % (given_app or tree2app[tree], locale)),
                "signoff": sorted(values),
            }
            for (tree, locale), values in sorted(so_items.iteritems(), key=lambda t: t[0])
        ]
        items += [
            {"type": "Shippings", "label": "%s::%s" % (av, locale), "shipped": stones}
            for (av, locale), stones in shipped_in.iteritems()
        ]
        items += [{"type": "AppVer4Tree", "label": tree, "appversion": av} for tree, av in tree2av.iteritems()]
        return items
Beispiel #46
0
    def get_signoffs(self):
        avq = defaultdict(set)
        if self.avs:
            for appver in (AppVersion.objects.filter(
                    code__in=self.avs).values('id')):
                avq['id__in'].add(appver['id'])

        if self.trees:
            av_ids = (AppVersion.trees.through.objects.current().filter(
                tree__code__in=self.trees).values_list('appversion_id',
                                                       flat=True))
            avq['id__in'].update(av_ids)

        # restrict avq to building appversions open to signoffs
        currently_building = set(
            AppVersion.trees.through.objects.current().filter(
                appversion__accepts_signoffs=True).values_list('appversion_id',
                                                               flat=True))
        if avq:
            avq['id__in'] &= currently_building
        else:
            avq['id__in'] = currently_building

        apps = list(
            AppVersion.objects.filter(**avq).values_list('app',
                                                         flat=True).distinct())
        if len(apps) == 1:
            given_app = Application.objects.get(id=apps[0]).code
        else:
            given_app = None
        if apps:
            appvers = AppVersion.objects.filter(app__in=apps)
        else:
            appvers = AppVersion.objects.all()
        lq = {}
        if self.locales:
            lq['code__in'] = self.locales

        locflags4av = flags4appversions(locales=lq, appversions=avq)
        tree_avs = (AppVersion.trees.through.objects.current().filter(
            appversion__in=appvers))
        tree2av = dict(tree_avs.values_list("tree__code", "appversion__code"))
        av2tree = dict((av, tree) for tree, av in tree2av.iteritems())
        tree2app = dict(
            tree_avs.values_list("tree__code", "appversion__app__code"))
        values = dict(Action._meta.get_field('flag').flatchoices)
        so_items = {}
        for av in AppVersion.objects.filter(**avq):
            for loc, (real_av, flags) in locflags4av[av].iteritems():
                flag_values = [
                    (real_av == av.code or f != Action.ACCEPTED) and values[f]
                    or real_av for f in flags
                ]
                so_items[(av2tree[av.code], loc)] = flag_values
        # get shipped-in data, latest milestone of all appversions for now
        shipped_in = defaultdict(list)
        for _av in appvers.select_related('app'):
            for _ms in _av.milestone_set.filter(status=2).order_by('-pk')[:1]:
                break
            else:
                continue
            app = _av.app.code
            _sos = _ms.signoffs
            if self.locales:
                _sos = _sos.filter(locale__code__in=self.locales)
            for loc in _sos.values_list('locale__code', flat=True):
                shipped_in[(app, loc)].append(_av.code)

        # make a list now
        items = [{
            "type": "SignOff",
            "label": "%s/%s" % (tree, locale),
            "tree": tree,
            "apploc": ("%s::%s" % (given_app or tree2app[tree], locale)),
            "signoff": sorted(values)
        } for (
            tree,
            locale), values in sorted(so_items.iteritems(), key=lambda t: t[0])
                 ]
        items += [{
            "type": "Shippings",
            "label": "%s::%s" % (av, locale),
            "shipped": stones
        } for (av, locale), stones in shipped_in.iteritems()]
        items += [{
            "type": "AppVer4Tree",
            "label": tree,
            "appversion": av
        } for tree, av in tree2av.iteritems()]
        return items
Beispiel #47
0
    def get_context_data(self, lang, appver):
        # which pushes to show
        real_av, flags = (flags4appversions([appver],
            locales=[lang.id])
                          .get(appver, {})
                          .get(lang.code, [None, {}]))
        actions = list(Action.objects.filter(id__in=flags.values())
                       .select_related('signoff__push__repository', 'author'))

        # get current status of signoffs
        push4action = dict((a.id, a.signoff.push)
            for a in actions)
        pending = push4action.get(flags.get(Action.PENDING))
        rejected = push4action.get(flags.get(Action.REJECTED))
        accepted = push4action.get(flags.get(Action.ACCEPTED))

        if real_av != appver.code and accepted is not None:
            # we're falling back, add the accepted push to the table
            fallback = accepted
        else:
            fallback = None

        pushes_data = self.annotated_pushes(
            lang,
            appver,
            actions=actions,
            flags=flags,
            fallback=fallback,
            count=self.count,
        )

        # Check if this is the very first release.
        # Only applies to products that have a fallback (Firefox for example).
        first = appver.fallback is not None and accepted is None

        try:
            team_locale = (
                TeamLocaleThrough.objects.current().get(locale=lang).team
            )
        except TeamLocaleThrough.DoesNotExist:
            team_locale = lang

        if pushes_data['next_push_date']:
            next_push_date = pushes_data['next_push_date'].isoformat()
        else:
            next_push_date = None

        return {
            'appver': appver,
            'language': lang,
            'team_locale': team_locale,
            'pushes': pushes_data['pushes'],
            'pushes_left': pushes_data['pushes_left'],
            'next_push_date': next_push_date,
            'pending': pending,
            'rejected': rejected,
            'accepted': accepted,
            'first': first,
            'suggested_signoff': pushes_data['suggested_signoff'],
            'login_form_needs_reload': True,
            'fallback': fallback,
            'real_av': real_av,
        }
Beispiel #48
0
    def get_signoffs(self):
        avq = defaultdict(set)
        if self.avs:
            for appver in (AppVersion.objects
                           .filter(code__in=self.avs).values('id')):
                avq['id__in'].add(appver['id'])

        if self.trees:
            av_ids = (AppVersion.trees.through.objects
                      .current()
                .filter(tree__code__in=self.trees)
                .values_list('appversion_id', flat=True))
            avq['id__in'].update(av_ids)

        # restrict avq to building appversions open to signoffs
        currently_building = set(AppVersion.trees.through.objects
                                 .current()
                                 .filter(appversion__accepts_signoffs=True)
                                 .values_list('appversion_id', flat=True))
        if avq:
            avq['id__in'] &= currently_building
        else:
            avq['id__in'] = currently_building

        apps = list(AppVersion.objects
                    .filter(**avq)
                    .values_list('app', flat=True)
                    .distinct())
        if len(apps) == 1:
            given_app = Application.objects.get(id=apps[0]).code
        else:
            given_app = None
        if apps:
            appvers = AppVersion.objects.filter(app__in=apps)
        else:
            appvers = AppVersion.objects.all()
        lq = {}
        if self.locales:
            lq['code__in'] = self.locales

        locflags4av = flags4appversions(locales=lq, appversions=avq)
        tree_avs = (AppVersion.trees.through.objects
                    .current()
                    .filter(appversion__in=appvers))
        tree2av = dict(tree_avs.values_list("tree__code", "appversion__code"))
        av2tree = dict((av, tree) for tree, av in tree2av.iteritems())
        tree2app = dict(tree_avs.values_list("tree__code",
                                             "appversion__app__code"))
        values = dict(Action._meta.get_field('flag').flatchoices)
        so_items = {}
        for av in AppVersion.objects.filter(**avq):
            for loc, (real_av, flags) in locflags4av[av].iteritems():
                flag_values = [
                    (real_av == av.code or f != Action.ACCEPTED) and values[f]
                    or real_av
                    for f in flags]
                so_items[(av2tree[av.code], loc)] = flag_values

        # make a list now
        items = [{"type": "SignOff",
                  "label": "%s/%s" % (tree, locale),
                  "tree": tree,
                  "apploc": ("%s::%s" % (given_app or tree2app[tree],
                                         locale)),
                  "signoff": sorted(values)}
                 for (tree, locale), values in sorted(so_items.iteritems(),
                                                      key=lambda t:t[0])]
        items += [{"type": "AppVer4Tree",
                   "label": tree,
                   "appversion": av}
                  for tree, av in tree2av.iteritems()]
        return items
    def get_signoffs(self):
        appversions_with_pushes = []
        _treeid_to_avt = {}
        tree_ids = {}
        avts = (AppVersion.trees.through.objects.current().filter(
            appversion__accepts_signoffs=True))
        if self.avs:
            avts = avts.filter(appversion__code__in=self.avs)
        for avt in avts.select_related('appversion__app', 'tree__l10n'):
            _treeid_to_avt[avt.tree.id] = avt
            tree_ids[avt.tree.code] = avt.tree.id

        appvers = [avt.appversion for avt in _treeid_to_avt.values()]
        locales = None
        if self.locales:
            locales = list(
                Locale.objects.filter(code__in=self.locales).values_list(
                    'id', flat=True))

        locflags4av = flags4appversions(appvers, locales=locales)
        av2tree = {
            avt.appversion.code: avt.tree.code
            for avt in six.itervalues(_treeid_to_avt)
        }
        values = dict(Action._meta.get_field('flag').flatchoices)
        so_items = {}
        actions = {}
        for av in appvers:
            for loc, (real_av, flags) in six.iteritems(locflags4av[av]):
                flag_values = [
                    (real_av == av.code or f != Action.ACCEPTED) and values[f]
                    or real_av for f in flags
                ]
                item = {'flags': flag_values, 'state': None, 'action': []}
                if Action.ACCEPTED in flags:
                    item['state'] = (real_av == av.code) and 'OK' or real_av
                if Action.REJECTED in flags:
                    item['action'].append('rejected')
                if Action.PENDING in flags:
                    item['action'].append('review')
                so_items[(av2tree[av.code], loc)] = item
                for flag, action in flags.items():
                    # don't keep track of fallback actions
                    if not (real_av != av.code and flag == Action.ACCEPTED):
                        actions[action] = [av, loc]
        for action, signoff, push_date in (Action.objects.filter(
                id__in=actions.keys()).values_list(
                    'id', 'signoff', 'signoff__push__push_date')):
            actions[action] += [signoff, push_date]
        last_action = {}
        last_signoff = {}
        for action, (av, loc, signoff,
                     push_date) in sorted(six.iteritems(actions),
                                          key=lambda t: t[1][3],
                                          reverse=True):
            if (av, loc) in last_action:
                continue
            last_action[(av, loc)] = push_date
            last_signoff[(av, loc)] = signoff

        runs_with_open_av = [
            run for run, tree in self.runids2tree.items() if tree in tree_ids
            and _treeid_to_avt[tree_ids[tree]].appversion.accepts_signoffs
        ]
        changesets = {
            tuple(t[:2]): t[2]
            for t in Run.revisions.through.objects.filter(
                run__in=runs_with_open_av,
                changeset__repositories__locale__isnull=False).values_list(
                    'run__tree', 'run__locale__code', 'changeset')
        }
        pushdates = {
            tuple(t[:2]): t[2]
            for t in Push.changesets.through.objects.filter(
                changeset__in=set(changesets.values())).values_list(
                    'changeset', 'push__repository__forest', 'push__push_date')
        }
        avl_has_new_run = set()
        maybe_new_run = []
        for (treeid, locale_code), changeset in six.iteritems(changesets):
            avt = _treeid_to_avt[treeid]
            push_date = pushdates[(changeset, avt.tree.l10n.id)]
            if avt.start and push_date < avt.start:
                # need update
                appversions_with_pushes.append({
                    "needs_update":
                    True,
                    "type":
                    "NewPush",
                    "label":
                    avt.tree.code + '/' + locale_code
                })
                continue
            if avt.end and push_date > avt.end:
                continue
            if ((avt.appversion, locale_code) in last_action and
                    last_action[(avt.appversion, locale_code)] >= push_date):
                continue
            maybe_new_run.append(
                (avt.appversion, changeset, avt.tree.code, locale_code,
                 last_signoff.get((avt.appversion, locale_code))))

        # find out if the sign-off is on the same revision as the last push
        old_signoffs = [t[4] for t in maybe_new_run if t[4] is not None]
        signoffs_to_skip = set()
        if old_signoffs:
            so_tips = dict(
                Signoff.objects.filter(id__in=old_signoffs).annotate(
                    cs=Max("push__changesets")).values_list('id', 'cs'))
            latest_changeset = dict((t[4], t[1]) for t in maybe_new_run)
            for so_id, tip in six.iteritems(so_tips):
                if latest_changeset[so_id] == tip:
                    signoffs_to_skip.add(so_id)
        for av, changeset, tree_code, locale_code, last_so in maybe_new_run:
            if last_so in signoffs_to_skip:
                continue
            avl_has_new_run.add((tree_code, locale_code))
            appversions_with_pushes.append({
                "new_run":
                "sign off",
                "type":
                "NewPush",
                "label":
                tree_code + '/' + locale_code
            })

        # make a list now
        items = []
        for (tree, locale), values in sorted(six.iteritems(so_items),
                                             key=lambda t: t[0]):
            glyph = ''
            if values['state'] == 'OK':
                glyph = 'check'
                if (tree, locale) in avl_has_new_run:
                    glyph = 'graph'
            elif values['state']:
                glyph = 'warning'
            item = {
                "type": "SignOff",
                "label": "%s/%s" % (tree, locale),
                "tree": tree,
                "state": values['state'],
                "state_glyph": glyph,
                "signoff": sorted(values['flags'])
            }
            if values['action']:
                item['action'] = values['action']
            items.append(item)

        items += [{
            "type": "AppVer4Tree",
            "label": tree,
            "appversion": av
        } for av, tree in six.iteritems(av2tree)]
        items += appversions_with_pushes
        return items
Beispiel #50
0
def teamsnippet(loc, team_locales):
    locs = Locale.objects.filter(pk__in=[loc.pk] + list(team_locales))
    runs = sorted(
        (Run.objects
         .filter(locale__in=locs, active__isnull=False)
         .select_related('tree', 'locale')),
        key=lambda r: (r.tree.code,
                       '' if r.locale.code == loc.code else r.locale.code)
    )

    # this locale isn't active in our trees yet
    if not runs:
        return ''

    # 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 = {}
    appversion_has_pushes = {}
    for avt in (AppVersion.trees.through.objects
                .current()
                .select_related('appversion__app', 'tree__l10n')):
        _trees_to_appversions[avt.tree] = avt.appversion
        # find out if the appversion has activity (if open for sign-offs)
        if not avt.appversion.accepts_signoffs:
            continue
        pushes = Push.objects.filter(repository__forest=avt.tree.l10n)
        pushes = pushes.filter(repository__locale__in=locs)
        if avt.start:
            pushes = pushes.filter(push_date__gt=avt.start)
        if avt.end:
            pushes = pushes.filter(push_date__lt=avt.end)
        locales_with_runs = (Run.revisions.through.objects
            .filter(run__tree=avt.tree, changeset__pushes__in=pushes)
            .values_list('run__locale', flat=True))
        for locale_id in locales_with_runs:
            appversion_has_pushes[(avt.appversion, locale_id)] = True

    def tree_to_appversion(tree):
        return _trees_to_appversions.get(tree)

    def tree_to_application(tree):
        av = tree_to_appversion(tree)
        return av and av.app or None

    # offer all revisions to sign-off.
    # in api.annotated_pushes, we only highlight the latest run if it's green
    suggested_runs = runs

    suggested_rev = dict(Run_Revisions.objects
                         .filter(run__in=suggested_runs,
                                 changeset__repositories__locale__in=locs)
                         .values_list('run_id', 'changeset__revision'))

    progresses = dict(
        ((pp.tree.code, pp.locale.code), pp) for pp in (
            ProgressPosition.objects
            .filter(locale__in=locs)
            .select_related('tree', 'locale')
            )
        )
    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 = RunElement(dict((k, getattr(run_, k))
                       for k in run_.__dict__
                       if not k.startswith('_')))
        run.locale = run_.locale
        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
        run.prog_pos = progresses.get((run.tree.code, run.locale.code))

        appversion = tree_to_appversion(run.tree)
        # because Django templates (stupidly) swallows lookup errors we
        # have to apply the missing defaults too
        defaults = (
            "pending", "actions", "accepted", "suggested_shortrev",
            "is_active", "under_review", "suggest_glyph", "suggest_class",
            "fallback")
        for attr in defaults:
            setattr(run, attr, None)
        run.appversion = appversion
        if appversion and appversion.accepts_signoffs:
            run.is_active = \
                appversion_has_pushes.get((appversion, run.locale_id))
            real_av, flags = (
                flags4appversions(
                    locales={'id': run_.locale.id},
                    appversions={'id': appversion.id}
                )
                .get(appversion, {})
                .get(run_.locale.code, [None, {}])
            )

            # get current status of signoffs
            # we really only need the shortrevs, we'll get those below
            if flags:
                interesting_flags = (Action.PENDING, Action.ACCEPTED,
                                     Action.REJECTED)
                actions = list(Action.objects
                    .filter(id__in=flags.values(),
                            flag__in=interesting_flags)
                    .select_related('signoff__push')
                    .order_by('when'))
                # only keep a rejected sign-off it's the last
                if (Action.REJECTED in flags and
                    actions[-1].flag != Action.REJECTED):
                    actions = filter(lambda a: a.flag != Action.REJECTED,
                                     actions)
                pushes.update(a.signoff.push for a in actions)
                objects = [RunElement(
                    dict((k, getattr(a, k))
                          for k in a.__dict__
                          if not k.startswith('_')))
                    for a in actions]
                for a, obj in zip(actions, objects):
                    obj.signoff = a.signoff
                    obj.push = a.signoff.push
                    obj.flag_name = a.get_flag_display()
                run.actions = objects
                if Action.ACCEPTED in flags:
                    run.accepted = [obj for obj in objects
                        if obj.flag == Action.ACCEPTED][0]
                    objects.remove(run.accepted)
            if appversion.code != real_av:
                run.fallback = real_av

            # 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]
                if run.errors:
                    run.suggest_glyph = 'bolt'
                    run.suggest_class = 'error'
                elif run.allmissing:
                    run.suggest_glyph = 'graph'
                    run.suggest_class = 'warning'
                else:
                    run.suggest_glyph = 'badge'
                    run.suggest_class = 'success'

        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:
            actions = [run.accepted] if run.accepted else []
            if run.actions:
                actions += run.actions
            for action in actions:
                action.rev = rev4id[tip4push[action.push.id]][:12]
                # unset the suggestion if there's existing signoff action
                if action.rev == run.suggested_shortrev:
                    run.suggested_shortrev = None
                    # if we have a pending sign-off as the last thing,
                    # let's say so
                    if action.flag == Action.PENDING:
                        run.under_review = True
    applications = sorted(
        ((k, v) for (k, v) in applications.items()),
        key=lambda t: t[0] and t[0].name or None
    )
    other_team_locales = Locale.objects.filter(id__in=team_locales)

    progress_start = datetime.utcnow() - timedelta(days=settings.PROGRESS_DAYS)

    return render_to_string('shipping/team-snippet.html',
                            {'locale': loc,
                             'other_team_locales': other_team_locales,
                             'applications': applications,
                             'progress_start': progress_start,
                            })
Beispiel #51
0
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 avt in (AppVersion.trees.through.objects.current().select_related(
            'appversion', 'tree')):
        _trees_to_appversions[avt.tree] = avt.appversion

    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.fallback = run.appversion = None
        if appversion and appversion.accepts_signoffs:
            run.appversion = appversion
            real_av, flags = (flags4appversions(locales={
                'id': loc.id
            },
                                                appversions={
                                                    'id': appversion.id
                                                }).get(appversion, {}).get(
                                                    loc.code, [None, {}]))
            # get current status of signoffs
            # we really only need the shortrevs, we'll get those below
            if flags:
                actions = Action.objects.filter(id__in=flags.values()) \
                                        .select_related('signoff__push')
                action4id = dict((a.id, a) for a in actions)
                for flag in (Action.PENDING, Action.ACCEPTED, Action.REJECTED):
                    if flag in flags:
                        a = action4id[flags[flag]]
                        setattr(run, a.get_flag_display(), a.signoff.push)
            if appversion.code != real_av:
                run.fallback = real_av
            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,
    })
Beispiel #52
0
def teamsnippet(loc, team_locales):
    locs = Locale.objects.filter(pk__in=[loc.pk] + list(team_locales))
    runs = list((Run.objects.filter(locale__in=locs,
                                    active__isnull=False).select_related(
                                        'tree', 'locale')))

    # this locale isn't active in our trees yet
    if not runs:
        return {
            'template': 'shipping/team-snippet.html',
            'context': {
                'locale': loc,
                'applications': [],
            }
        }

    # 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
    appversion_has_pushes = {}
    _treeid_to_avt = {}
    for avt in (AppVersion.trees.through.objects.current().filter(tree__in=set(
            run.tree.id
            for run in runs)).select_related('appversion__app', 'tree__l10n')):
        _treeid_to_avt[avt.tree.id] = avt

    runs_with_open_av = [
        run for run in runs if run.tree.id in _treeid_to_avt
        and _treeid_to_avt[run.tree.id].appversion.accepts_signoffs
    ]
    changesets = {
        tuple(t[:2]): t[2]
        for t in Run.revisions.through.objects.filter(
            run__in=runs_with_open_av,
            changeset__repositories__locale__in=locs).values_list(
                'run__tree', 'run__locale', 'changeset')
    }
    pushdates = {
        tuple(t[:2]): t[2]
        for t in Push.changesets.through.objects.filter(
            changeset__in=set(changesets.values())).values_list(
                'changeset', 'push__repository__forest', 'push__push_date')
    }
    for (treeid, locale_id), changeset in six.iteritems(changesets):
        avt = _treeid_to_avt[treeid]
        push_date = pushdates[(changeset, avt.tree.l10n.id)]
        if avt.start and push_date < avt.start:
            continue
        if avt.end and push_date > avt.end:
            continue
        appversion_has_pushes[(avt.appversion, locale_id)] = True

    def tree_to_appversion(tree):
        avt = tree.id in _treeid_to_avt and _treeid_to_avt[tree.id]
        return avt and avt.appversion or None

    def tree_to_version(tree):
        av = tree_to_appversion(tree)
        return av and av.version or None

    def tree_to_application(tree):
        av = tree_to_appversion(tree)
        return av and av.app or None

    runs.sort(key=lambda r: (tree_to_version(r.tree), r.tree.code, ''
                             if r.locale.code == loc.code else r.locale.code))

    # offer all revisions to sign-off.
    # in api.annotated_pushes, we only highlight the latest run if it's green
    suggested_runs = runs_with_open_av

    suggested_rev = dict(
        Run.revisions.through.objects.filter(
            run__in=suggested_runs,
            changeset__repositories__locale__in=locs).values_list(
                'run_id', 'changeset__revision'))

    applications = defaultdict(list)
    pushes = set()

    flags4av = flags4appversions(
        [_treeid_to_avt[run.tree.id].appversion for run in runs_with_open_av],
        locales=list(set(run.locale.id for run in runs_with_open_av)),
    )
    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 = RunElement({
            k: getattr(run_, k)
            for k in run_.__dict__ if not k.startswith('_')
        })
        run.locale = run_.locale
        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
        defaults = ("actions", "accepted", "suggested_shortrev", "is_active",
                    "under_review", "suggest_glyph", "suggest_class",
                    "fallback")
        for attr in defaults:
            setattr(run, attr, None)
        run.appversion = appversion
        run.is_active = \
            appversion_has_pushes.get((appversion, run.locale_id))
        applications[application].append(run)
        if appversion and appversion in flags4av:
            real_av, flags = (flags4av[appversion].get(run_.locale.code,
                                                       [None, {}]))

            # get current status of signoffs
            # we really only need the shortrevs, we'll get those below
            if flags:
                interesting_flags = (Action.PENDING, Action.ACCEPTED,
                                     Action.REJECTED)
                actions = list(
                    Action.objects.filter(
                        id__in=flags.values(),
                        flag__in=interesting_flags).select_related(
                            'signoff__push').order_by('when', 'pk'))
                # only keep a rejected sign-off it's the last
                if (Action.REJECTED in flags
                        and actions[-1].flag != Action.REJECTED):
                    actions = [a for a in actions if a.flag != Action.REJECTED]
                pushes.update(a.signoff.push for a in actions)
                objects = [
                    RunElement({
                        k: getattr(a, k)
                        for k in a.__dict__ if not k.startswith('_')
                    }) for a in actions
                ]
                for a, obj in zip(actions, objects):
                    obj.signoff = a.signoff
                    obj.push = a.signoff.push
                    obj.flag_name = a.get_flag_display()
                run.actions = objects
                if Action.ACCEPTED in flags:
                    run.accepted = [
                        obj for obj in objects if obj.flag == Action.ACCEPTED
                    ][0]
                    objects.remove(run.accepted)
            if appversion.code != real_av:
                run.fallback = real_av

            # 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 and run.is_active:
                run.suggested_shortrev = suggested_rev[run_.id][:12]
                if run.errors:
                    run.suggest_glyph = 'bolt'
                    run.suggest_class = 'error'
                elif run.allmissing:
                    run.suggest_glyph = 'graph'
                    run.suggest_class = 'warning'
                else:
                    run.suggest_glyph = 'badge'
                    run.suggest_class = 'success'

    # get the tip shortrevs for all our pushes
    pushes = [p.id for p in pushes if p is not None]
    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 six.itervalues(applications):
        for run in runs:
            actions = [run.accepted] if run.accepted else []
            if run.actions:
                actions += run.actions
            for action in actions:
                action.rev = rev4id[tip4push[action.push.id]][:12]
                # unset the suggestion if there's existing signoff action
                if action.rev == run.suggested_shortrev:
                    run.suggested_shortrev = None
                    run.suggest_glyph = run.suggest_class = None
                    # if we have a pending sign-off as the last thing,
                    # let's say so
                    if action.flag == Action.PENDING:
                        run.under_review = True
    # Sort by appname, make trees without app come last via 'ZZZZZZ'
    applications = sorted(((k, v) for (k, v) in applications.items()),
                          key=lambda t: t[0] and t[0].name or 'ZZZZZZ')
    other_team_locales = Locale.objects.filter(id__in=team_locales)

    progress_start = datetime.utcnow() - timedelta(days=settings.PROGRESS_DAYS)

    return {
        'template': 'shipping/team-snippet.html',
        'context': {
            'locale': loc,
            'other_team_locales': other_team_locales,
            'applications': applications,
            'progress_start': progress_start,
        }
    }
Beispiel #53
0
def data(request):
    app_codes = request.GET.getlist('app')
    if not app_codes:
        raise Http404('List of applications required')
    beta_apps = Application.objects.filter(code__in=app_codes)
    if len(app_codes) != beta_apps.count():
        raise Http404('Some of the given apps were not found')
    branches = request.GET.getlist('branch')

    f_aurora = Forest.objects.get(name='releases/l10n/mozilla-aurora')
    f_beta = Forest.objects.get(name='releases/l10n/mozilla-beta')
    forests = []
    if 'aurora' in branches:
        forests.append(f_aurora)
    if 'beta' in branches:
        forests.append(f_beta)
    name4app = dict(beta_apps.values_list('id', 'name'))
    # get AppVersion_Trees
    avts = (AppVersionTreeThrough.objects.current().filter(
        appversion__app__in=name4app.keys(),
        tree__l10n__in=forests).order_by('appversion__code').select_related(
            'appversion__app', 'tree'))
    avts = list(avts)
    code4av = dict((avt.appversion_id, avt.appversion.code) for avt in avts)
    appname4av = dict(
        (avt.appversion_id, name4app[avt.appversion.app_id]) for avt in avts)
    tree4av = dict((avt.appversion_id, avt.tree_id) for avt in avts)
    av4tree = dict((avt.tree_id, avt.appversion_id) for avt in avts)
    locs = set()
    loc4tree = defaultdict(list)
    for t, l in (Run.objects.filter(tree__in=tree4av.values(),
                                    active__isnull=False).values_list(
                                        'tree', 'locale__code').distinct()):
        loc4tree[t].append(l)
        locs.add(l)
    locflags4av = flags4appversions([avt.appversion for avt in avts])
    loc4tree = defaultdict(list)
    for av, flags4loc in locflags4av.iteritems():
        if av.id not in tree4av:
            continue
        for loc, (real_av, flags) in flags4loc.iteritems():
            if real_av == av.code:
                continue
            loc4tree[tree4av[av.id]].append(loc)
    loc4tree = dict((t, locs) for t, locs in loc4tree.iteritems() if locs)
    or_ = lambda l, r: l | r
    runqueries = reduce(or_, (reduce(or_, (Q(tree=t, locale__code=l)
                                           for l in locs))
                              for t, locs in loc4tree.iteritems()))
    actives = (Run.objects.filter(runqueries).exclude(
        active__isnull=True).select_related('locale'))
    missings = dict(
        ((r.tree_id, r.locale.code), r.allmissing) for r in actives)
    matrix = dict()
    columns = tuple(avt.appversion_id for avt in avts)
    for tree_id, locs in loc4tree.iteritems():
        av_id = av4tree[tree_id]
        for loc in locs:
            if loc not in matrix:
                matrix[loc] = [None] * len(avts)
            col_index = columns.index(av_id)
            entry = {
                'av': code4av[av_id],
                'app': appname4av[av_id],
                'missing': missings[(tree4av[av_id], loc)]
            }
            matrix[loc][col_index] = entry

    rows = [{
        'loc': loc,
        'entries': matrix[loc]
    } for loc in sorted(matrix.keys())]

    return render(
        request, 'shipping/out-data.html', {
            'apps': beta_apps,
            'appvers': [avt.appversion for avt in avts],
            'rows': rows,
        })
Beispiel #54
0
def changes(request, app_code):
    """Show which milestones on the given appversion took changes for which
    locale
    """
    av = get_object_or_404(AppVersion, code=app_code)
    ms_names = {}
    ms_codes = {}
    for ms in (Milestone.objects
               .filter(appver=av)
               .select_related('appver__app')):
        ms_names[ms.id] = str(ms)
        ms_codes[ms.id] = ms.code
    rows = []
    changes = None
    ms_id = None
    latest = {}
    current = {}
    ms_name = None
    # get historic data that enters this appversion
    # get that in fallback order, we'll reverse afterwards
    flags4av = flags4appversions([av])
    flags4loc = flags4av[av]
    locs4av = defaultdict(dict)  # av -> loc -> ACCEPTED
    notaccepted = {}  # av -> flags
    for loc, (real_av, flags) in flags4loc.iteritems():
        if Action.ACCEPTED in flags:
            locs4av[real_av][loc] = flags[Action.ACCEPTED]
        else:
            notaccepted[loc] = flags
    # for not accepted locales on the current appver,
    # check for accepted on fallbacks
    if av.fallback and notaccepted:
        flags4fallback = flags4av[av.fallback]
        for loc in notaccepted:
            if loc in flags4fallback:
                # if the loc isn't here, it's never been accepted so far
                real_av, flags = flags4fallback[loc]
                if Action.ACCEPTED in flags:
                    locs4av[real_av] = flags[Action.ACCEPTED]
    # let's keep the current appver data around for later,
    # and order the fallbacks.
    # Also, keep track of how many locales fell back to
    #  * the previous cycle
    #  * the two cycles before that (2 and 3)
    #  * older cycles (4 and more)
    accepted = locs4av.pop(av.code, {})
    av_ = av
    fallback = 0  # how deep are we falling back
    buckets = {0: 0, 1: 1, 2: 2, 3: 2}  # which fallback to which bucket
    bucket = 0  # bucket we're in
    rowspan = 0  # how many rows are in this bucket
    locales_group = set()  # which locales are in this bucket
    while av_ and locs4av:
        thisbucket = buckets.get(fallback, 3)
        if thisbucket != bucket and locales_group:
            rows[-1].update({
                'rowspan': rowspan,
                'group_locales_count': len(locales_group)
                })
            locales_group.clear()
            rowspan = 0
        bucket = thisbucket
        if av_.code in locs4av:
            accepted4loc = locs4av.pop(av_.code)
            # store actions for now, we'll get the push_ids later
            current.update(accepted4loc)
            locales_group.update(accepted4loc.keys())
            rowspan += 1
            rows.append({
                'name': str(av_),
                'code': av_.code,
                'isAppVersion': True,
                'changes': [(loc, 'added') for loc in sorted(accepted4loc)]
                })
        av_ = av_.fallback
        fallback += 1
    if locales_group and rows:
        rows[-1].update({
            'rowspan': rowspan,
            'group_locales_count': len(locales_group)
            })
    rows.reverse()
    for loc, pid in (Signoff.objects
                     .filter(action__in=current.values())
                     .values_list('locale__code',
                                  'push__id')):
        current[loc] = pid
    for loc, pid in (Signoff.objects
                     .filter(action__in=accepted.values())
                     .values_list('locale__code',
                                  'push__id')):
        accepted[loc] = pid
    # reset group data for the current appversion, all one group
    avrow = None  # keep track of the first row
    rowspan = 0
    locales_group.clear()
    current = {}
    # we're only doing sign-offs for this appversion now, and for milestones
    # of this appversion
    for _mid, loc, pid in (Milestone_Signoffs.objects
                           .filter(milestone__appver=av)
                           .filter(signoff__appversion=av)
                           .order_by('milestone__id',
                                     'signoff__locale__code')
                           .values_list('milestone__id',
                                        'signoff__locale__code',
                                        'signoff__push__id')):
        if _mid != ms_id:
            ms_id = _mid
            # next milestone, bootstrap new row
            if latest:
                # previous milestone has locales left, update previous changes
                changes += [(_loc, 'dropped') for _loc in latest.iterkeys()]
                changes.sort()
            latest = current
            current = {}
            ms_name = ms_names[ms_id]
            changes = []
            rows.append({'name': ms_name,
                         'code': ms_codes[ms_id],
                         'changes': changes})
            rowspan += 1
            if avrow is None:
                avrow = rows[-1]
        if loc not in latest:
            changes.append((loc, 'added'))
            locales_group.add(loc)
        else:
            lpid = latest.pop(loc)
            if lpid != pid:
                changes.append((loc, 'changed'))
        current[loc] = pid

    # see if we have some locales dropped in the last milestone
    if latest:
        # previous milestone has locales left, update previous changes
        changes += [(loc, 'dropped') for loc in latest.iterkeys()]
        changes.sort()
    # add group info to the avrow
    if avrow:
        avrow.update({
            'rowspan': rowspan,
            'rowspan_last': True,
            'group_locales_count': len(locales_group)
            })
    # finally, check if there's more signoffs after the last shipped milestone
    newso = [(loc, loc in current and 'changed' or 'added')
        for loc, pid in accepted.iteritems()
        if current.get(loc) != pid]
    for loc, flags in notaccepted.iteritems():
        if Action.PENDING in flags:
            newso.append((loc, 'pending'))
        elif Action.REJECTED in flags:
            newso.append((loc, 'rejected'))
        elif Action.OBSOLETED in flags:
            newso.append((loc, 'obsoleted'))
    if newso:
        newso.sort()
        rows.append({
            'name': '%s .next' % str(av),
            'changes': newso
        })
        addcount = len([t for t in newso if t[1]=='added'])
        if addcount:
            rows[-1].update({
                'rowspan': 1,
                'rowspan_last': True,
                'group_locales_count': '+%d' % addcount
            })

    return render(request, 'shipping/app-changes.html', {
                    'appver': av,
                    'rows': rows,
                  })
Beispiel #55
0
def changes(request, app_code):
    """Show which milestones on the given appversion took changes for which
    locale
    """
    av = get_object_or_404(AppVersion, code=app_code)
    ms_names = {}
    ms_codes = {}
    for ms in (Milestone.objects
               .filter(appver=av)
               .select_related('appver__app')):
        ms_names[ms.id] = str(ms)
        ms_codes[ms.id] = ms.code
    rows = []
    changes = None
    ms_id = None
    latest = {}
    current = {}
    ms_name = None
    # get historic data that enters this appversion
    # get that in fallback order, we'll reverse afterwards
    flags4av = flags4appversions(appversions={"id": av.id})
    flags4loc = flags4av[av]
    locs4av = defaultdict(dict)  # av -> loc -> ACCEPTED
    notaccepted = {}  # av -> flags
    for loc, (real_av, flags) in flags4loc.iteritems():
        if Action.ACCEPTED in flags:
            locs4av[real_av][loc] = flags[Action.ACCEPTED]
        else:
            notaccepted[loc] = flags
    # for not accepted locales on the current appver,
    # check for accepted on fallbacks
    if av.fallback and notaccepted:
        flags4fallback = flags4av[av.fallback]
        for loc in notaccepted:
            if loc in flags4fallback:
                # if the loc isn't here, it's never been accepted so far
                real_av, flags = flags4fallback[loc]
                if Action.ACCEPTED in flags:
                    locs4av[real_av] = flags[Action.ACCEPTED]
    # let's keep the current appver data around for later,
    # and order the fallbacks
    accepted = locs4av.pop(av.code, {})
    av_ = av
    while av_ and locs4av:
        if av_.code in locs4av:
            accepted4loc = locs4av.pop(av_.code)
            # store actions for now, we'll get the push_ids later
            current.update(accepted4loc)
            rows.append({
                'name': str(av_),
                'code': av_.code,
                'isAppVersion': True,
                'changes': [(loc, 'added') for loc in sorted(accepted4loc)]
                })
        av_ = av_.fallback
    rows.reverse()
    for loc, pid in (Signoff.objects
                     .filter(action__in=current.values())
                     .values_list('locale__code',
                                  'push__id')):
        current[loc] = pid
    for loc, pid in (Signoff.objects
                     .filter(action__in=accepted.values())
                     .values_list('locale__code',
                                  'push__id')):
        accepted[loc] = pid
    for _mid, loc, pid in (Milestone_Signoffs.objects
                           .filter(milestone__appver=av)
                           .order_by('milestone__id',
                                     'signoff__locale__code')
                           .values_list('milestone__id',
                                        'signoff__locale__code',
                                        'signoff__push__id')):
        if _mid != ms_id:
            ms_id = _mid
            # next milestone, bootstrap new row
            if latest:
                # previous milestone has locales left, update previous changes
                changes += [(_loc, 'dropped') for _loc in latest.iterkeys()]
                changes.sort()
            latest = current
            current = {}
            ms_name = ms_names[ms_id]
            changes = []
            rows.append({'name': ms_name,
                         'code': ms_codes[ms_id],
                         'changes': changes})
        if loc not in latest:
            changes.append((loc, 'added'))
        else:
            lpid = latest.pop(loc)
            if lpid != pid:
                changes.append((loc, 'changed'))
        current[loc] = pid
    # see if we have some locales dropped in the last milestone
    if latest:
        # previous milestone has locales left, update previous changes
        changes += [(loc, 'dropped') for loc in latest.iterkeys()]
        changes.sort()
    # finally, check if there's more signoffs after the last shipped milestone
    newso = [(loc, loc in current and 'changed' or 'added')
        for loc, pid in accepted.iteritems()
        if current.get(loc) != pid]
    for loc, flags in notaccepted.iteritems():
        if Action.PENDING in flags:
            newso.append((loc, 'pending'))
        elif Action.REJECTED in flags:
            newso.append((loc, 'rejected'))
        elif Action.OBSOLETED in flags:
            newso.append((loc, 'obsoleted'))
    if newso:
        newso.sort()
        rows.append({
            'name': '%s .next' % str(av),
            'changes': newso
        })

    return render(request, 'shipping/app-changes.html', {
                    'appver': av,
                    'rows': rows,
                  })
Beispiel #56
0
def teamsnippet(loc, team_locales):
    locs = Locale.objects.filter(pk__in=[loc.pk] + list(team_locales))
    runs = sorted(
        (Run.objects
         .filter(locale__in=locs, active__isnull=False)
         .select_related('tree', 'locale')),
        key=lambda r: (r.tree.code,
                       '' if r.locale.code == loc.code else r.locale.code)
    )

    # this locale isn't active in our trees yet
    if not runs:
        return ''

    # 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 avt in (AppVersion.trees.through.objects
                .current()
                .select_related('appversion__app', 'tree')):
        _trees_to_appversions[avt.tree] = avt.appversion

    def tree_to_appversion(tree):
        return _trees_to_appversions.get(tree)

    def tree_to_application(tree):
        av = tree_to_appversion(tree)
        return av and av.app or None

    # 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__in=locs)
                         .values_list('run_id', 'changeset__revision'))

    progresses = dict(
        ((pp.tree.code, pp.locale.code), pp) for pp in (
            ProgressPosition.objects
            .filter(tree__run__in=runs)
            .select_related('tree', 'locale')
            )
        )
    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 = RunElement(dict((k, getattr(run_, k))
                       for k in run_.__dict__
                       if not k.startswith('_')))
        run.locale = run_.locale
        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
        run.prog_pos = progresses.get((run.tree.code, run.locale.code))

        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.fallback = run.appversion = None
        if appversion and appversion.accepts_signoffs:
            run.appversion = appversion
            real_av, flags = (
                flags4appversions(
                    locales={'id': run_.locale.id},
                    appversions={'id': appversion.id}
                )
                .get(appversion, {})
                .get(run_.locale.code, [None, {}])
            )

            # get current status of signoffs
            # we really only need the shortrevs, we'll get those below
            if flags:
                actions = Action.objects.filter(id__in=flags.values()) \
                                        .select_related('signoff__push')
                action4id = dict((a.id, a)
                    for a in actions)
                for flag in (Action.PENDING, Action.ACCEPTED, Action.REJECTED):
                    if flag in flags:
                        a = action4id[flags[flag]]
                        setattr(run, a.get_flag_display(), a.signoff.push)
            if appversion.code != real_av:
                run.fallback = real_av
            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 = sorted(
        ((k, v) for (k, v) in applications.items()),
        key=lambda t: t[0] and t[0].name or None
    )
    other_team_locales = Locale.objects.filter(id__in=team_locales)

    progress_start = datetime.utcnow() - timedelta(days=settings.PROGRESS_DAYS)

    return render_to_string('shipping/team-snippet.html',
                            {'locale': loc,
                             'other_team_locales': other_team_locales,
                             'applications': applications,
                             'progress_start': progress_start,
                            })
Beispiel #57
0
def changes(request, app_code):
    """Show fallbacks for a given appversion.
    """
    av = get_object_or_404(AppVersion, code=app_code)
    rows = []
    changes = None
    latest = {}
    current = {}
    # get historic data that enters this appversion
    # get that in fallback order, we'll reverse afterwards
    flags4av = flags4appversions([av])
    flags4loc = flags4av[av]
    locs4av = defaultdict(dict)  # av -> loc -> ACCEPTED
    notaccepted = {}  # av -> flags
    for loc, (real_av, flags) in flags4loc.iteritems():
        if Action.ACCEPTED in flags:
            locs4av[real_av][loc] = flags[Action.ACCEPTED]
        else:
            notaccepted[loc] = flags
    # for not accepted locales on the current appver,
    # check for accepted on fallbacks
    if av.fallback and notaccepted:
        flags4fallback = flags4av[av.fallback]
        for loc in notaccepted:
            if loc in flags4fallback:
                # if the loc isn't here, it's never been accepted so far
                real_av, flags = flags4fallback[loc]
                if Action.ACCEPTED in flags:
                    locs4av[real_av] = flags[Action.ACCEPTED]
    # let's keep the current appver data around for later,
    # and order the fallbacks.
    # Also, keep track of how many locales fell back to
    #  * the previous cycle
    #  * the two cycles before that (2 and 3)
    #  * older cycles (4 and more)
    accepted = locs4av.pop(av.code, {})
    av_ = av
    fallback = 0  # how deep are we falling back
    buckets = {0: 0, 1: 1, 2: 2, 3: 2}  # which fallback to which bucket
    bucket = 0  # bucket we're in
    rowspan = 0  # how many rows are in this bucket
    locales_group = set()  # which locales are in this bucket
    while av_ and locs4av:
        thisbucket = buckets.get(fallback, 3)
        if thisbucket != bucket and locales_group:
            rows[-1].update({
                'rowspan': rowspan,
                'group_locales_count': len(locales_group)
            })
            locales_group.clear()
            rowspan = 0
        bucket = thisbucket
        if av_.code in locs4av:
            accepted4loc = locs4av.pop(av_.code)
            # store actions for now, we'll get the push_ids later
            current.update(accepted4loc)
            locales_group.update(accepted4loc.keys())
            rowspan += 1
            rows.append({
                'name':
                str(av_),
                'code':
                av_.code,
                'isAppVersion':
                True,
                'changes': [(loc, 'added') for loc in sorted(accepted4loc)]
            })
        av_ = av_.fallback
        fallback += 1
    if locales_group and rows:
        rows[-1].update({
            'rowspan': rowspan,
            'group_locales_count': len(locales_group)
        })
    rows.reverse()
    for loc, pid in (Signoff.objects.filter(
            action__in=current.values()).values_list('locale__code',
                                                     'push__id')):
        current[loc] = pid
    for loc, pid in (Signoff.objects.filter(
            action__in=accepted.values()).values_list('locale__code',
                                                      'push__id')):
        accepted[loc] = pid
    # reset group data for the current appversion, all one group
    avrow = None  # keep track of the first row
    rowspan = 0
    locales_group.clear()

    # see if we have some locales dropped in the last appver
    if latest:
        # previous appver has locales left, update previous changes
        changes += [(loc, 'dropped') for loc in latest.iterkeys()]
        changes.sort()
    # add group info to the avrow
    if avrow:
        avrow.update({
            'rowspan': rowspan,
            'rowspan_last': True,
            'group_locales_count': len(locales_group)
        })
    # finally, check if there's more signoffs after the last shipped appver
    newso = [(loc, loc in current and 'changed' or 'added')
             for loc, pid in accepted.iteritems() if current.get(loc) != pid]
    for loc, flags in notaccepted.iteritems():
        if Action.PENDING in flags:
            newso.append((loc, 'pending'))
        elif Action.REJECTED in flags:
            newso.append((loc, 'rejected'))
        elif Action.OBSOLETED in flags:
            newso.append((loc, 'obsoleted'))
    if newso:
        newso.sort()
        rows.append({'name': '%s .next' % str(av), 'changes': newso})
        addcount = len([t for t in newso if t[1] == 'added'])
        if addcount:
            rows[-1].update({
                'rowspan': 1,
                'rowspan_last': True,
                'group_locales_count': '+%d' % addcount
            })

    return render(request, 'shipping/app-changes.html', {
        'appver': av,
        'rows': rows,
    })
Beispiel #58
0
 def _get_flags_and_actions(self):
     __, flags = (api.flags4appversions(
         locales=self._locale_search, appversions=self._appver_search).get(
             self.av, {}).get(self.locale.code, [None, {}]))
     actions = Action.objects.filter(id__in=flags.values())
     return flags, actions
Beispiel #59
0
    def get_signoffs(self):
        appversions_with_pushes = []
        _treeid_to_avt = {}
        tree_ids = {}
        avts = (AppVersion.trees.through.objects
            .current()
            .filter(appversion__accepts_signoffs=True)
        )
        if self.avs:
            avts = avts.filter(appversion__code__in=self.avs)
        for avt in avts.select_related('appversion__app', 'tree__l10n'):
            _treeid_to_avt[avt.tree.id] = avt
            tree_ids[avt.tree.code] = avt.tree.id

        appvers = [avt.appversion for avt in _treeid_to_avt.values()]
        locales = None
        if self.locales:
            locales = list(Locale.objects
                .filter(code__in=self.locales)
                .values_list('id', flat=True))

        locflags4av = flags4appversions(appvers, locales=locales)
        av2tree = dict((avt.appversion.code, avt.tree.code)
            for avt in _treeid_to_avt.itervalues())
        values = dict(Action._meta.get_field('flag').flatchoices)
        so_items = {}
        actions = {}
        for av in appvers:
            for loc, (real_av, flags) in locflags4av[av].iteritems():
                flag_values = [
                    (real_av == av.code or f != Action.ACCEPTED) and values[f]
                    or real_av
                    for f in flags]
                item = {
                    'flags': flag_values,
                    'state': None,
                    'action': []
                }
                if Action.ACCEPTED in flags:
                    item['state'] = (real_av == av.code) and 'OK' or real_av
                if Action.REJECTED in flags:
                    item['action'].append('rejected')
                if Action.PENDING in flags:
                    item['action'].append('review')
                so_items[(av2tree[av.code], loc)] = item
                for flag, action in flags.items():
                    if real_av == av.code or flag == Action.PENDING:
                        actions[action] = [av, loc]
        for action, signoff, push_date in (Action.objects
            .filter(id__in = actions.keys())
            .values_list('id', 'signoff', 'signoff__push__push_date')):
                actions[action] += [signoff, push_date]
        last_action = {}
        last_signoff = {}
        for action, (av, loc, signoff, push_date) in sorted(
                actions.iteritems(),
                key=lambda t: t[1][3],
                reverse=True):
            if (av, loc) in last_action:
                continue
            last_action[(av, loc)] = push_date
            last_signoff[(av, loc)] = signoff

        runs_with_open_av = [run for run, tree in self.runids2tree.items()
            if tree in tree_ids and _treeid_to_avt[tree_ids[tree]].appversion.accepts_signoffs]
        changesets = dict((tuple(t[:2]), t[2])
            for t in Run.revisions.through.objects
            .filter(run__in=runs_with_open_av,
                    changeset__repositories__locale__isnull=False)
            .values_list('run__tree', 'run__locale__code', 'changeset'))
        pushdates = dict((tuple(t[:2]), t[2])
            for t in Push.changesets.through.objects
            .filter(changeset__in=set(changesets.values()))
            .values_list('changeset', 'push__repository__forest', 'push__push_date'))
        avl_has_new_run = set()
        maybe_new_run = []
        for (treeid, locale_code), changeset in changesets.iteritems():
            avt = _treeid_to_avt[treeid]
            push_date = pushdates[(changeset, avt.tree.l10n.id)]
            if avt.start and push_date < avt.start:
                # need update
                appversions_with_pushes.append({
                    "needs_update": True,
                    "type": "NewPush",
                    "label": avt.tree.code  + '/' + locale_code
                })
                continue
            if avt.end and push_date > avt.end:
                continue
            if ((avt.appversion, locale_code) in last_action and
                last_action[(avt.appversion, locale_code)] >= push_date):
                    continue
            maybe_new_run.append((avt.appversion, changeset, avt.tree.code,
                locale_code, last_signoff.get((avt.appversion, locale_code))
            ))

        # find out if the sign-off is on the same revision as the last push
        old_signoffs = filter(None, (t[4] for t in maybe_new_run))
        signoffs_to_skip = set()
        if old_signoffs:
            so_tips = dict(Signoff.objects
                .filter(id__in=old_signoffs)
                .annotate(cs=Max("push__changesets"))
                .values_list('id', 'cs'))
            latest_changeset = dict((t[4], t[1]) for t in maybe_new_run)
            for so_id, tip in so_tips.iteritems():
                if latest_changeset[so_id] == tip:
                    signoffs_to_skip.add(so_id)
        for av, changeset, tree_code, locale_code, last_so in maybe_new_run:
            if last_so in signoffs_to_skip:
                continue
            avl_has_new_run.add((tree_code, locale_code))
            appversions_with_pushes.append({
                "new_run": "sign off",
                "type": "NewPush",
                "label": tree_code + '/' + locale_code
            })

        # make a list now
        items = []
        for (tree, locale), values in sorted(so_items.iteritems(),
                                             key=lambda t:t[0]):
            glyph = ''
            if values['state'] == 'OK':
                glyph = 'check'
                if (tree, locale) in avl_has_new_run:
                    glyph = 'graph'
            elif values['state']:
                glyph = 'warning'
            item = {
                "type": "SignOff",
                "label": "%s/%s" % (tree, locale),
                "tree": tree,
                "state": values['state'],
                "state_glyph": glyph,
                "signoff": sorted(values['flags'])
            }
            if values['action']:
                item['action'] = values['action']
            items.append(item)

        items += [{"type": "AppVer4Tree",
                   "label": tree,
                   "appversion": av}
                  for av, tree in av2tree.iteritems()]
        items += appversions_with_pushes
        return items
Beispiel #60
0
def data(request):
    app_codes = request.GET.getlist('app')
    if not app_codes:
        raise Http404('List of applications required')
    beta_apps = Application.objects.filter(code__in=app_codes)
    if len(app_codes) != beta_apps.count():
        raise Http404('Some of the given apps were not found')
    branches = request.GET.getlist('branch')

    f_aurora = Forest.objects.get(name='releases/l10n/mozilla-aurora')
    f_beta = Forest.objects.get(name='releases/l10n/mozilla-beta')
    forests = []
    if 'aurora' in branches:
        forests.append(f_aurora)
    if 'beta' in branches:
        forests.append(f_beta)
    name4app = dict(beta_apps.values_list('id', 'name'))
    # get AppVersion_Trees
    avts = (AppVersionTreeThrough.objects.current()
            .filter(appversion__app__in=name4app.keys(),
                    tree__l10n__in=forests)
            .order_by('appversion__code')
            .select_related('appversion__app', 'tree'))
    avts = list(avts)
    code4av = dict((avt.appversion_id, avt.appversion.code) for avt in avts)
    appname4av = dict((avt.appversion_id,
                       name4app[avt.appversion.app_id])
                      for avt in avts)
    tree4av = dict((avt.appversion_id, avt.tree_id) for avt in avts)
    av4tree = dict((avt.tree_id, avt.appversion_id) for avt in avts)
    locs = set()
    loc4tree = defaultdict(list)
    for t, l in (Run.objects
                 .filter(tree__in=tree4av.values(), active__isnull=False)
                 .values_list('tree', 'locale__code')
                 .distinct()):
        loc4tree[t].append(l)
        locs.add(l)
    locflags4av = flags4appversions([avt.appversion for avt in avts])
    loc4tree = defaultdict(list)
    for av, flags4loc in locflags4av.iteritems():
        if av.id not in tree4av:
            continue
        for loc, (real_av, flags) in flags4loc.iteritems():
            if real_av == av.code:
                continue
            loc4tree[tree4av[av.id]].append(loc)
    loc4tree = dict((t, locs) for t, locs in loc4tree.iteritems()
                    if locs)
    or_ = lambda l, r: l | r
    runqueries = reduce(or_,
                        (reduce(or_,
                                (Q(tree=t, locale__code=l) for l in locs))
                            for t, locs in loc4tree.iteritems()))
    actives = (Run.objects
               .filter(runqueries)
               .exclude(active__isnull=True)
               .select_related('locale'))
    missings = dict(((r.tree_id, r.locale.code), r.allmissing)
                    for r in actives)
    matrix = dict()
    columns = tuple(avt.appversion_id for avt in avts)
    for tree_id, locs in loc4tree.iteritems():
        av_id = av4tree[tree_id]
        for loc in locs:
            if loc not in matrix:
                matrix[loc] = [None] * len(avts)
            col_index = columns.index(av_id)
            entry = {'av': code4av[av_id],
                     'app': appname4av[av_id],
                     'missing': missings[(tree4av[av_id],
                                          loc)]}
            matrix[loc][col_index] = entry

    rows = [{'loc':loc, 'entries': matrix[loc]}
            for loc in sorted(matrix.keys())]

    return render(request, 'shipping/out-data.html', {
                    'apps': beta_apps,
                    'appvers': [avt.appversion for avt in avts],
                    'rows': rows,
                  })