diff --git a/apps/l10nstats/management/commands/deactivate.py b/apps/l10nstats/management/commands/deactivate.py
new file mode 100644
index 00000000..708885b3
--- /dev/null
+++ b/apps/l10nstats/management/commands/deactivate.py
@@ -0,0 +1,74 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+from urllib2 import urlopen
+from urlparse import urljoin
+from ConfigParser import ConfigParser
+
+from django.core.management.base import BaseCommand
+
+from l10nstats.models import Active
+
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list
+
+ def handle(self, *args, **kwargs):
+ self.handleApps(*args, **kwargs)
+ self.handleDirs(*args, **kwargs)
+
+ def handleApps(self, *args, **kwargs):
+ l10nbuilds = urlopen(
+ 'https://raw.githubusercontent.com/Pike/master-ball/'
+ 'master/l10n-master/l10nbuilds.ini')
+ cp = ConfigParser()
+ cp.readfp(l10nbuilds)
+ for section in cp.sections():
+ self.stdout.write(section + '\n')
+ self.handleSection(section, dict(cp.items(section)))
+
+ def handleSection(self, section, items):
+ locales = items['locales']
+ if locales == 'all':
+ inipath = '/'.join((
+ items['repo'], items['mozilla'],
+ 'raw-file', 'default',
+ items['l10n.ini']
+ ))
+ ini = ConfigParser()
+ ini.readfp(urlopen(inipath))
+ allpath = urljoin(
+ urljoin(inipath, ini.get('general', 'depth')),
+ ini.get('general', 'all'))
+ locales = urlopen(allpath).read()
+ locales = locales.split()
+ obs = (Active.objects
+ .filter(run__tree__code=section)
+ .exclude(run__locale__code__in=locales)
+ .order_by('run__locale__code'))
+ obslocs = ' '.join(obs.values_list('run__locale__code', flat=True))
+ if not obslocs:
+ self.stdout.write(' OK\n')
+ return
+ s = raw_input('Remove %s? [Y/n] ' % obslocs)
+ if s.lower() == 'y' or s == '':
+ obs.delete()
+
+ def handleDirs(self, *args, **kwargs):
+ dirbuilds = json.load(urlopen(
+ 'https://raw.githubusercontent.com/Pike/master-ball/'
+ 'master/l10n-master/dir-builds.json'))
+ for treedata in dirbuilds:
+ obs = (Active.objects
+ .filter(run__tree__code=treedata['name'])
+ .exclude(run__locale__code__in=treedata['locales'])
+ .order_by('run__locale__code'))
+ obslocs = ' '.join(obs.values_list('run__locale__code', flat=True))
+ if not obslocs:
+ self.stdout.write(' OK\n')
+ continue
+ s = raw_input('Remove %s? [Y/n] ' % obslocs)
+ if s.lower() == 'y' or s == '':
+ obs.delete()
diff --git a/apps/shipping/static/shipping/css/dashboard.css b/apps/shipping/static/shipping/css/dashboard.css
index d6552df9..d42a0d7f 100644
--- a/apps/shipping/static/shipping/css/dashboard.css
+++ b/apps/shipping/static/shipping/css/dashboard.css
@@ -72,6 +72,53 @@ table.stats td.status {
}
+.actions .pending.oi {
+ color: #0096DD;
+}
+
+.actions .rejected.oi {
+ color: red;
+}
+.actions .no_update.oi {
+ color: black;
+}
+
+.signoffs.actions .suggest-error {
+ color: red;
+}
+.signoffs.actions .suggest-failure {
+ color: #ff6c00;
+}
+.signoffs.actions .suggest-warnings {
+ color: green;
+}
+.signoffs.actions .suggest-success {
+ color: green;
+}
+
+.signoffs.status, .signoffs.actions {
+ text-align: center;
+ white-space: nowrap;
+}
+.signoffs.status .status-ok {
+ color: green;
+}
+
+.signoffs .status-fallback {
+ color: #ff6c00;
+}
+
+.signoffs a {
+ color: #000000;
+}
+
+.signoffs a:hover {
+ color: #000000;
+ border-color: #484848;
+}
+
+
+
/* override of exhibit CSS effect
(done here rather than editing exhibit-bundle.css)
*/
diff --git a/apps/shipping/static/shipping/js/dashboard.js b/apps/shipping/static/shipping/js/dashboard.js
index dff821e6..906e1117 100644
--- a/apps/shipping/static/shipping/js/dashboard.js
+++ b/apps/shipping/static/shipping/js/dashboard.js
@@ -4,7 +4,7 @@
/* global Exhibit */
jQuery(document).one("scriptsLoaded.exhibit", function(evt) {
- var EX_OPTIONS = ["result", "shipping", "signoff"];
+ var EX_OPTIONS = ["result", "action", "signoff"];
var params = Exhibit.parseURLParameters();
for (var i in EX_OPTIONS) {
var option = EX_OPTIONS[i];
diff --git a/apps/shipping/templates/shipping/dashboard.html b/apps/shipping/templates/shipping/dashboard.html
index f26a6cfe..d1dc1423 100644
--- a/apps/shipping/templates/shipping/dashboard.html
+++ b/apps/shipping/templates/shipping/dashboard.html
@@ -10,6 +10,7 @@
{% block head_matter %}
+