From 0c6ee81eb469734dae380f428149f316fdbf75bd Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 7 Mar 2011 21:06:04 -0600 Subject: Work around unicode/str issue in pytz Signed-off-by: Dan McGee --- devel/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 311922ca..c926c5a4 100644 --- a/devel/views.py +++ b/devel/views.py @@ -55,7 +55,9 @@ def clock(request): now = datetime.datetime.now() utc_now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) for dev in devs: - tz = pytz.timezone(dev.userprofile.time_zone) + # Work around https://bugs.launchpad.net/pytz/+bug/718673 + timezone = str(dev.userprofile.time_zone) + tz = pytz.timezone(timezone) dev.current_time = utc_now.astimezone(tz) page_dict = { -- cgit v1.2.3-2-g168b From 1dc867587da6b66ca575eb26f4f65cb9d67ffdb3 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 4 Jan 2011 08:45:15 -0600 Subject: Add Admin log overview page This puts the admin log functionality to a bit more use and allows seeing the last 100 overall entries. You can also drill down to see actions on a per-user basis. Signed-off-by: Dan McGee --- devel/urls.py | 2 ++ devel/views.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/urls.py b/devel/urls.py index bcf9c071..41be2b31 100644 --- a/devel/urls.py +++ b/devel/urls.py @@ -5,6 +5,8 @@ urlpatterns = patterns('devel.views', (r'^clock/$', 'clock'), (r'^profile/$', 'change_profile'), (r'^newuser/$', 'new_user_form'), + (r'^admin_log/(?P.*)/$','admin_log'), + (r'^admin_log/$','admin_log'), ) # vim: set ts=4 sw=4 et: diff --git a/devel/views.py b/devel/views.py index c926c5a4..46387f7a 100644 --- a/devel/views.py +++ b/devel/views.py @@ -1,9 +1,11 @@ from django import forms from django.http import HttpResponseRedirect -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import \ + login_required, permission_required, user_passes_test from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.core.mail import send_mail +from django.shortcuts import get_object_or_404 from django.template import loader, Context from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template @@ -169,4 +171,16 @@ def new_user_form(request): } return direct_to_template(request, 'general_form.html', context) +@user_passes_test(lambda u: u.is_superuser) +@never_cache +def admin_log(request, username=None): + user = None + if username: + user = get_object_or_404(User, username=username) + context = { + 'title': "Admin Action Log", + 'log_user': user, + } + return direct_to_template(request, 'devel/admin_log.html', context) + # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 9730be60a8ef4a04358b0a026ce6b706de21d4e8 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 13 Mar 2011 11:29:05 -0500 Subject: Add package epoch support This comes with pacman 3.5, replacing the old "force" PKGBUILD option. We parse it and store it for now, but don't display it anywhere just yet. Also update a few queries relying on version differences in any of the multiple parts. Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 09e48559..f826abea 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -88,11 +88,13 @@ class Pkg(object): bare = ( 'name', 'base', 'arch', 'desc', 'filename', 'md5sum', 'url', 'builddate', 'packager' ) number = ( 'csize', 'isize' ) + version_re = re.compile(r'^((\d+):)?(.+)-([^-]+)$') def __init__(self, repo): self.repo = repo self.ver = None self.rel = None + self.epoch = 0 for k in self.bare + self.number: setattr(self, k, None) @@ -106,9 +108,11 @@ class Pkg(object): elif k == 'force': setattr(self, k, True) elif k == 'version': - ver, rel = v[0].rsplit('-') - setattr(self, 'ver', ver) - setattr(self, 'rel', rel) + match = self.version_re.match(v[0]) + self.ver = match.group(3) + self.rel = match.group(4) + if match.group(2): + self.epoch = int(match.group(2)) else: # files, depends, etc. setattr(self, k, v) @@ -184,6 +188,7 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): dbpkg.pkgbase = repopkg.name dbpkg.pkgver = repopkg.ver dbpkg.pkgrel = repopkg.rel + dbpkg.epoch = repopkg.epoch dbpkg.pkgdesc = repopkg.desc dbpkg.url = repopkg.url dbpkg.filename = repopkg.filename @@ -230,10 +235,12 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): def populate_files(dbpkg, repopkg, force=False): if not force: - if dbpkg.pkgver != repopkg.ver or dbpkg.pkgrel != repopkg.rel: - logger.info("db version (%s-%s) didn't match repo version (%s-%s) " - "for package %s, skipping file list addition", - dbpkg.pkgver, dbpkg.pkgrel, repopkg.ver, repopkg.rel, + if dbpkg.pkgver != repopkg.ver or dbpkg.pkgrel != repopkg.rel \ + or dbpkg.epoch != repopkg.epoch: + logger.info("db version (%s:%s-%s) didn't match repo version " + "(%s:%s-%s) for package %s, skipping file list addition", + dbpkg.epoch, dbpkg.pkgver, dbpkg.pkgrel, + repopkg.epoch, repopkg.ver, repopkg.rel, dbpkg.pkgname) return if not dbpkg.files_last_update or not dbpkg.last_update: @@ -334,7 +341,8 @@ def db_update(archname, reponame, pkgs, options): # for a non-force, we don't want to do anything at all. if filesonly: pass - elif p.ver == dbp.pkgver and p.rel == dbp.pkgrel: + elif p.ver == dbp.pkgver and p.rel == dbp.pkgrel \ + and p.epoch == dbp.epoch: if not force: continue else: -- cgit v1.2.3-2-g168b From ad2a9ac23af0d4610e079741070c4408a4c6ce16 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 11 Mar 2011 17:13:25 -0600 Subject: Show orphan package counts in maintainer dashboard table Signed-off-by: Dan McGee --- devel/views.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 46387f7a..5b8965d8 100644 --- a/devel/views.py +++ b/devel/views.py @@ -36,11 +36,22 @@ def index(request): maintainers = get_annotated_maintainers() + maintained = PackageRelation.objects.filter( + type=PackageRelation.MAINTAINER).values('pkgbase') + total_orphans = Package.objects.exclude(pkgbase__in=maintained).count() + total_flagged_orphans = Package.objects.filter( + flag_date__isnull=False).exclude(pkgbase__in=maintained).count() + orphan = { + 'package_count': total_orphans, + 'flagged_count': total_flagged_orphans, + } + page_dict = { 'todos': Todolist.objects.incomplete().order_by('-date_added'), 'repos': Repo.objects.all(), 'arches': Arch.objects.all(), 'maintainers': maintainers, + 'orphan': orphan, 'flagged' : flagged, 'todopkgs' : todopkgs, } -- cgit v1.2.3-2-g168b From b0061f5bd99ef49733b4b66ef67dfd8f156785b1 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 16 Mar 2011 08:32:40 -0500 Subject: Various reporead small cleanups Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 53 +++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 24 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index f826abea..3f1e9ddf 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -117,6 +117,13 @@ class Pkg(object): # files, depends, etc. setattr(self, k, v) + @property + def full_version(self): + '''Very similar to the main.models.Package method.''' + if self.epoch > 0: + return u'%d:%s-%s' % (self.epoch, self.ver, self.rel) + return u'%s-%s' % (self.ver, self.rel) + def find_user(userstring): ''' @@ -237,11 +244,9 @@ def populate_files(dbpkg, repopkg, force=False): if not force: if dbpkg.pkgver != repopkg.ver or dbpkg.pkgrel != repopkg.rel \ or dbpkg.epoch != repopkg.epoch: - logger.info("db version (%s:%s-%s) didn't match repo version " - "(%s:%s-%s) for package %s, skipping file list addition", - dbpkg.epoch, dbpkg.pkgver, dbpkg.pkgrel, - repopkg.epoch, repopkg.ver, repopkg.rel, - dbpkg.pkgname) + logger.info("DB version (%s) didn't match repo version " + "(%s) for package %s, skipping file list addition", + dbpkg.full_version, repopkg.full_version, dbpkg.pkgname) return if not dbpkg.files_last_update or not dbpkg.last_update: pass @@ -280,25 +285,23 @@ def db_update(archname, reponame, pkgs, options): filesonly = options.get('filesonly', False) repository = Repo.objects.get(name__iexact=reponame) architecture = Arch.objects.get(name__iexact=archname) - dbpkgs = Package.objects.filter(arch=architecture, repo=repository) - # It makes sense to fully evaluate our DB query now because we will - # be using 99% of the objects in our "in both sets" loop. Force eval - # by calling list() on the QuerySet. - list(dbpkgs) + # no-arg order_by() removes even the default ordering; we don't need it + dbpkgs = Package.objects.filter( + arch=architecture, repo=repository).order_by() # This makes our inner loop where we find packages by name *way* more # efficient by not having to go to the database for each package to # SELECT them by name. dbdict = dict([(pkg.pkgname, pkg) for pkg in dbpkgs]) logger.debug("Creating sets") - dbset = set([pkg.pkgname for pkg in dbpkgs]) + dbset = set(dbdict.keys()) syncset = set([pkg.name for pkg in pkgs]) logger.info("%d packages in current web DB", len(dbset)) logger.info("%d packages in new updating db", len(syncset)) in_sync_not_db = syncset - dbset logger.info("%d packages in sync not db", len(in_sync_not_db)) - # Try to catch those random orphaning issues that make Eric so unhappy. + # Try to catch those random package deletions that make Eric so unhappy. if len(dbset): dbpercent = 100.0 * len(syncset) / len(dbset) else: @@ -309,12 +312,14 @@ def db_update(archname, reponame, pkgs, options): # means we expect the repo to fluctuate a lot. msg = "Package database has %.1f%% the number of packages in the " \ "web database" % dbpercent - if not filesonly and \ + if len(dbset) == 0 and len(syncset) == 0: + pass + elif not filesonly and \ len(dbset) > 20 and dbpercent < 50.0 and \ not repository.testing: logger.error(msg) raise Exception(msg) - if dbpercent < 75.0: + elif dbpercent < 75.0: logger.warning(msg) if not filesonly: @@ -328,8 +333,8 @@ def db_update(archname, reponame, pkgs, options): in_db_not_sync = dbset - syncset for p in in_db_not_sync: logger.info("Removing package %s from database", p) - Package.objects.get( - pkgname=p, arch=architecture, repo=repository).delete() + dbp = dbdict[p] + dbp.delete() # packages in both database and in syncdb (update in database) pkg_in_both = syncset & dbset @@ -429,10 +434,9 @@ def parse_repo(repopath): logger.info("Finished repo parsing, %d total packages", len(pkgs)) return (reponame, pkgs.values()) -def validate_arch(arch): +def validate_arch(archname): "Check if arch is valid." - available_arches = [x.name for x in Arch.objects.all()] - return arch in available_arches + return Arch.objects.filter(name__iexact=archname).exists() def read_repo(primary_arch, repo_file, options): """ @@ -440,21 +444,22 @@ def read_repo(primary_arch, repo_file, options): """ repo, packages = parse_repo(repo_file) - # sort packages by arch -- to handle noarch stuff + # group packages by arch -- to handle noarch stuff packages_arches = {} - packages_arches['any'] = [] + for arch in Arch.objects.filter(agnostic=True): + packages_arches[arch.name] = [] packages_arches[primary_arch] = [] for package in packages: - if package.arch in ('any', primary_arch): + if package.arch in packages_arches: packages_arches[package.arch].append(package) else: # we don't include mis-arched packages logger.warning("Package %s arch = %s", package.name,package.arch) logger.info('Starting database updates.') - for (arch, pkgs) in packages_arches.items(): - db_update(arch, repo, pkgs, options) + for arch in sorted(packages_arches.keys()): + db_update(arch, repo, packages_arches[arch], options) logger.info('Finished database updates.') return 0 -- cgit v1.2.3-2-g168b From 59b176ed025548118f750e0d506f8a1d20cc4635 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 21 Mar 2011 13:46:09 -0500 Subject: Don't bail on staging repo being short on packages Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 3f1e9ddf..231cad01 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -316,7 +316,7 @@ def db_update(archname, reponame, pkgs, options): pass elif not filesonly and \ len(dbset) > 20 and dbpercent < 50.0 and \ - not repository.testing: + not repository.testing and not repository.staging: logger.error(msg) raise Exception(msg) elif dbpercent < 75.0: -- cgit v1.2.3-2-g168b From 857cae2e96aa0d23b0702d5ad9db572787f657a9 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 16 Feb 2011 18:22:14 -0600 Subject: reporead: refactor multivalued attribute creation This will come in more handy with our new models, but we can adapt groups and licenses to use it first. Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 231cad01..0c835555 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -188,6 +188,17 @@ def create_depend(package, dep_str, optional=False): depend.save(force_insert=True) return depend +def create_multivalued(dbpkg, repopkg, db_attr, repo_attr): + '''Populate the simplest of multivalued attributes. These are those that + only deal with a 'name' attribute, such as licenses, groups, etc. The input + and output objects and attribute names are specified, and everything is + done via hasattr()/getattr().''' + collection = getattr(dbpkg, db_attr) + collection.all().delete() + if hasattr(repopkg, repo_attr): + for name in getattr(repopkg, repo_attr): + collection.create(name=name) + def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): if repopkg.base: dbpkg.pkgbase = repopkg.base @@ -229,15 +240,8 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): for y in repopkg.optdepends: dep = create_depend(dbpkg, y, True) - dbpkg.groups.all().delete() - if hasattr(repopkg, 'groups'): - for y in repopkg.groups: - dbpkg.groups.create(name=y) - - dbpkg.licenses.all().delete() - if hasattr(repopkg, 'license'): - for y in repopkg.license: - dbpkg.licenses.create(name=y) + create_multivalued(dbpkg, repopkg, 'groups', 'groups') + create_multivalued(dbpkg, repopkg, 'licenses', 'license') def populate_files(dbpkg, repopkg, force=False): -- cgit v1.2.3-2-g168b From 151eafb0192957c489fa5b8010c081eb6db5f925 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 16:43:39 -0500 Subject: Remove need to import each individual logger constant Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 0c835555..8ce6eab8 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -35,12 +35,10 @@ try: except ImportError: pass -from logging import ERROR, WARNING, INFO, DEBUG - from main.models import Arch, Package, PackageDepend, PackageFile, Repo logging.basicConfig( - level=WARNING, + level=logging.WARNING, format='%(asctime)s -> %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', stream=sys.stderr) @@ -69,11 +67,11 @@ class Command(BaseCommand): v = int(options.get('verbosity', 0)) if v == 0: - logger.level = ERROR + logger.level = logging.ERROR elif v == 1: - logger.level = INFO + logger.level = logging.INFO elif v == 2: - logger.level = DEBUG + logger.level = logging.DEBUG import signal, traceback handler = lambda sig, stack: traceback.print_stack(stack) -- cgit v1.2.3-2-g168b From d6b148779f3a1119e30fa75c63a2a0e46938098c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 16:44:17 -0500 Subject: reporead: read in provisions, conflicts, and replacements Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 45 ++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 8ce6eab8..708b8a59 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -36,6 +36,7 @@ except ImportError: pass from main.models import Arch, Package, PackageDepend, PackageFile, Repo +from packages.models import Conflict, Provision, Replacement logging.basicConfig( level=logging.WARNING, @@ -172,20 +173,48 @@ def find_user(userstring): # lookup more than strictly necessary. find_user.cache = {} +DEPEND_RE = re.compile(r"^(.+?)((>=|<=|=|>|<)(.*))?$") + def create_depend(package, dep_str, optional=False): depend = PackageDepend(pkg=package, optional=optional) # lop off any description first parts = dep_str.split(':', 1) if len(parts) > 1: depend.description = parts[1].strip() - match = re.match(r"^(.+?)((>=|<=|=|>|<)(.*))?$", parts[0].strip()) + match = DEPEND_RE.match(parts[0].strip()) if match: depend.depname = match.group(1) if match.group(2): depend.depvcmp = match.group(2) + else: + logger.warning('Package %s had unparsable depend string %s', + package.pkgname, dep_str) + return None depend.save(force_insert=True) return depend +def create_related(model, package, rel_str, equals_only=False): + related = model(pkg=package) + match = DEPEND_RE.match(rel_str) + if match: + related.name = match.group(1) + if match.group(3): + comp = match.group(3) + if not equals_only: + related.comparison = comp + elif comp != '=': + logger.warning( + 'Package %s had unexpected comparison operator %s for %s in %s', + package.pkgname, comp, model.__name__, rel_str) + if match.group(4): + related.version = match.group(4) + else: + logger.warning('Package %s had unparsable %s string %s', + package.pkgname, model.___name__, rel_str) + return None + related.save(force_insert=True) + return related + def create_multivalued(dbpkg, repopkg, db_attr, repo_attr): '''Populate the simplest of multivalued attributes. These are those that only deal with a 'name' attribute, such as licenses, groups, etc. The input @@ -233,10 +262,20 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): dbpkg.packagedepend_set.all().delete() if hasattr(repopkg, 'depends'): for y in repopkg.depends: - dep = create_depend(dbpkg, y) + create_depend(dbpkg, y) if hasattr(repopkg, 'optdepends'): for y in repopkg.optdepends: - dep = create_depend(dbpkg, y, True) + create_depend(dbpkg, y, True) + + if hasattr(repopkg, 'conflicts'): + for y in repopkg.conflicts: + create_related(Conflict, dbpkg, y) + if hasattr(repopkg, 'provides'): + for y in repopkg.provides: + create_related(Provision, dbpkg, y, equals_only=True) + if hasattr(repopkg, 'replaces'): + for y in repopkg.replaces: + create_related(Replacement, dbpkg, y) create_multivalued(dbpkg, repopkg, 'groups', 'groups') create_multivalued(dbpkg, repopkg, 'licenses', 'license') -- cgit v1.2.3-2-g168b From dad2ca8b3e42cbf0ad5a67be7016426ec4835a19 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 20:19:00 -0500 Subject: Clear out package relation sets before adding new values Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 708b8a59..277196e2 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -267,12 +267,15 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): for y in repopkg.optdepends: create_depend(dbpkg, y, True) + dbpkg.conflicts.all().delete() if hasattr(repopkg, 'conflicts'): for y in repopkg.conflicts: create_related(Conflict, dbpkg, y) + dbpkg.provides.all().delete() if hasattr(repopkg, 'provides'): for y in repopkg.provides: create_related(Provision, dbpkg, y, equals_only=True) + dbpkg.replaces.all().delete() if hasattr(repopkg, 'replaces'): for y in repopkg.replaces: create_related(Replacement, dbpkg, y) -- cgit v1.2.3-2-g168b From ca370499d863dc2ce86fbeb26cf810d3808239f6 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 23 Mar 2011 20:32:55 -0500 Subject: reporead: remove the need for hasattr() checks Ensure all our multivalued attributes already exist on the object beforehand, and add some special sauce to handle the difference between a package without files and a database without files entries. Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 51 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 277196e2..35bb7da3 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -87,6 +87,9 @@ class Pkg(object): bare = ( 'name', 'base', 'arch', 'desc', 'filename', 'md5sum', 'url', 'builddate', 'packager' ) number = ( 'csize', 'isize' ) + collections = ( 'depends', 'optdepends', 'conflicts', + 'replaces', 'groups', 'license', 'files' ) + version_re = re.compile(r'^((\d+):)?(.+)-([^-]+)$') def __init__(self, repo): @@ -96,6 +99,11 @@ class Pkg(object): self.epoch = 0 for k in self.bare + self.number: setattr(self, k, None) + for k in self.collections: + setattr(self, k, ()) + # So we can tell the diffence between a package with no files, and a DB + # without files entries + self.has_files = False def populate(self, values): for k, v in values.iteritems(): @@ -104,16 +112,17 @@ class Pkg(object): setattr(self, k, v[0][:254]) elif k in self.number: setattr(self, k, long(v[0])) - elif k == 'force': - setattr(self, k, True) elif k == 'version': match = self.version_re.match(v[0]) self.ver = match.group(3) self.rel = match.group(4) if match.group(2): self.epoch = int(match.group(2)) + elif k == 'files': + self.files = v + self.has_files = True else: - # files, depends, etc. + # anything left in collections setattr(self, k, v) @property @@ -219,12 +228,11 @@ def create_multivalued(dbpkg, repopkg, db_attr, repo_attr): '''Populate the simplest of multivalued attributes. These are those that only deal with a 'name' attribute, such as licenses, groups, etc. The input and output objects and attribute names are specified, and everything is - done via hasattr()/getattr().''' + done via getattr().''' collection = getattr(dbpkg, db_attr) collection.all().delete() - if hasattr(repopkg, repo_attr): - for name in getattr(repopkg, repo_attr): - collection.create(name=name) + for name in getattr(repopkg, repo_attr): + collection.create(name=name) def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): if repopkg.base: @@ -260,25 +268,20 @@ def populate_pkg(dbpkg, repopkg, force=False, timestamp=None): populate_files(dbpkg, repopkg, force=force) dbpkg.packagedepend_set.all().delete() - if hasattr(repopkg, 'depends'): - for y in repopkg.depends: - create_depend(dbpkg, y) - if hasattr(repopkg, 'optdepends'): - for y in repopkg.optdepends: - create_depend(dbpkg, y, True) + for y in repopkg.depends: + create_depend(dbpkg, y) + for y in repopkg.optdepends: + create_depend(dbpkg, y, True) dbpkg.conflicts.all().delete() - if hasattr(repopkg, 'conflicts'): - for y in repopkg.conflicts: - create_related(Conflict, dbpkg, y) + for y in repopkg.conflicts: + create_related(Conflict, dbpkg, y) dbpkg.provides.all().delete() - if hasattr(repopkg, 'provides'): - for y in repopkg.provides: - create_related(Provision, dbpkg, y, equals_only=True) + for y in repopkg.provides: + create_related(Provision, dbpkg, y, equals_only=True) dbpkg.replaces.all().delete() - if hasattr(repopkg, 'replaces'): - for y in repopkg.replaces: - create_related(Replacement, dbpkg, y) + for y in repopkg.replaces: + create_related(Replacement, dbpkg, y) create_multivalued(dbpkg, repopkg, 'groups', 'groups') create_multivalued(dbpkg, repopkg, 'licenses', 'license') @@ -297,7 +300,7 @@ def populate_files(dbpkg, repopkg, force=False): elif dbpkg.files_last_update > dbpkg.last_update: return # only delete files if we are reading a DB that contains them - if hasattr(repopkg, 'files'): + if repopkg.has_files: dbpkg.packagefile_set.all().delete() logger.info("adding %d files for package %s", len(repopkg.files), dbpkg.pkgname) @@ -311,7 +314,7 @@ def populate_files(dbpkg, repopkg, force=False): is_directory=(filename is None), directory=dirname + '/', filename=filename) - pkgfile.save() + pkgfile.save(force_insert=True) dbpkg.files_last_update = datetime.now() dbpkg.save() -- cgit v1.2.3-2-g168b From f6464da0e14047e9db803f4b40c18ba644e75f24 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 24 Mar 2011 00:47:23 -0500 Subject: Add provides to collections list Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index 35bb7da3..e26bb800 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -88,7 +88,7 @@ class Pkg(object): 'md5sum', 'url', 'builddate', 'packager' ) number = ( 'csize', 'isize' ) collections = ( 'depends', 'optdepends', 'conflicts', - 'replaces', 'groups', 'license', 'files' ) + 'provides', 'replaces', 'groups', 'license', 'files' ) version_re = re.compile(r'^((\d+):)?(.+)-([^-]+)$') -- cgit v1.2.3-2-g168b From 36f8649c36c0fa6af02247e0a796b79df8b1eb2b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 29 Mar 2011 13:53:55 -0500 Subject: Show important fields first on new user form Signed-off-by: Dan McGee --- devel/views.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 5b8965d8..b774e0e2 100644 --- a/devel/views.py +++ b/devel/views.py @@ -119,14 +119,25 @@ def change_profile(request): {'form': form, 'profile_form': profile_form}) class NewUserForm(forms.ModelForm): - class Meta: - model = UserProfile - exclude = ('picture', 'user') username = forms.CharField(max_length=30) - email = forms.EmailField() + private_email = forms.EmailField() first_name = forms.CharField(required=False) last_name = forms.CharField(required=False) + class Meta: + model = UserProfile + exclude = ('picture', 'user') + + def __init__(self, *args, **kwargs): + super(NewUserForm, self).__init__(*args, **kwargs) + # Hack ourself so certain fields appear first. self.fields is a + # SortedDict object where we can manipulate the keyOrder list. + order = self.fields.keyOrder + keys = ('username', 'private_email', 'first_name', 'last_name') + for key in reversed(keys): + order.remove(key) + order.insert(0, key) + def clean_username(self): username = self.cleaned_data['username'] if User.objects.filter(username=username).exists(): @@ -139,7 +150,7 @@ class NewUserForm(forms.ModelForm): pwletters = ascii_letters + digits password = ''.join([random.choice(pwletters) for i in xrange(8)]) user = User.objects.create_user(username=self.cleaned_data['username'], - email=self.cleaned_data['email'], password=password) + email=self.cleaned_data['private_email'], password=password) user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] user.save() -- cgit v1.2.3-2-g168b From 1ce650d3c3850020c6ba54766cb70ad049c6e0bd Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 29 Mar 2011 14:19:51 -0500 Subject: Log user additions via new user form Signed-off-by: Dan McGee --- devel/views.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index b774e0e2..a6a51f22 100644 --- a/devel/views.py +++ b/devel/views.py @@ -170,6 +170,20 @@ class NewUserForm(forms.ModelForm): [user.email], fail_silently=False) +def log_addition(request, obj): + """Cribbed from ModelAdmin.log_addition.""" + from django.contrib.admin.models import LogEntry, ADDITION + from django.contrib.contenttypes.models import ContentType + from django.utils.encoding import force_unicode + LogEntry.objects.log_action( + user_id = request.user.pk, + content_type_id = ContentType.objects.get_for_model(obj).pk, + object_id = obj.pk, + object_repr = force_unicode(obj), + action_flag = ADDITION, + change_message = "Added via Create New User form." + ) + @permission_required('auth.add_user') @never_cache def new_user_form(request): @@ -177,6 +191,7 @@ def new_user_form(request): form = NewUserForm(request.POST) if form.is_valid(): form.save() + log_addition(request, form.instance.user) return HttpResponseRedirect('/admin/auth/user/%d/' % \ form.instance.user.id) else: -- cgit v1.2.3-2-g168b From 325b9d63d07cff73117258507e7cde592d2f824c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 29 Mar 2011 14:46:25 -0500 Subject: Work out kinks in create new user view We allowed repo selection before, but never actually called save_m2m() so selections would have to be repeated on the next page. Add in group selection as well to the form, and ensure we do all of our database operations in one transaction so it is a lot easier to test and roll back if things go wrong. Signed-off-by: Dan McGee --- devel/views.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index a6a51f22..a83c3bbc 100644 --- a/devel/views.py +++ b/devel/views.py @@ -2,9 +2,10 @@ from django import forms from django.http import HttpResponseRedirect from django.contrib.auth.decorators import \ login_required, permission_required, user_passes_test -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group from django.contrib.sites.models import Site from django.core.mail import send_mail +from django.db import transaction from django.shortcuts import get_object_or_404 from django.template import loader, Context from django.views.decorators.cache import never_cache @@ -123,6 +124,8 @@ class NewUserForm(forms.ModelForm): private_email = forms.EmailField() first_name = forms.CharField(required=False) last_name = forms.CharField(required=False) + groups = forms.ModelMultipleChoiceField(required=False, + queryset=Group.objects.all()) class Meta: model = UserProfile @@ -145,8 +148,8 @@ class NewUserForm(forms.ModelForm): "A user with that username already exists.") return username - def save(self): - profile = forms.ModelForm.save(self, False) + def save(self, commit=True): + profile = super(NewUserForm, self).save(False) pwletters = ascii_letters + digits password = ''.join([random.choice(pwletters) for i in xrange(8)]) user = User.objects.create_user(username=self.cleaned_data['username'], @@ -154,8 +157,13 @@ class NewUserForm(forms.ModelForm): user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] user.save() + # sucks that the MRM.add() method can't take a list directly... we have + # to resort to dirty * magic. + user.groups.add(*self.cleaned_data['groups']) profile.user = user - profile.save() + if commit: + profile.save() + self.save_m2m() t = loader.get_template('devel/new_account.txt') c = Context({ @@ -190,8 +198,11 @@ def new_user_form(request): if request.POST: form = NewUserForm(request.POST) if form.is_valid(): - form.save() - log_addition(request, form.instance.user) + @transaction.commit_on_success + def inner_save(): + form.save() + log_addition(request, form.instance.user) + inner_save() return HttpResponseRedirect('/admin/auth/user/%d/' % \ form.instance.user.id) else: -- cgit v1.2.3-2-g168b From 01db07bad844e17e084f650b6732647f77a91c5c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 7 Apr 2011 15:39:53 -0500 Subject: Use UTC datetime objects everywhere Rather than the twisted mix of local times and UTC times we currently have. Signed-off-by: Dan McGee --- devel/management/commands/reporead.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'devel') diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index e26bb800..a8875c7e 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -315,7 +315,7 @@ def populate_files(dbpkg, repopkg, force=False): directory=dirname + '/', filename=filename) pkgfile.save(force_insert=True) - dbpkg.files_last_update = datetime.now() + dbpkg.files_last_update = datetime.utcnow() dbpkg.save() @transaction.commit_on_success @@ -374,7 +374,7 @@ def db_update(archname, reponame, pkgs, options): for p in [x for x in pkgs if x.name in in_sync_not_db]: logger.info("Adding package %s", p.name) pkg = Package(pkgname = p.name, arch = architecture, repo = repository) - populate_pkg(pkg, p, timestamp=datetime.now()) + populate_pkg(pkg, p, timestamp=datetime.utcnow()) # packages in database and not in syncdb (remove from database) in_db_not_sync = dbset - syncset @@ -398,7 +398,7 @@ def db_update(archname, reponame, pkgs, options): if not force: continue else: - timestamp = datetime.now() + timestamp = datetime.utcnow() if filesonly: logger.debug("Checking files for package %s in database", p.name) populate_files(dbp, p, force=force) -- cgit v1.2.3-2-g168b From 842f59d018947ca696cf116e9d1591f2ad83f8a7 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 9 Apr 2011 16:29:02 -0500 Subject: Rename local variables for clarity Signed-off-by: Dan McGee --- devel/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index a83c3bbc..cb0ff056 100644 --- a/devel/views.py +++ b/devel/views.py @@ -165,15 +165,15 @@ class NewUserForm(forms.ModelForm): profile.save() self.save_m2m() - t = loader.get_template('devel/new_account.txt') - c = Context({ + template = loader.get_template('devel/new_account.txt') + ctx = Context({ 'site': Site.objects.get_current(), 'user': user, 'password': password, }) send_mail("Your new archweb account", - t.render(c), + template.render(ctx), 'Arch Website Notification ', [user.email], fail_silently=False) -- cgit v1.2.3-2-g168b From 064813560c20f53f9fd759d0c4e0f0a6729c8ba6 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 9 Apr 2011 16:29:38 -0500 Subject: Show more info about todolists on developer dashboard Signed-off-by: Dan McGee --- devel/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index cb0ff056..b61e605f 100644 --- a/devel/views.py +++ b/devel/views.py @@ -11,10 +11,11 @@ from django.template import loader, Context from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template -from main.models import Package, Todolist, TodolistPkg +from main.models import Package, TodolistPkg from main.models import Arch, Repo from main.models import UserProfile from packages.models import PackageRelation +from todolists.utils import get_annotated_todolists from .utils import get_annotated_maintainers import datetime @@ -35,6 +36,9 @@ def index(request): todopkgs = todopkgs.filter(pkg__pkgbase__in=inner_q).order_by( 'list__name', 'pkg__pkgname') + todolists = get_annotated_todolists() + todolists = [todolist for todolist in todolists if todolist.incomplete_count > 0] + maintainers = get_annotated_maintainers() maintained = PackageRelation.objects.filter( @@ -48,7 +52,7 @@ def index(request): } page_dict = { - 'todos': Todolist.objects.incomplete().order_by('-date_added'), + 'todos': todolists, 'repos': Repo.objects.all(), 'arches': Arch.objects.all(), 'maintainers': maintainers, -- cgit v1.2.3-2-g168b From 381e0a787205af530ae11bac1b1a17e567eecc84 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 25 Apr 2011 18:09:39 -0500 Subject: Developer reports This commit adds four initial developer reports that are hopefully useful to developers and packages in checking up on the state of things. They include: * big : the 100 biggest packages in the repos * old : packages built > 2 years ago * uncompressed-man : self-explanatory * uncompressed-info : self-explanatory There should obviously be some sort of index page to access all of these, so that will be coming soon. Signed-off-by: Dan McGee --- devel/urls.py | 9 +++++---- devel/views.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 9 deletions(-) (limited to 'devel') diff --git a/devel/urls.py b/devel/urls.py index 41be2b31..9bf50f45 100644 --- a/devel/urls.py +++ b/devel/urls.py @@ -1,12 +1,13 @@ from django.conf.urls.defaults import patterns urlpatterns = patterns('devel.views', - (r'^$', 'index'), + (r'^admin_log/$','admin_log'), + (r'^admin_log/(?P.*)/$','admin_log'), (r'^clock/$', 'clock'), - (r'^profile/$', 'change_profile'), + (r'^$', 'index'), (r'^newuser/$', 'new_user_form'), - (r'^admin_log/(?P.*)/$','admin_log'), - (r'^admin_log/$','admin_log'), + (r'^profile/$', 'change_profile'), + (r'^reports/(?P.*)/$', 'report'), ) # vim: set ts=4 sw=4 et: diff --git a/devel/views.py b/devel/views.py index b61e605f..01d54e6f 100644 --- a/devel/views.py +++ b/devel/views.py @@ -6,19 +6,22 @@ from django.contrib.auth.models import User, Group from django.contrib.sites.models import Site from django.core.mail import send_mail from django.db import transaction +from django.db.models import Q +from django.http import Http404 from django.shortcuts import get_object_or_404 from django.template import loader, Context from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template -from main.models import Package, TodolistPkg +from main.models import Package, PackageFile, TodolistPkg from main.models import Arch, Repo from main.models import UserProfile from packages.models import PackageRelation from todolists.utils import get_annotated_todolists from .utils import get_annotated_maintainers -import datetime +from datetime import datetime, timedelta +import operator import pytz import random from string import ascii_letters, digits @@ -26,7 +29,7 @@ from string import ascii_letters, digits @login_required @never_cache def index(request): - '''the Developer dashboard''' + '''the developer dashboard''' inner_q = PackageRelation.objects.filter(user=request.user).values('pkgbase') flagged = Package.objects.select_related('arch', 'repo').filter( flag_date__isnull=False, pkgbase__in=inner_q).order_by('pkgname') @@ -70,8 +73,8 @@ def clock(request): 'username').select_related('userprofile') # now annotate each dev object with their current time - now = datetime.datetime.now() - utc_now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) + now = datetime.now() + utc_now = datetime.utcnow().replace(tzinfo=pytz.utc) for dev in devs: # Work around https://bugs.launchpad.net/pytz/+bug/718673 timezone = str(dev.userprofile.time_zone) @@ -123,6 +126,47 @@ def change_profile(request): return direct_to_template(request, 'devel/profile.html', {'form': form, 'profile_form': profile_form}) +@login_required +def report(request, report): + title = "Developer Report" + packages = Package.objects.select_related('arch', 'repo') + names = attrs = None + if report == "old": + title = "Packages last built more than two years ago" + cutoff = datetime.now() - timedelta(days=730) + packages = packages.filter(build_date__lt=cutoff).order_by('build_date') + elif report == "big": + title = "100 largest compressed packages" + packages = packages.order_by('-compressed_size')[:100] + names = [ 'Compressed Size', 'Installed Size' ] + attrs = [ 'compressed_size', 'installed_size' ] + elif report == "uncompressed-man": + title = "Packages with uncompressed manpages" + # magic going on here! Checking for all '.1'...'.9' extensions + invalid_endings = [Q(filename__endswith='.%d' % n) for n in range(1,10)] + invalid_endings.append(Q(filename__endswith='.n')) + bad_files = PackageFile.objects.filter(Q(directory__contains='man') & ( + reduce(operator.or_, invalid_endings)) + ).values_list('pkg_id', flat=True).distinct() + packages = packages.filter(id__in=set(bad_files)) + elif report == "uncompressed-info": + title = "Packages with uncompressed infopages" + bad_files = PackageFile.objects.filter(directory__contains='/info', + filename__endswith='.info').values_list( + 'pkg_id', flat=True).distinct() + packages = packages.filter(id__in=set(bad_files)) + else: + raise Http404 + + context = { + 'title': title, + 'packages': packages, + 'column_names': names, + 'column_attrs': attrs, + } + return direct_to_template(request, 'devel/packages.html', context) + + class NewUserForm(forms.ModelForm): username = forms.CharField(max_length=30) private_email = forms.EmailField() -- cgit v1.2.3-2-g168b From 2d1cfc24232945b2ad2c749cc3b3443a89ee880f Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 29 Apr 2011 17:58:31 -0500 Subject: Use single quotes only in dev report view --- devel/views.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 01d54e6f..cf24f0d9 100644 --- a/devel/views.py +++ b/devel/views.py @@ -128,20 +128,20 @@ def change_profile(request): @login_required def report(request, report): - title = "Developer Report" + title = 'Developer Report' packages = Package.objects.select_related('arch', 'repo') names = attrs = None - if report == "old": - title = "Packages last built more than two years ago" + if report == 'old': + title = 'Packages last built more than two years ago' cutoff = datetime.now() - timedelta(days=730) packages = packages.filter(build_date__lt=cutoff).order_by('build_date') - elif report == "big": - title = "100 largest compressed packages" + elif report == 'big': + title = '100 largest compressed packages' packages = packages.order_by('-compressed_size')[:100] names = [ 'Compressed Size', 'Installed Size' ] attrs = [ 'compressed_size', 'installed_size' ] - elif report == "uncompressed-man": - title = "Packages with uncompressed manpages" + elif report == 'uncompressed-man': + title = 'Packages with uncompressed manpages' # magic going on here! Checking for all '.1'...'.9' extensions invalid_endings = [Q(filename__endswith='.%d' % n) for n in range(1,10)] invalid_endings.append(Q(filename__endswith='.n')) @@ -149,8 +149,8 @@ def report(request, report): reduce(operator.or_, invalid_endings)) ).values_list('pkg_id', flat=True).distinct() packages = packages.filter(id__in=set(bad_files)) - elif report == "uncompressed-info": - title = "Packages with uncompressed infopages" + elif report == 'uncompressed-info': + title = 'Packages with uncompressed infopages' bad_files = PackageFile.objects.filter(directory__contains='/info', filename__endswith='.info').values_list( 'pkg_id', flat=True).distinct() -- cgit v1.2.3-2-g168b From e58eb76a1ab4d6f1293b717e21da68f5aa3e5c45 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 29 Apr 2011 18:21:10 -0500 Subject: Change big packages report Signed-off-by: Dan McGee --- devel/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index cf24f0d9..a013b329 100644 --- a/devel/views.py +++ b/devel/views.py @@ -136,8 +136,9 @@ def report(request, report): cutoff = datetime.now() - timedelta(days=730) packages = packages.filter(build_date__lt=cutoff).order_by('build_date') elif report == 'big': - title = '100 largest compressed packages' - packages = packages.order_by('-compressed_size')[:100] + title = 'Packages with compressed size > 50 MiB' + cutoff = 50 * 1024 * 1024 + packages = packages.filter(compressed_size__gte=cutoff).order_by('-compressed_size') names = [ 'Compressed Size', 'Installed Size' ] attrs = [ 'compressed_size', 'installed_size' ] elif report == 'uncompressed-man': -- cgit v1.2.3-2-g168b From 5379348c9337a4abe27e807fef7956e11eebed30 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 29 Apr 2011 18:21:43 -0500 Subject: Add unneeded orphans report Signed-off-by: Dan McGee --- devel/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index a013b329..6907de24 100644 --- a/devel/views.py +++ b/devel/views.py @@ -13,7 +13,7 @@ from django.template import loader, Context from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template -from main.models import Package, PackageFile, TodolistPkg +from main.models import Package, PackageDepend, PackageFile, TodolistPkg from main.models import Arch, Repo from main.models import UserProfile from packages.models import PackageRelation @@ -156,6 +156,13 @@ def report(request, report): filename__endswith='.info').values_list( 'pkg_id', flat=True).distinct() packages = packages.filter(id__in=set(bad_files)) + elif report == 'unneeded-orphans': + title = 'Orphan packages required by no other packages' + owned = PackageRelation.objects.all().values('pkgbase') + required = PackageDepend.objects.all().values('depname') + # The two separate calls to exclude is required to do the right thing + packages = packages.exclude(pkgbase__in=owned).exclude( + pkgname__in=required) else: raise Http404 -- cgit v1.2.3-2-g168b From 8de1bd0639a8b6117bc35dfe0ad1e6a1ac34f715 Mon Sep 17 00:00:00 2001 From: Evangelos Foutras Date: Sat, 30 Apr 2011 15:15:38 +0300 Subject: Add filesizeformat filter to sizes in reports/big We also add a new 'filesize' tablesorter parser that handles all the suffixes found in django's filesizeformat filter. Signed-off-by: Dan McGee --- devel/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 6907de24..9c523f0a 100644 --- a/devel/views.py +++ b/devel/views.py @@ -10,6 +10,7 @@ from django.db.models import Q from django.http import Http404 from django.shortcuts import get_object_or_404 from django.template import loader, Context +from django.template.defaultfilters import filesizeformat from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template @@ -140,7 +141,13 @@ def report(request, report): cutoff = 50 * 1024 * 1024 packages = packages.filter(compressed_size__gte=cutoff).order_by('-compressed_size') names = [ 'Compressed Size', 'Installed Size' ] - attrs = [ 'compressed_size', 'installed_size' ] + attrs = [ 'compressed_size_pretty', 'installed_size_pretty' ] + # Format the compressed and installed sizes with MB/GB/etc suffixes + for package in packages: + package.compressed_size_pretty = filesizeformat( + package.compressed_size) + package.installed_size_pretty = filesizeformat( + package.installed_size) elif report == 'uncompressed-man': title = 'Packages with uncompressed manpages' # magic going on here! Checking for all '.1'...'.9' extensions -- cgit v1.2.3-2-g168b From 3fdc79b01066b91348ab655be890a0aa59aac8b8 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 4 May 2011 12:53:49 -0500 Subject: Make uncompressed info query slightly easier on the database Signed-off-by: Dan McGee --- devel/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 9c523f0a..555c7cc5 100644 --- a/devel/views.py +++ b/devel/views.py @@ -159,7 +159,9 @@ def report(request, report): packages = packages.filter(id__in=set(bad_files)) elif report == 'uncompressed-info': title = 'Packages with uncompressed infopages' - bad_files = PackageFile.objects.filter(directory__contains='/info', + # we don't worry abut looking for '*.info-1', etc., given that an + # uncompressed root page probably exists in the package anyway + bad_files = PackageFile.objects.filter(directory__endswith='/info/', filename__endswith='.info').values_list( 'pkg_id', flat=True).distinct() packages = packages.filter(id__in=set(bad_files)) -- cgit v1.2.3-2-g168b From d8f82d9d72eec6042536797f75e06a9296f4cc71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Reynolds?= Date: Thu, 12 May 2011 15:52:47 -0300 Subject: More rebranding --- devel/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'devel') diff --git a/devel/views.py b/devel/views.py index 5b8965d8..5b03f8c0 100644 --- a/devel/views.py +++ b/devel/views.py @@ -155,7 +155,7 @@ class NewUserForm(forms.ModelForm): send_mail("Your new archweb account", t.render(c), - 'Arch Website Notification ', + 'Parabola ', [user.email], fail_silently=False) -- cgit v1.2.3-2-g168b From f04de80528da8e4eeba8fbbf9f720ba046a5bf11 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 15 May 2011 11:25:17 -0500 Subject: Allow screening developer reports by maintainer A simple link is added for each user, but the URLs are flexible enough to screen by any maintainer if you know how they are constructed. Signed-off-by: Dan McGee --- devel/urls.py | 1 + devel/views.py | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'devel') diff --git a/devel/urls.py b/devel/urls.py index 9bf50f45..8759562e 100644 --- a/devel/urls.py +++ b/devel/urls.py @@ -7,6 +7,7 @@ urlpatterns = patterns('devel.views', (r'^$', 'index'), (r'^newuser/$', 'new_user_form'), (r'^profile/$', 'change_profile'), + (r'^reports/(?P.*)/(?P.*)/$', 'report'), (r'^reports/(?P.*)/$', 'report'), ) diff --git a/devel/views.py b/devel/views.py index 555c7cc5..6c36742f 100644 --- a/devel/views.py +++ b/devel/views.py @@ -128,18 +128,21 @@ def change_profile(request): {'form': form, 'profile_form': profile_form}) @login_required -def report(request, report): +def report(request, report, username=None): title = 'Developer Report' packages = Package.objects.select_related('arch', 'repo') - names = attrs = None + names = attrs = user = None + if report == 'old': title = 'Packages last built more than two years ago' cutoff = datetime.now() - timedelta(days=730) - packages = packages.filter(build_date__lt=cutoff).order_by('build_date') + packages = packages.filter( + build_date__lt=cutoff).order_by('build_date') elif report == 'big': title = 'Packages with compressed size > 50 MiB' cutoff = 50 * 1024 * 1024 - packages = packages.filter(compressed_size__gte=cutoff).order_by('-compressed_size') + packages = packages.filter( + compressed_size__gte=cutoff).order_by('-compressed_size') names = [ 'Compressed Size', 'Installed Size' ] attrs = [ 'compressed_size_pretty', 'installed_size_pretty' ] # Format the compressed and installed sizes with MB/GB/etc suffixes @@ -175,8 +178,19 @@ def report(request, report): else: raise Http404 + if username: + user = get_object_or_404(User, username=username, is_active=True) + maintained = PackageRelation.objects.filter(user=user, + type=PackageRelation.MAINTAINER).values('pkgbase') + packages = packages.filter(pkgbase__in=maintained) + + maints = User.objects.filter(id__in=PackageRelation.objects.filter( + type=PackageRelation.MAINTAINER).values('user')) + context = { + 'all_maintainers': maints, 'title': title, + 'maintainer': user, 'packages': packages, 'column_names': names, 'column_attrs': attrs, -- cgit v1.2.3-2-g168b