summaryrefslogtreecommitdiff
path: root/releng
diff options
context:
space:
mode:
authorLuke Shumaker <LukeShu@sbcglobal.net>2013-04-22 00:36:57 -0400
committerLuke Shumaker <LukeShu@sbcglobal.net>2013-04-22 00:36:57 -0400
commitdf7a6aa620af6a165bdacd755757f8cb1179331c (patch)
tree384b4c62d1f50d8effb733d81d2a810666807624 /releng
parent94f972bb892dbf9a86f089f1872ae6d849c0cd0e (diff)
parenta22557811a24b68ef85d4271787c48d8d1e4fc99 (diff)
Merge branch 'archweb-generic2'
Conflicts: README.BRANDING local_settings.py.example packages/templatetags/package_extras.py public/views.py releng/views.py settings.py sitestatic/archnavbar/archnavbar.css sitestatic/silhouette.png templates/base.html templates/packages/differences.html templates/packages/opensearch.xml templates/packages/search.html templates/public/donate.html templates/public/download.html templates/public/feeds.html templates/public/index.html urls.py
Diffstat (limited to 'releng')
-rw-r--r--releng/admin.py23
-rw-r--r--releng/management/commands/syncisos.py9
-rw-r--r--releng/migrations/0003_auto__chg_field_test_ip_address.py99
-rw-r--r--releng/migrations/0004_auto__add_release.py121
-rw-r--r--releng/migrations/0005_auto__add_field_release_file_size__add_field_release_torrent_data.py127
-rw-r--r--releng/migrations/0006_auto__add_unique_release_version.py117
-rw-r--r--releng/migrations/0007_auto__add_field_release_md5__add_field_release_sha1.py122
-rw-r--r--releng/models.py112
-rw-r--r--releng/urls.py13
-rw-r--r--releng/views.py98
10 files changed, 792 insertions, 49 deletions
diff --git a/releng/admin.py b/releng/admin.py
index 42755002..c7e6396e 100644
--- a/releng/admin.py
+++ b/releng/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
from .models import (Architecture, BootType, Bootloader, ClockChoice,
Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source,
- Test)
+ Test, Release)
class IsoAdmin(admin.ModelAdmin):
list_display = ('name', 'created', 'active', 'removed')
@@ -14,19 +14,20 @@ class TestAdmin(admin.ModelAdmin):
'iso', 'success')
list_filter = ('success', 'iso')
+class ReleaseAdmin(admin.ModelAdmin):
+ list_display = ('version', 'release_date', 'kernel_version', 'available',
+ 'created')
+ list_filter = ('available', 'release_date')
-admin.site.register(Architecture)
-admin.site.register(BootType)
-admin.site.register(Bootloader)
-admin.site.register(ClockChoice)
-admin.site.register(Filesystem)
-admin.site.register(HardwareType)
-admin.site.register(InstallType)
-admin.site.register(IsoType)
-admin.site.register(Module)
-admin.site.register(Source)
+
+SIMPLE_MODELS = (Architecture, BootType, Bootloader, ClockChoice, Filesystem,
+ HardwareType, InstallType, IsoType, Module, Source)
+
+for model in SIMPLE_MODELS:
+ admin.site.register(model)
admin.site.register(Iso, IsoAdmin)
admin.site.register(Test, TestAdmin)
+admin.site.register(Release, ReleaseAdmin)
# vim: set ts=4 sw=4 et:
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index 62f005ff..f182cc33 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -4,8 +4,8 @@ from HTMLParser import HTMLParser, HTMLParseError
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
+from django.utils.timezone import now
-from main.utils import utc_now
from releng.models import Iso
@@ -20,7 +20,7 @@ class IsoListParser(HTMLParser):
if tag == 'a':
for name, value in attrs:
if name == "href":
- if value != '../' and self.url_re.search(value) != None:
+ if value != '../' and self.url_re.search(value) is not None:
self.hyperlinks.append(value[:-1])
def parse(self, url):
@@ -53,10 +53,9 @@ class Command(BaseCommand):
if not existing.active:
existing.active = True
existing.removed = None
- existing.save()
- now = utc_now()
+ existing.save(update_fields=('active', 'removed'))
# and then mark all other names as no longer active
Iso.objects.filter(active=True).exclude(name__in=active_isos).update(
- active=False, removed=now)
+ active=False, removed=now())
# vim: set ts=4 sw=4 et:
diff --git a/releng/migrations/0003_auto__chg_field_test_ip_address.py b/releng/migrations/0003_auto__chg_field_test_ip_address.py
new file mode 100644
index 00000000..78297f14
--- /dev/null
+++ b/releng/migrations/0003_auto__chg_field_test_ip_address.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.alter_column('releng_test', 'ip_address', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39))
+
+ def backwards(self, orm):
+ db.alter_column('releng_test', 'ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15))
+
+ models = {
+ 'releng.architecture': {
+ 'Meta': {'object_name': 'Architecture'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.bootloader': {
+ 'Meta': {'object_name': 'Bootloader'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.boottype': {
+ 'Meta': {'object_name': 'BootType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.clockchoice': {
+ 'Meta': {'object_name': 'ClockChoice'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.filesystem': {
+ 'Meta': {'object_name': 'Filesystem'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.hardwaretype': {
+ 'Meta': {'object_name': 'HardwareType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.installtype': {
+ 'Meta': {'object_name': 'InstallType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.iso': {
+ 'Meta': {'object_name': 'Iso'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'removed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
+ },
+ 'releng.isotype': {
+ 'Meta': {'object_name': 'IsoType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.module': {
+ 'Meta': {'object_name': 'Module'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.source': {
+ 'Meta': {'object_name': 'Source'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.test': {
+ 'Meta': {'object_name': 'Test'},
+ 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Architecture']"}),
+ 'boot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.BootType']"}),
+ 'bootloader': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Bootloader']"}),
+ 'clock_choice': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.ClockChoice']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'filesystem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Filesystem']"}),
+ 'hardware_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.HardwareType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'install_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.InstallType']"}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'iso': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Iso']"}),
+ 'iso_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.IsoType']"}),
+ 'modules': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['releng.Module']", 'null': 'True', 'blank': 'True'}),
+ 'rollback_filesystem': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'to': "orm['releng.Filesystem']"}),
+ 'rollback_modules': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['releng.Module']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Source']"}),
+ 'success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['releng']
diff --git a/releng/migrations/0004_auto__add_release.py b/releng/migrations/0004_auto__add_release.py
new file mode 100644
index 00000000..fe4acea5
--- /dev/null
+++ b/releng/migrations/0004_auto__add_release.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.create_table('releng_release', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('release_date', self.gf('django.db.models.fields.DateField')(db_index=True)),
+ ('version', self.gf('django.db.models.fields.CharField')(max_length=50)),
+ ('kernel_version', self.gf('django.db.models.fields.CharField')(max_length=50, blank=True)),
+ ('torrent_infohash', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')()),
+ ('available', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('info', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('releng', ['Release'])
+
+ def backwards(self, orm):
+ db.delete_table('releng_release')
+
+
+ models = {
+ 'releng.architecture': {
+ 'Meta': {'object_name': 'Architecture'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.bootloader': {
+ 'Meta': {'object_name': 'Bootloader'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.boottype': {
+ 'Meta': {'object_name': 'BootType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.clockchoice': {
+ 'Meta': {'object_name': 'ClockChoice'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.filesystem': {
+ 'Meta': {'object_name': 'Filesystem'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.hardwaretype': {
+ 'Meta': {'object_name': 'HardwareType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.installtype': {
+ 'Meta': {'object_name': 'InstallType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.iso': {
+ 'Meta': {'object_name': 'Iso'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'removed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
+ },
+ 'releng.isotype': {
+ 'Meta': {'object_name': 'IsoType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.module': {
+ 'Meta': {'object_name': 'Module'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.release': {
+ 'Meta': {'ordering': "('-release_date', '-version')", 'object_name': 'Release'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'info': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'kernel_version': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'release_date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}),
+ 'torrent_infohash': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'releng.source': {
+ 'Meta': {'object_name': 'Source'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.test': {
+ 'Meta': {'object_name': 'Test'},
+ 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Architecture']"}),
+ 'boot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.BootType']"}),
+ 'bootloader': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Bootloader']"}),
+ 'clock_choice': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.ClockChoice']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'filesystem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Filesystem']"}),
+ 'hardware_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.HardwareType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'install_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.InstallType']"}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'iso': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Iso']"}),
+ 'iso_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.IsoType']"}),
+ 'modules': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['releng.Module']", 'null': 'True', 'blank': 'True'}),
+ 'rollback_filesystem': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'to': "orm['releng.Filesystem']"}),
+ 'rollback_modules': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['releng.Module']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Source']"}),
+ 'success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['releng']
diff --git a/releng/migrations/0005_auto__add_field_release_file_size__add_field_release_torrent_data.py b/releng/migrations/0005_auto__add_field_release_file_size__add_field_release_torrent_data.py
new file mode 100644
index 00000000..96e0727c
--- /dev/null
+++ b/releng/migrations/0005_auto__add_field_release_file_size__add_field_release_torrent_data.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Release.file_size'
+ db.add_column('releng_release', 'file_size',
+ self.gf('main.fields.PositiveBigIntegerField')(null=True, blank=True),
+ keep_default=False)
+
+ # Adding field 'Release.torrent_data'
+ db.add_column('releng_release', 'torrent_data',
+ self.gf('django.db.models.fields.TextField')(default='', blank=True),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Release.file_size'
+ db.delete_column('releng_release', 'file_size')
+
+ # Deleting field 'Release.torrent_data'
+ db.delete_column('releng_release', 'torrent_data')
+
+
+ models = {
+ 'releng.architecture': {
+ 'Meta': {'object_name': 'Architecture'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.bootloader': {
+ 'Meta': {'object_name': 'Bootloader'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.boottype': {
+ 'Meta': {'object_name': 'BootType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.clockchoice': {
+ 'Meta': {'object_name': 'ClockChoice'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.filesystem': {
+ 'Meta': {'object_name': 'Filesystem'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.hardwaretype': {
+ 'Meta': {'object_name': 'HardwareType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.installtype': {
+ 'Meta': {'object_name': 'InstallType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.iso': {
+ 'Meta': {'object_name': 'Iso'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'removed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
+ },
+ 'releng.isotype': {
+ 'Meta': {'object_name': 'IsoType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.module': {
+ 'Meta': {'object_name': 'Module'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.release': {
+ 'Meta': {'ordering': "('-release_date', '-version')", 'object_name': 'Release'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'file_size': ('main.fields.PositiveBigIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'info': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'kernel_version': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'release_date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}),
+ 'torrent_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'torrent_infohash': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'releng.source': {
+ 'Meta': {'object_name': 'Source'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.test': {
+ 'Meta': {'object_name': 'Test'},
+ 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Architecture']"}),
+ 'boot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.BootType']"}),
+ 'bootloader': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Bootloader']"}),
+ 'clock_choice': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.ClockChoice']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'filesystem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Filesystem']"}),
+ 'hardware_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.HardwareType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'install_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.InstallType']"}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'iso': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Iso']"}),
+ 'iso_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.IsoType']"}),
+ 'modules': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['releng.Module']", 'null': 'True', 'blank': 'True'}),
+ 'rollback_filesystem': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'to': "orm['releng.Filesystem']"}),
+ 'rollback_modules': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['releng.Module']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Source']"}),
+ 'success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['releng'] \ No newline at end of file
diff --git a/releng/migrations/0006_auto__add_unique_release_version.py b/releng/migrations/0006_auto__add_unique_release_version.py
new file mode 100644
index 00000000..cb834870
--- /dev/null
+++ b/releng/migrations/0006_auto__add_unique_release_version.py
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding unique constraint on 'Release', fields ['version']
+ db.create_unique('releng_release', ['version'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'Release', fields ['version']
+ db.delete_unique('releng_release', ['version'])
+
+
+ models = {
+ 'releng.architecture': {
+ 'Meta': {'object_name': 'Architecture'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.bootloader': {
+ 'Meta': {'object_name': 'Bootloader'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.boottype': {
+ 'Meta': {'object_name': 'BootType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.clockchoice': {
+ 'Meta': {'object_name': 'ClockChoice'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.filesystem': {
+ 'Meta': {'object_name': 'Filesystem'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.hardwaretype': {
+ 'Meta': {'object_name': 'HardwareType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.installtype': {
+ 'Meta': {'object_name': 'InstallType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.iso': {
+ 'Meta': {'object_name': 'Iso'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'removed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
+ },
+ 'releng.isotype': {
+ 'Meta': {'object_name': 'IsoType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.module': {
+ 'Meta': {'object_name': 'Module'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.release': {
+ 'Meta': {'ordering': "('-release_date', '-version')", 'object_name': 'Release'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'file_size': ('main.fields.PositiveBigIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'info': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'kernel_version': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'release_date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}),
+ 'torrent_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'torrent_infohash': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'releng.source': {
+ 'Meta': {'object_name': 'Source'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.test': {
+ 'Meta': {'object_name': 'Test'},
+ 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Architecture']"}),
+ 'boot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.BootType']"}),
+ 'bootloader': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Bootloader']"}),
+ 'clock_choice': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.ClockChoice']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'filesystem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Filesystem']"}),
+ 'hardware_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.HardwareType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'install_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.InstallType']"}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'iso': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Iso']"}),
+ 'iso_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.IsoType']"}),
+ 'modules': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['releng.Module']", 'null': 'True', 'blank': 'True'}),
+ 'rollback_filesystem': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'to': "orm['releng.Filesystem']"}),
+ 'rollback_modules': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['releng.Module']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Source']"}),
+ 'success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['releng'] \ No newline at end of file
diff --git a/releng/migrations/0007_auto__add_field_release_md5__add_field_release_sha1.py b/releng/migrations/0007_auto__add_field_release_md5__add_field_release_sha1.py
new file mode 100644
index 00000000..f76be3d7
--- /dev/null
+++ b/releng/migrations/0007_auto__add_field_release_md5__add_field_release_sha1.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.add_column('releng_release', 'md5_sum',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=32, blank=True),
+ keep_default=False)
+ db.add_column('releng_release', 'sha1_sum',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=40, blank=True),
+ keep_default=False)
+ db.alter_column('releng_release', 'torrent_infohash', self.gf('django.db.models.fields.CharField')(max_length=40))
+
+ def backwards(self, orm):
+ db.delete_column('releng_release', 'md5_sum')
+ db.delete_column('releng_release', 'sha1_sum')
+ db.alter_column('releng_release', 'torrent_infohash', self.gf('django.db.models.fields.CharField')(max_length=64))
+
+ models = {
+ 'releng.architecture': {
+ 'Meta': {'object_name': 'Architecture'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.bootloader': {
+ 'Meta': {'object_name': 'Bootloader'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.boottype': {
+ 'Meta': {'object_name': 'BootType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.clockchoice': {
+ 'Meta': {'object_name': 'ClockChoice'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.filesystem': {
+ 'Meta': {'object_name': 'Filesystem'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.hardwaretype': {
+ 'Meta': {'object_name': 'HardwareType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.installtype': {
+ 'Meta': {'object_name': 'InstallType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.iso': {
+ 'Meta': {'object_name': 'Iso'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'removed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
+ },
+ 'releng.isotype': {
+ 'Meta': {'object_name': 'IsoType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.module': {
+ 'Meta': {'object_name': 'Module'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.release': {
+ 'Meta': {'ordering': "('-release_date', '-version')", 'object_name': 'Release'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'file_size': ('main.fields.PositiveBigIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'info': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'kernel_version': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'md5_sum': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'release_date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}),
+ 'sha1_sum': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}),
+ 'torrent_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'torrent_infohash': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'releng.source': {
+ 'Meta': {'object_name': 'Source'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'releng.test': {
+ 'Meta': {'object_name': 'Test'},
+ 'architecture': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Architecture']"}),
+ 'boot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.BootType']"}),
+ 'bootloader': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Bootloader']"}),
+ 'clock_choice': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.ClockChoice']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'filesystem': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Filesystem']"}),
+ 'hardware_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.HardwareType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'install_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.InstallType']"}),
+ 'ip_address': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
+ 'iso': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Iso']"}),
+ 'iso_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.IsoType']"}),
+ 'modules': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['releng.Module']", 'null': 'True', 'blank': 'True'}),
+ 'rollback_filesystem': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'to': "orm['releng.Filesystem']"}),
+ 'rollback_modules': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rollback_test_set'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['releng.Module']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['releng.Source']"}),
+ 'success': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+ 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['releng']
diff --git a/releng/models.py b/releng/models.py
index 56187766..5ee2f325 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -1,9 +1,20 @@
+from base64 import b64decode
+from bencode import bdecode, bencode
+from datetime import datetime
+import hashlib
+import markdown
+from pytz import utc
+
+from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.signals import pre_save
+from django.utils.safestring import mark_safe
+from main.fields import PositiveBigIntegerField
from main.utils import set_created_field
+
class IsoOption(models.Model):
name = models.CharField(max_length=200)
@@ -13,10 +24,12 @@ class IsoOption(models.Model):
class Meta:
abstract = True
+
class RollbackOption(IsoOption):
class Meta:
abstract = True
+
class Iso(models.Model):
name = models.CharField(max_length=255)
created = models.DateTimeField(editable=False)
@@ -32,41 +45,54 @@ class Iso(models.Model):
class Meta:
verbose_name = 'ISO'
+
class Architecture(IsoOption):
pass
+
class IsoType(IsoOption):
class Meta:
verbose_name = 'ISO type'
+
class BootType(IsoOption):
pass
+
class HardwareType(IsoOption):
pass
+
class InstallType(IsoOption):
pass
+
class Source(IsoOption):
pass
+
class ClockChoice(IsoOption):
pass
+
class Filesystem(RollbackOption):
pass
+
class Module(RollbackOption):
pass
+
class Bootloader(IsoOption):
pass
+
class Test(models.Model):
user_name = models.CharField(max_length=500)
- user_email = models.EmailField()
- ip_address = models.IPAddressField()
+ user_email = models.EmailField('email address')
+ # Great work, Django... https://code.djangoproject.com/ticket/18212
+ ip_address = models.GenericIPAddressField(verbose_name='IP address',
+ unpack_ipv4=True)
created = models.DateTimeField(editable=False)
iso = models.ForeignKey(Iso)
@@ -88,9 +114,83 @@ class Test(models.Model):
success = models.BooleanField()
comments = models.TextField(null=True, blank=True)
-pre_save.connect(set_created_field, sender=Iso,
- dispatch_uid="releng.models")
-pre_save.connect(set_created_field, sender=Test,
- dispatch_uid="releng.models")
+
+class Release(models.Model):
+ release_date = models.DateField(db_index=True)
+ version = models.CharField(max_length=50, unique=True)
+ kernel_version = models.CharField(max_length=50, blank=True)
+ torrent_infohash = models.CharField(max_length=40, blank=True)
+ md5_sum = models.CharField('MD5 digest', max_length=32, blank=True)
+ sha1_sum = models.CharField('SHA1 digest', max_length=40, blank=True)
+ file_size = PositiveBigIntegerField(null=True, blank=True)
+ created = models.DateTimeField(editable=False)
+ available = models.BooleanField(default=True)
+ info = models.TextField('Public information', blank=True)
+ torrent_data = models.TextField(blank=True)
+
+ class Meta:
+ get_latest_by = 'release_date'
+ ordering = ('-release_date', '-version')
+
+ def __unicode__(self):
+ return self.version
+
+ def get_absolute_url(self):
+ return reverse('releng-release-detail', args=[self.version])
+
+ def dir_path(self):
+ return "iso/%s/" % self.version
+
+ def iso_url(self):
+ return "iso/%s/archlinux-%s-dual.iso" % (self.version, self.version)
+
+ def magnet_uri(self):
+ query = [
+ ('dn', "archlinux-%s-dual.iso" % self.version),
+ ]
+ if settings.TORRENT_TRACKERS:
+ query.extend(('tr', uri) for uri in settings.TORRENT_TRACKERS)
+ if self.torrent_infohash:
+ query.insert(0, ('xt', "urn:btih:%s" % self.torrent_infohash))
+ return "magnet:?%s" % '&'.join(['%s=%s' % (k, v) for k, v in query])
+
+ def info_html(self):
+ return mark_safe(markdown.markdown(
+ self.info, safe_mode=True, enable_attributes=False))
+
+ def torrent(self):
+ try:
+ data = b64decode(self.torrent_data.encode('utf-8'))
+ except TypeError:
+ return None
+ if not data:
+ return None
+ data = bdecode(data)
+ # transform the data into a template-friendly dict
+ info = data.get('info', {})
+ metadata = {
+ 'comment': data.get('comment', None),
+ 'created_by': data.get('created by', None),
+ 'creation_date': None,
+ 'announce': data.get('announce', None),
+ 'file_name': info.get('name', None),
+ 'file_length': info.get('length', None),
+ 'piece_count': len(info.get('pieces', '')) / 20,
+ 'piece_length': info.get('piece length', None),
+ 'url_list': data.get('url-list', []),
+ 'info_hash': None,
+ }
+ if 'creation date' in data:
+ created= datetime.utcfromtimestamp(data['creation date'])
+ metadata['creation_date'] = created.replace(tzinfo=utc)
+ if info:
+ metadata['info_hash'] = hashlib.sha1(bencode(info)).hexdigest()
+
+ return metadata
+
+
+for model in (Iso, Test, Release):
+ pre_save.connect(set_created_field, sender=model,
+ dispatch_uid="releng.models")
# vim: set ts=4 sw=4 et:
diff --git a/releng/urls.py b/releng/urls.py
index 8d1b8f24..76c36345 100644
--- a/releng/urls.py
+++ b/releng/urls.py
@@ -1,5 +1,7 @@
from django.conf.urls import include, patterns
+from .views import ReleaseListView, ReleaseDetailView
+
feedback_patterns = patterns('releng.views',
(r'^$', 'test_results_overview', {}, 'releng-test-overview'),
(r'^submit/$', 'submit_test_result', {}, 'releng-test-submit'),
@@ -9,7 +11,18 @@ feedback_patterns = patterns('releng.views',
(r'^iso/overview/$', 'iso_overview', {}, 'releng-iso-overview'),
)
+releases_patterns = patterns('releng.views',
+ (r'^$',
+ ReleaseListView.as_view(), {}, 'releng-release-list'),
+ (r'^(?P<version>[-.\w]+)/$',
+ ReleaseDetailView.as_view(), {}, 'releng-release-detail'),
+ (r'^(?P<version>[-.\w]+)/torrent/$',
+ 'release_torrent', {}, 'releng-release-torrent'),
+)
+
urlpatterns = patterns('',
(r'^feedback/', include(feedback_patterns)),
+ (r'^releases/', include(releases_patterns)),
)
+
# vim: set ts=4 sw=4 et:
diff --git a/releng/views.py b/releng/views.py
index 5f20c203..ca0a7dd2 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -1,19 +1,23 @@
+from base64 import b64decode
+
from django import forms
from django.conf import settings
from django.db.models import Count, Max
-from django.http import Http404
-from django.shortcuts import get_object_or_404, redirect
-from django.views.generic.simple import direct_to_template
+from django.http import Http404, HttpResponse
+from django.shortcuts import get_object_or_404, redirect, render
+from django.views.generic import DetailView, ListView
from .models import (Architecture, BootType, Bootloader, ClockChoice,
Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source,
- Test)
+ Test, Release)
+
def standard_field(model, empty_label=None, help_text=None, required=True):
return forms.ModelChoiceField(queryset=model.objects.all(),
widget=forms.RadioSelect(), empty_label=empty_label,
help_text=help_text, required=required)
+
class TestForm(forms.ModelForm):
iso = forms.ModelChoiceField(queryset=Iso.objects.filter(
active=True).order_by('-id'))
@@ -25,29 +29,34 @@ class TestForm(forms.ModelForm):
source = standard_field(Source)
clock_choice = standard_field(ClockChoice)
filesystem = standard_field(Filesystem,
- help_text="Verify /etc/fstab, `df -hT` output and commands like " \
+ help_text="Verify /etc/fstab, `df -hT` output and commands like "
"lvdisplay for special modules.")
modules = forms.ModelMultipleChoiceField(queryset=Module.objects.all(),
- help_text="", widget=forms.CheckboxSelectMultiple(), required=False)
+ widget=forms.CheckboxSelectMultiple(), required=False)
bootloader = standard_field(Bootloader,
- help_text="Verify that the entries in the bootloader config looks OK.")
+ help_text="Verify that the entries in the bootloader config "
+ "looks OK.")
rollback_filesystem = standard_field(Filesystem,
- help_text="If you did a rollback followed by a new attempt to setup " \
- "your blockdevices/filesystems, select which option you took here.",
+ help_text="If you did a rollback followed by a new attempt to "
+ "setup your blockdevices/filesystems, select which option you "
+ "took here.",
empty_label="N/A (did not rollback)", required=False)
- rollback_modules = forms.ModelMultipleChoiceField(queryset=Module.objects.all(),
- help_text="If you did a rollback followed by a new attempt to setup " \
- "your blockdevices/filesystems, select which option you took here.",
+ rollback_modules = forms.ModelMultipleChoiceField(
+ queryset=Module.objects.all(),
+ help_text="If you did a rollback followed by a new attempt to "
+ "setup your blockdevices/filesystems, select which option you "
+ "took here.",
widget=forms.CheckboxSelectMultiple(), required=False)
success = forms.BooleanField(
- help_text="Only check this if everything went fine. " \
- "If you ran into problems please create a ticket on <a " \
- "href=\""+settings.BUGTRACKER_RELENG_URL+"\">the " \
- "bugtracker</a> (or check that one already exists) and link to " \
+ help_text="Only check this if everything went fine. "
+ "If you ran into problems please create a ticket on <a "
+ "href=\""+settings.BUGTRACKER_RELENG_URL+"\">the "
+ "bugtracker</a> (or check that one already exists) and link to "
"it in the comments.",
required=False)
website = forms.CharField(label='',
- widget=forms.TextInput(attrs={'style': 'display:none;'}), required=False)
+ widget=forms.TextInput(attrs={'style': 'display:none;'}),
+ required=False)
class Meta:
model = Test
@@ -60,6 +69,7 @@ class TestForm(forms.ModelForm):
"modules": forms.CheckboxSelectMultiple(),
}
+
def submit_test_result(request):
if request.POST:
form = TestForm(request.POST)
@@ -73,7 +83,8 @@ def submit_test_result(request):
form = TestForm()
context = {'form': form}
- return direct_to_template(request, 'releng/add.html', context)
+ return render(request, 'releng/add.html', context)
+
def calculate_option_overview(field_name):
field = Test._meta.get_field(field_name)
@@ -109,6 +120,7 @@ def calculate_option_overview(field_name):
return option
+
def options_fetch_iso(options):
'''Replaces the Iso PK with a full Iso model object in a list of options
used on the overview page. We do it this way to only have to query the Iso
@@ -129,13 +141,19 @@ def options_fetch_iso(options):
return options
+
def test_results_overview(request):
# data structure produced:
- # [ { option, name, is_rollback, values: [ { value, success, failure } ... ] } ... ]
+ # [ {
+ # option, name, is_rollback,
+ # values: [ { value, success, failure } ... ]
+ # }
+ # ...
+ # ]
all_options = []
- fields = [ 'architecture', 'iso_type', 'boot_type', 'hardware_type',
+ fields = ['architecture', 'iso_type', 'boot_type', 'hardware_type',
'install_type', 'source', 'clock_choice', 'filesystem', 'modules',
- 'bootloader', 'rollback_filesystem', 'rollback_modules' ]
+ 'bootloader', 'rollback_filesystem', 'rollback_modules']
for field in fields:
all_options.append(calculate_option_overview(field))
@@ -145,7 +163,8 @@ def test_results_overview(request):
'options': all_options,
'iso_url': settings.ISO_LIST_URL,
}
- return direct_to_template(request, 'releng/results.html', context)
+ return render(request, 'releng/results.html', context)
+
def test_results_iso(request, iso_id):
iso = get_object_or_404(Iso, pk=iso_id)
@@ -154,7 +173,8 @@ def test_results_iso(request, iso_id):
'iso_name': iso.name,
'test_list': test_list
}
- return direct_to_template(request, 'releng/result_list.html', context)
+ return render(request, 'releng/result_list.html', context)
+
def test_results_for(request, option, value):
if option not in Test._meta.get_all_field_names():
@@ -170,10 +190,12 @@ def test_results_for(request, option, value):
'value_id': value,
'test_list': test_list
}
- return direct_to_template(request, 'releng/result_list.html', context)
+ return render(request, 'releng/result_list.html', context)
+
def submit_test_thanks(request):
- return direct_to_template(request, "releng/thanks.html", None)
+ return render(request, "releng/thanks.html", None)
+
def iso_overview(request):
isos = Iso.objects.all().order_by('-pk')
@@ -187,11 +209,33 @@ def iso_overview(request):
# only show "useful" rows, currently active ISOs or those with results
isos = [iso for iso in isos if
- iso.active == True or iso.successes > 0 or iso.failures > 0]
+ iso.active is True or iso.successes > 0 or iso.failures > 0]
context = {
'isos': isos
}
- return direct_to_template(request, 'releng/iso_overview.html', context)
+ return render(request, 'releng/iso_overview.html', context)
+
+
+class ReleaseListView(ListView):
+ model = Release
+
+
+class ReleaseDetailView(DetailView):
+ model = Release
+ slug_field = 'version'
+ slug_url_kwarg = 'version'
+
+
+def release_torrent(request, version):
+ release = get_object_or_404(Release, version=version)
+ if not release.torrent_data:
+ raise Http404
+ data = b64decode(release.torrent_data.encode('utf-8'))
+ response = HttpResponse(data, content_type='application/x-bittorrent')
+ # TODO: this is duplicated from Release.iso_url()
+ filename = 'archlinux-%s-dual.iso.torrent' % release.version
+ response['Content-Disposition'] = 'attachment; filename=%s' % filename
+ return response
# vim: set ts=4 sw=4 et: