2010-02-25 20:00:03 +03:00
|
|
|
# -*- coding: utf-8 -*-
|
2010-04-14 02:00:03 +04:00
|
|
|
import itertools
|
2010-11-12 19:51:52 +03:00
|
|
|
import os
|
2010-04-14 02:00:03 +04:00
|
|
|
|
2010-11-12 19:51:52 +03:00
|
|
|
from django.conf import settings
|
2009-10-23 02:38:11 +04:00
|
|
|
from django.db import models
|
|
|
|
|
2010-11-12 20:42:36 +03:00
|
|
|
import commonware.log
|
2010-02-04 08:13:44 +03:00
|
|
|
import caching.base
|
|
|
|
|
2010-11-04 23:22:57 +03:00
|
|
|
import amo
|
2010-01-29 04:59:26 +03:00
|
|
|
import amo.models
|
2010-11-04 23:22:57 +03:00
|
|
|
from amo.urlresolvers import reverse
|
2010-01-23 03:52:41 +03:00
|
|
|
from applications.models import Application, AppVersion
|
2010-11-12 20:42:36 +03:00
|
|
|
from files.models import File, Platform
|
2010-10-05 04:06:39 +04:00
|
|
|
from translations.fields import (TranslatedField, PurifiedField,
|
|
|
|
LinkifiedField)
|
2010-02-04 08:13:44 +03:00
|
|
|
from users.models import UserProfile
|
2009-10-23 02:38:11 +04:00
|
|
|
|
2010-02-25 21:46:19 +03:00
|
|
|
from . import compare
|
|
|
|
|
2010-11-12 20:42:36 +03:00
|
|
|
log = commonware.log.getLogger('z.versions')
|
|
|
|
|
2009-10-23 02:38:11 +04:00
|
|
|
|
2010-01-29 04:59:26 +03:00
|
|
|
class Version(amo.models.ModelBase):
|
2010-04-14 02:00:03 +04:00
|
|
|
addon = models.ForeignKey('addons.Addon', related_name='versions')
|
2010-01-20 20:45:05 +03:00
|
|
|
license = models.ForeignKey('License', null=True)
|
2010-02-23 22:47:16 +03:00
|
|
|
releasenotes = PurifiedField()
|
2010-03-06 04:37:13 +03:00
|
|
|
approvalnotes = models.TextField(default='', null=True)
|
2009-12-31 03:57:36 +03:00
|
|
|
version = models.CharField(max_length=255, default=0)
|
2009-10-23 02:38:11 +04:00
|
|
|
|
2010-01-29 04:59:26 +03:00
|
|
|
class Meta(amo.models.ModelBase.Meta):
|
2009-10-23 02:38:11 +04:00
|
|
|
db_table = 'versions'
|
2010-03-04 00:30:00 +03:00
|
|
|
ordering = ['-created', '-modified']
|
2010-02-02 23:22:24 +03:00
|
|
|
|
2010-02-25 21:46:19 +03:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(Version, self).__init__(*args, **kwargs)
|
|
|
|
self.__dict__.update(compare.version_dict(self.version or ''))
|
|
|
|
|
2010-02-18 02:56:17 +03:00
|
|
|
def __unicode__(self):
|
|
|
|
return self.version
|
|
|
|
|
2010-11-12 20:42:36 +03:00
|
|
|
@classmethod
|
|
|
|
def from_upload(cls, upload, addon):
|
2010-11-16 03:58:42 +03:00
|
|
|
from . import utils
|
2010-11-12 20:42:36 +03:00
|
|
|
# TODO: license, relnotes
|
2010-11-16 03:58:42 +03:00
|
|
|
data = utils.parse_xpi(upload.path, addon)
|
2010-11-16 04:47:15 +03:00
|
|
|
try:
|
|
|
|
license = addon.versions.latest().license_id
|
|
|
|
except Version.DoesNotExist:
|
|
|
|
license = None
|
|
|
|
v = cls.objects.create(addon=addon, version=data['version'],
|
|
|
|
license_id=license)
|
2010-11-12 20:42:36 +03:00
|
|
|
log.debug('New version: %r (%s) from %r' % (v, v.id, upload))
|
|
|
|
# TODO: platforms?
|
|
|
|
File.from_upload(upload, v,
|
|
|
|
Platform.objects.get(id=amo.PLATFORM_ALL.id))
|
|
|
|
# appversions
|
|
|
|
AV = ApplicationsVersions
|
|
|
|
for app in data['apps']:
|
|
|
|
AV(version=v, min=app.min, max=app.max,
|
|
|
|
application_id=app.id).save()
|
|
|
|
# TODO: share more with XPIForm
|
|
|
|
return v
|
|
|
|
|
2010-11-12 19:51:52 +03:00
|
|
|
@property
|
|
|
|
def path_prefix(self):
|
|
|
|
return os.path.join(settings.ADDONS_PATH, str(self.addon_id))
|
|
|
|
|
2010-10-23 04:30:58 +04:00
|
|
|
def license_url(self):
|
|
|
|
return reverse('addons.license', args=[self.addon_id, self.version])
|
|
|
|
|
2010-08-13 20:24:36 +04:00
|
|
|
def flush_urls(self):
|
|
|
|
return self.addon.flush_urls()
|
|
|
|
|
2010-11-06 00:48:05 +03:00
|
|
|
def get_url_path(self):
|
|
|
|
return reverse('addons.versions', args=(self.addon.id, self.version,))
|
|
|
|
|
2010-11-04 23:22:57 +03:00
|
|
|
def delete(self):
|
2010-11-20 00:51:36 +03:00
|
|
|
amo.log(amo.LOG.DELETE_VERSION, self.addon, str(self.version))
|
2010-11-04 23:22:57 +03:00
|
|
|
super(Version, self).delete()
|
|
|
|
|
2010-04-14 02:00:03 +04:00
|
|
|
@amo.cached_property(writable=True)
|
2010-02-04 09:15:02 +03:00
|
|
|
def compatible_apps(self):
|
|
|
|
"""Get a mapping of {APP: ApplicationVersion}."""
|
2010-04-17 03:19:49 +04:00
|
|
|
avs = self.apps.select_related(depth=1)
|
2010-04-14 02:00:03 +04:00
|
|
|
return self._compat_map(avs)
|
|
|
|
|
|
|
|
@amo.cached_property(writable=True)
|
|
|
|
def all_files(self):
|
|
|
|
"""Shortcut for list(self.files.all()). Heavily cached."""
|
|
|
|
return list(self.files.all())
|
2010-02-04 09:15:02 +03:00
|
|
|
|
|
|
|
# TODO(jbalogh): Do we want names or Platforms?
|
2010-02-06 01:45:23 +03:00
|
|
|
@amo.cached_property
|
2010-02-04 09:15:02 +03:00
|
|
|
def supported_platforms(self):
|
|
|
|
"""Get a list of supported platform names."""
|
2010-03-04 00:30:00 +03:00
|
|
|
return list(set(amo.PLATFORMS[f.platform_id]
|
2010-04-14 02:00:03 +04:00
|
|
|
for f in self.all_files))
|
2009-12-31 03:57:36 +03:00
|
|
|
|
2010-03-02 16:34:07 +03:00
|
|
|
@amo.cached_property
|
|
|
|
def has_files(self):
|
2010-04-14 02:00:03 +04:00
|
|
|
return bool(self.all_files)
|
2010-03-02 16:34:07 +03:00
|
|
|
|
2010-03-09 22:50:56 +03:00
|
|
|
@amo.cached_property
|
|
|
|
def is_unreviewed(self):
|
2010-06-24 20:22:48 +04:00
|
|
|
return filter(lambda f: f.status in amo.UNREVIEWED_STATUSES,
|
2010-04-14 02:00:03 +04:00
|
|
|
self.all_files)
|
|
|
|
|
2010-06-23 22:44:16 +04:00
|
|
|
@amo.cached_property
|
|
|
|
def is_beta(self):
|
|
|
|
return filter(lambda f: f.status == amo.STATUS_BETA,
|
|
|
|
self.all_files)
|
|
|
|
|
2010-04-14 02:00:03 +04:00
|
|
|
@classmethod
|
|
|
|
def _compat_map(cls, avs):
|
|
|
|
apps = {}
|
|
|
|
for av in avs:
|
|
|
|
app_id = av.application_id
|
|
|
|
if app_id in amo.APP_IDS:
|
|
|
|
apps[amo.APP_IDS[app_id]] = av
|
|
|
|
return apps
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def transformer(cls, versions):
|
|
|
|
"""Attach all the compatible apps and files to the versions."""
|
|
|
|
if not versions:
|
|
|
|
return
|
|
|
|
|
|
|
|
avs = (ApplicationsVersions.objects.filter(version__in=versions)
|
2010-06-09 04:16:28 +04:00
|
|
|
.select_related(depth=1).order_by('version__id').no_cache())
|
|
|
|
files = (File.objects.filter(version__in=versions)
|
|
|
|
.order_by('version__id').select_related('version').no_cache())
|
2010-04-14 02:00:03 +04:00
|
|
|
|
|
|
|
def rollup(xs):
|
|
|
|
groups = itertools.groupby(xs, key=lambda x: x.version_id)
|
|
|
|
return dict((k, list(vs)) for k, vs in groups)
|
|
|
|
|
|
|
|
av_dict, file_dict = rollup(avs), rollup(files)
|
|
|
|
|
|
|
|
for version in versions:
|
|
|
|
v_id = version.id
|
|
|
|
version.compatible_apps = cls._compat_map(av_dict.get(v_id, []))
|
|
|
|
version.all_files = file_dict.get(v_id, [])
|
2010-03-09 22:50:56 +03:00
|
|
|
|
2009-12-31 03:57:36 +03:00
|
|
|
|
2010-10-05 04:06:39 +04:00
|
|
|
class LicenseManager(amo.models.ManagerBase):
|
2010-03-01 22:21:09 +03:00
|
|
|
|
2010-10-05 04:06:39 +04:00
|
|
|
def builtins(self):
|
|
|
|
return self.filter(builtin__gt=0).order_by('builtin')
|
2009-12-31 03:57:36 +03:00
|
|
|
|
2010-10-05 04:06:39 +04:00
|
|
|
|
|
|
|
class License(amo.models.ModelBase):
|
|
|
|
OTHER = 0
|
|
|
|
|
|
|
|
name = TranslatedField(db_column='name')
|
|
|
|
url = models.URLField(null=True)
|
|
|
|
builtin = models.PositiveIntegerField(default=OTHER)
|
|
|
|
text = LinkifiedField()
|
|
|
|
on_form = models.BooleanField(default=False,
|
|
|
|
help_text='Is this a license choice in the devhub?')
|
|
|
|
some_rights = models.BooleanField(default=False,
|
|
|
|
help_text='Show "Some Rights Reserved" instead of the license name?')
|
|
|
|
icons = models.CharField(max_length=255, null=True,
|
|
|
|
help_text='Space-separated list of icon identifiers.')
|
|
|
|
|
|
|
|
objects = LicenseManager()
|
|
|
|
|
|
|
|
class Meta:
|
2009-12-31 03:57:36 +03:00
|
|
|
db_table = 'licenses'
|
2010-01-14 03:01:23 +03:00
|
|
|
|
2010-03-01 22:21:09 +03:00
|
|
|
def __unicode__(self):
|
2010-10-05 04:06:39 +04:00
|
|
|
return '%s %s' % (self.id, self.name)
|
2010-03-01 22:21:09 +03:00
|
|
|
|
2010-01-14 03:01:23 +03:00
|
|
|
|
2010-01-29 04:59:26 +03:00
|
|
|
class VersionComment(amo.models.ModelBase):
|
2010-01-14 03:01:23 +03:00
|
|
|
"""Editor comments for version discussion threads."""
|
|
|
|
version = models.ForeignKey(Version)
|
2010-01-18 13:51:34 +03:00
|
|
|
user = models.ForeignKey(UserProfile)
|
2010-08-05 00:25:09 +04:00
|
|
|
reply_to = models.ForeignKey(Version, related_name="reply_to",
|
|
|
|
db_column='reply_to', null=True)
|
2010-01-14 03:01:23 +03:00
|
|
|
subject = models.CharField(max_length=1000)
|
|
|
|
comment = models.TextField()
|
|
|
|
|
2010-01-29 04:59:26 +03:00
|
|
|
class Meta(amo.models.ModelBase.Meta):
|
2010-01-14 03:01:23 +03:00
|
|
|
db_table = 'versioncomments'
|
|
|
|
|
|
|
|
|
2010-01-29 04:59:26 +03:00
|
|
|
class VersionSummary(amo.models.ModelBase):
|
2010-04-14 02:00:03 +04:00
|
|
|
addon = models.ForeignKey('addons.Addon')
|
2010-01-14 03:01:23 +03:00
|
|
|
version = models.ForeignKey(Version)
|
|
|
|
application = models.ForeignKey(Application)
|
|
|
|
min = models.IntegerField(null=True)
|
|
|
|
max = models.IntegerField(null=True)
|
|
|
|
|
2010-01-29 04:59:26 +03:00
|
|
|
class Meta(amo.models.ModelBase.Meta):
|
2010-01-14 03:01:23 +03:00
|
|
|
db_table = 'versions_summary'
|
2010-01-23 03:52:41 +03:00
|
|
|
|
|
|
|
|
2010-02-04 08:13:44 +03:00
|
|
|
class ApplicationsVersions(caching.base.CachingMixin, models.Model):
|
2010-01-23 03:52:41 +03:00
|
|
|
|
|
|
|
application = models.ForeignKey(Application)
|
2010-04-17 03:19:49 +04:00
|
|
|
version = models.ForeignKey(Version, related_name='apps')
|
2010-01-27 01:44:12 +03:00
|
|
|
min = models.ForeignKey(AppVersion, db_column='min',
|
|
|
|
related_name='min_set')
|
|
|
|
max = models.ForeignKey(AppVersion, db_column='max',
|
|
|
|
related_name='max_set')
|
2010-01-23 03:52:41 +03:00
|
|
|
|
2010-02-04 08:13:44 +03:00
|
|
|
objects = caching.base.CachingManager()
|
|
|
|
|
2010-01-23 03:52:41 +03:00
|
|
|
class Meta:
|
|
|
|
db_table = u'applications_versions'
|
2010-02-02 23:22:24 +03:00
|
|
|
unique_together = (("application", "version"),)
|
2010-02-25 20:00:03 +03:00
|
|
|
|
|
|
|
def __unicode__(self):
|
2010-06-18 22:24:18 +04:00
|
|
|
return u'%s %s - %s' % (self.application, self.min, self.max)
|