evorepo/main/models.py
Dan McGee 82eb9de993 Make user profile a OneToOneField
We had this set up as a unique ForeignKey before, which adds some
indirection due to the RelatedManager object being there. By making it a
OneToOneField, we can get the profile object directly, enforce uniqueness,
and also use it in select_related() calls to make our profiles page a bit
more efficient.

Signed-off-by: Dan McGee <dan@archlinux.org>
2010-10-04 17:44:40 -05:00

328 lines
12 KiB
Python

from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from main.utils import cache_function
from packages.models import PackageRelation
class UserProfile(models.Model):
id = models.AutoField(primary_key=True) # not technically needed
notify = models.BooleanField(
"Send notifications",
default=True,
help_text="When enabled, send user 'flag out of date' notifications")
alias = models.CharField(
max_length=50,
help_text="Required field")
public_email = models.CharField(
max_length=50,
help_text="Required field")
other_contact = models.CharField(max_length=100, null=True, blank=True)
website = models.CharField(max_length=200, null=True, blank=True)
yob = models.IntegerField(null=True, blank=True)
location = models.CharField(max_length=50, null=True, blank=True)
languages = models.CharField(max_length=50, null=True, blank=True)
interests = models.CharField(max_length=255, null=True, blank=True)
occupation = models.CharField(max_length=50, null=True, blank=True)
roles = models.CharField(max_length=255, null=True, blank=True)
favorite_distros = models.CharField(max_length=255, null=True, blank=True)
picture = models.FileField(upload_to='devs', default='devs/silhouette.png')
user = models.OneToOneField(User, related_name='userprofile')
allowed_repos = models.ManyToManyField('Repo', blank=True)
class Meta:
db_table = 'user_profiles'
verbose_name = 'Additional Profile Data'
verbose_name_plural = 'Additional Profile Data'
class TodolistManager(models.Manager):
def incomplete(self):
return self.filter(todolistpkg__complete=False).distinct()
class PackageManager(models.Manager):
def flagged(self):
return self.get_query_set().filter(flag_date__isnull=False)
class Donor(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
visible = models.BooleanField(default=True,
help_text="Should we show this donor on the public page?")
def __unicode__(self):
return self.name
class Meta:
db_table = 'donors'
ordering = ['name']
class Arch(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
agnostic = models.BooleanField(default=False,
help_text="Is this architecture non-platform specific?")
def __unicode__(self):
return self.name
class Meta:
db_table = 'arches'
ordering = ['name']
verbose_name_plural = 'arches'
class Repo(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
testing = models.BooleanField(default=False,
help_text="Is this repo meant for package testing?")
bugs_project = models.SmallIntegerField(default=1,
help_text="Flyspray project ID for this repository.")
svn_root = models.CharField(max_length=64,
help_text="SVN root (e.g. path) for this repository.")
def __unicode__(self):
return self.name
class Meta:
db_table = 'repos'
ordering = ['name']
verbose_name_plural = 'repos'
class Package(models.Model):
id = models.AutoField(primary_key=True)
repo = models.ForeignKey(Repo, related_name="packages")
arch = models.ForeignKey(Arch, related_name="packages")
pkgname = models.CharField(max_length=255, db_index=True)
pkgbase = models.CharField(max_length=255, db_index=True)
pkgver = models.CharField(max_length=255)
pkgrel = models.CharField(max_length=255)
pkgdesc = models.CharField(max_length=255, null=True)
url = models.CharField(max_length=255, null=True)
filename = models.CharField(max_length=255)
# TODO: it would be nice to have the >0 check constraint back here
compressed_size = models.BigIntegerField(null=True)
installed_size = models.BigIntegerField(null=True)
build_date = models.DateTimeField(null=True)
last_update = models.DateTimeField(null=True, blank=True)
files_last_update = models.DateTimeField(null=True, blank=True)
license = models.CharField(max_length=255, null=True)
packager_str = models.CharField(max_length=255)
packager = models.ForeignKey(User, null=True)
flag_date = models.DateTimeField(null=True)
objects = PackageManager()
class Meta:
db_table = 'packages'
ordering = ('pkgname',)
#get_latest_by = 'last_update'
#ordering = ('-last_update',)
def __unicode__(self):
return self.pkgname
def get_absolute_url(self):
return '/packages/%s/%s/%s/' % (self.repo.name.lower(),
self.arch.name, self.pkgname)
def get_full_url(self, proto='http'):
'''get a URL suitable for things like email including the domain'''
domain = Site.objects.get_current().domain
return '%s://%s%s' % (proto, domain, self.get_absolute_url())
@property
def maintainers(self):
return User.objects.filter(
package_relations__pkgbase=self.pkgbase,
package_relations__type=PackageRelation.MAINTAINER)
@property
def signoffs(self):
if 'signoffs_cache' in dir(self):
return self.signoffs_cache
self.signoffs_cache = list(Signoff.objects.filter(
pkg=self,
pkgver=self.pkgver,
pkgrel=self.pkgrel))
return self.signoffs_cache
def approved_for_signoff(self):
return len(self.signoffs) >= 2
@cache_function(300)
def get_requiredby(self):
"""
Returns a list of package objects.
"""
arches = list(Arch.objects.filter(agnostic=True))
arches.append(self.arch)
requiredby = Package.objects.select_related('arch', 'repo').filter(
packagedepend__depname=self.pkgname,
arch__in=arches).distinct()
return requiredby.order_by('pkgname')
@cache_function(300)
def get_depends(self):
"""
Returns a list of dicts. Each dict contains ('pkg' and 'dep').
If it represents a found package both vars will be available;
else pkg will be None if it is a 'virtual' dependency.
"""
deps = []
arches = list(Arch.objects.filter(agnostic=True))
arches.append(self.arch)
# TODO: we can use list comprehension and an 'in' query to make this more effective
for dep in self.packagedepend_set.order_by('depname'):
pkgs = Package.objects.select_related('arch', 'repo').filter(
pkgname=dep.depname)
if not self.arch.agnostic:
# make sure we match architectures if possible
pkgs = pkgs.filter(arch__in=arches)
if len(pkgs) == 0:
# couldn't find a package in the DB
# it should be a virtual depend (or a removed package)
pkg = None
elif len(pkgs) == 1:
pkg = pkgs[0]
else:
# more than one package, see if we can't shrink it down
# grab the first though in case we fail
pkg = pkgs[0]
if self.repo.testing:
pkgs = pkgs.filter(repo__testing=True)
else:
pkgs = pkgs.filter(repo__testing=False)
if len(pkgs) > 0:
pkg = pkgs[0]
deps.append({'dep': dep, 'pkg': pkg})
return deps
def base_package(self):
"""
Locate the base package for this package. It may be this very package,
or if it was built in a way that the base package isn't real, will
return None.
"""
try:
# start by looking for something in this repo
return Package.objects.get(arch=self.arch,
repo=self.repo, pkgname=self.pkgbase)
except Package.DoesNotExist:
# this package might be split across repos? just find one
# that matches the correct [testing] repo flag
pkglist = Package.objects.filter(arch=self.arch,
repo__testing=self.repo.testing, pkgname=self.pkgbase)
if len(pkglist) > 0:
return pkglist[0]
return None
def split_packages(self):
"""
Return all packages that were built with this one (e.g. share a pkgbase
value). The package this method is called on will never be in the list,
and we will never return a package that does not have the same
repo.testing flag. For any non-split packages, the return value will be
an empty list.
"""
return Package.objects.filter(arch=self.arch,
repo__testing=self.repo.testing, pkgbase=self.pkgbase).exclude(id=self.id)
def get_svn_link(self, svnpath):
linkbase = "http://repos.archlinux.org/wsvn/%s/%s/%s/"
return linkbase % (self.repo.svn_root, self.pkgbase, svnpath)
def get_arch_svn_link(self):
repo = self.repo.name.lower()
return self.get_svn_link("repos/%s-%s" % (repo, self.arch.name))
def get_trunk_svn_link(self):
return self.get_svn_link("trunk")
def get_bugs_link(self):
return "https://bugs.archlinux.org/?project=%d&string=%s" % \
(self.repo.bugs_project, self.pkgname)
def is_same_version(self, other):
'is this package similar, name and version-wise, to another'
return self.pkgname == other.pkgname \
and self.pkgver == other.pkgver \
and self.pkgrel == other.pkgrel
def in_testing(self):
'''attempt to locate this package in a testing repo; if we are in
a testing repo we will always return None.'''
if self.repo.testing:
return None
try:
return Package.objects.get(repo__testing=True,
pkgname=self.pkgname, arch=self.arch)
except Package.DoesNotExist:
return None
def elsewhere(self):
'''attempt to locate this package anywhere else, regardless of
architecture or repository. Excludes this package from the list.'''
return Package.objects.select_related('arch', 'repo').filter(
pkgname=self.pkgname).exclude(id=self.id).order_by(
'arch__name', 'repo__name')
class Signoff(models.Model):
pkg = models.ForeignKey(Package)
pkgver = models.CharField(max_length=255)
pkgrel = models.CharField(max_length=255)
packager = models.ForeignKey(User)
class PackageFile(models.Model):
id = models.AutoField(primary_key=True)
pkg = models.ForeignKey('Package')
path = models.CharField(max_length=255)
class Meta:
db_table = 'package_files'
class PackageDepend(models.Model):
id = models.AutoField(primary_key=True)
pkg = models.ForeignKey('Package')
depname = models.CharField(db_index=True, max_length=255)
depvcmp = models.CharField(max_length=255)
class Meta:
db_table = 'package_depends'
class Todolist(models.Model):
id = models.AutoField(primary_key=True)
creator = models.ForeignKey(User)
name = models.CharField(max_length=255)
description = models.TextField()
date_added = models.DateField(auto_now_add=True)
objects = TodolistManager()
def __unicode__(self):
return self.name
@property
def packages(self):
# select_related() does not use LEFT OUTER JOIN for nullable ForeignKey
# fields. That is why we need to explicitly list the ones we want.
return TodolistPkg.objects.select_related(
'pkg__repo', 'pkg__arch').filter(list=self).order_by('pkg')
@property
def package_names(self):
# depends on packages property returning a queryset
return self.packages.values_list('pkg__pkgname', flat=True).distinct()
class Meta:
db_table = 'todolists'
def get_absolute_url(self):
return '/todo/%i/' % self.id
class TodolistPkg(models.Model):
id = models.AutoField(primary_key=True)
list = models.ForeignKey('Todolist')
pkg = models.ForeignKey('Package')
complete = models.BooleanField(default=False)
class Meta:
db_table = 'todolist_pkgs'
unique_together = (('list','pkg'),)
# vim: set ts=4 sw=4 et: