From c0bf9e20660cfae7ea8994472555bba23398b598 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 24 Jul 2012 09:19:48 -0500
Subject: Remove custom utc_now() function, use django.utils.timezone.now()
This was around from the time when we handled timezones sanely and
Django did not; now that we are on 1.4 we no longer need our own code to
handle this.
Signed-off-by: Dan McGee
---
releng/management/commands/syncisos.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
(limited to 'releng')
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index 62f005ff..223c771b 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -4,8 +4,8 @@
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
@@ -54,9 +54,8 @@ def handle(self, *args, **options):
existing.active = True
existing.removed = None
existing.save()
- now = utc_now()
# 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:
--
cgit v1.2.3-54-g00ecf
From 76c37ce3acc7a4af0271c7535d4a33042f7749b5 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 24 Jul 2012 09:35:55 -0500
Subject: Replace deprecated direct_to_template() with render() shortcut
Now that Django actually provides a concise way to use a RequestContext
object without instantiating it, we can use that rather than the old
function-based generic view that worked well to do the same.
Additionally, these function-based generic views will be gone in Django
1.5, so might as well make the move now.
Signed-off-by: Dan McGee
---
devel/views.py | 15 +++++++--------
mirrors/views.py | 19 +++++++++----------
packages/views/__init__.py | 11 +++++------
packages/views/display.py | 13 +++++--------
packages/views/flag.py | 12 +++++-------
packages/views/signoff.py | 5 ++---
public/views.py | 14 +++++++-------
releng/views.py | 15 +++++++--------
retro/views.py | 4 ++--
todolists/views.py | 13 ++++++-------
visualize/views.py | 4 ++--
11 files changed, 57 insertions(+), 68 deletions(-)
(limited to 'releng')
diff --git a/devel/views.py b/devel/views.py
index 143b12bf..f877bc84 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -17,11 +17,10 @@
from django.db import transaction
from django.db.models import F, Count, Max
from django.http import Http404
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, render
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
from django.utils.encoding import force_unicode
from django.utils.http import http_date
from django.utils.timezone import now
@@ -88,7 +87,7 @@ def index(request):
'signoffs': signoffs
}
- return direct_to_template(request, 'devel/index.html', page_dict)
+ return render(request, 'devel/index.html', page_dict)
@login_required
def clock(request):
@@ -128,7 +127,7 @@ def clock(request):
'utc_now': current_time,
}
- response = direct_to_template(request, 'devel/clock.html', page_dict)
+ response = render(request, 'devel/clock.html', page_dict)
if not response.has_header('Expires'):
expire_time = current_time.replace(second=0, microsecond=0)
expire_time += timedelta(minutes=1)
@@ -178,7 +177,7 @@ def change_profile(request):
else:
form = ProfileForm(initial={'email': request.user.email})
profile_form = UserProfileForm(instance=request.user.get_profile())
- return direct_to_template(request, 'devel/profile.html',
+ return render(request, 'devel/profile.html',
{'form': form, 'profile_form': profile_form})
@login_required
@@ -301,7 +300,7 @@ def report(request, report_name, username=None):
'column_names': names,
'column_attrs': attrs,
}
- return direct_to_template(request, 'devel/packages.html', context)
+ return render(request, 'devel/packages.html', context)
class NewUserForm(forms.ModelForm):
@@ -399,7 +398,7 @@ def inner_save():
'title': 'Create User',
'submit_text': 'Create User'
}
- return direct_to_template(request, 'general_form.html', context)
+ return render(request, 'general_form.html', context)
@user_passes_test(lambda u: u.is_superuser)
def admin_log(request, username=None):
@@ -410,6 +409,6 @@ def admin_log(request, username=None):
'title': "Admin Action Log",
'log_user': user,
}
- return direct_to_template(request, 'devel/admin_log.html', context)
+ return render(request, 'devel/admin_log.html', context)
# vim: set ts=4 sw=4 et:
diff --git a/mirrors/views.py b/mirrors/views.py
index 8f092be7..400c084d 100644
--- a/mirrors/views.py
+++ b/mirrors/views.py
@@ -8,9 +8,8 @@
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q
from django.http import Http404, HttpResponse
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, render
from django.views.decorators.csrf import csrf_exempt
-from django.views.generic.simple import direct_to_template
from django_countries.countries import COUNTRIES
from .models import Mirror, MirrorUrl, MirrorProtocol, TIER_CHOICES
@@ -76,7 +75,7 @@ def generate_mirrorlist(request):
else:
form = MirrorlistForm()
- return direct_to_template(request, 'mirrors/mirrorlist_generate.html',
+ return render(request, 'mirrors/mirrorlist_generate.html',
{'mirrorlist_form': form})
@@ -142,10 +141,10 @@ def find_mirrors(request, countries=None, protocols=None, use_status=False,
urls = status_filter(urls)
template = 'mirrors/mirrorlist_status.txt'
- return direct_to_template(request, template, {
- 'mirror_urls': urls,
- },
- mimetype='text/plain')
+ context = {
+ 'mirror_urls': urls,
+ }
+ return render(request, template, context, content_type='text/plain')
def find_mirrors_simple(request, protocol):
@@ -161,7 +160,7 @@ def mirrors(request):
mirror_list = Mirror.objects.select_related().order_by('tier', 'country')
if not request.user.is_authenticated():
mirror_list = mirror_list.filter(public=True, active=True)
- return direct_to_template(request, 'mirrors/mirrors.html',
+ return render(request, 'mirrors/mirrors.html',
{'mirror_list': mirror_list})
@@ -180,7 +179,7 @@ def mirror_details(request, name):
all_urls = set(checked_urls).union(all_urls)
all_urls = sorted(all_urls, key=attrgetter('url'))
- return direct_to_template(request, 'mirrors/mirror_details.html',
+ return render(request, 'mirrors/mirror_details.html',
{'mirror': mirror, 'urls': all_urls})
@@ -217,7 +216,7 @@ def status(request, tier=None):
'error_logs': error_logs,
'tier': tier,
})
- return direct_to_template(request, 'mirrors/status.html', context)
+ return render(request, 'mirrors/status.html', context)
class MirrorStatusJSONEncoder(DjangoJSONEncoder):
diff --git a/packages/views/__init__.py b/packages/views/__init__.py
index fa67daa8..19ea9103 100644
--- a/packages/views/__init__.py
+++ b/packages/views/__init__.py
@@ -6,10 +6,9 @@
from django.contrib.auth.models import User
from django.core.cache import cache
from django.http import HttpResponse
-from django.shortcuts import redirect
+from django.shortcuts import redirect, render
from django.views.decorators.cache import cache_control
from django.views.decorators.http import require_GET, require_POST
-from django.views.generic.simple import direct_to_template
from main.models import Package, Arch
from ..models import PackageRelation
@@ -32,9 +31,9 @@ def opensearch(request):
else:
domain = "http://%s" % request.META['HTTP_HOST']
- return direct_to_template(request, 'packages/opensearch.xml',
+ return render(request, 'packages/opensearch.xml',
{'domain': domain},
- mimetype='application/opensearchdescription+xml')
+ content_type='application/opensearchdescription+xml')
@require_GET
@@ -115,7 +114,7 @@ def arch_differences(request):
'differences': differences,
'multilib_differences': multilib_diffs
}
- return direct_to_template(request, 'packages/differences.html', context)
+ return render(request, 'packages/differences.html', context)
@permission_required('main.change_package')
def stale_relations(request):
@@ -132,7 +131,7 @@ def stale_relations(request):
'missing_pkgbase': missing_pkgbase,
'wrong_permissions': wrong_permissions,
}
- return direct_to_template(request, 'packages/stale_relations.html', context)
+ return render(request, 'packages/stale_relations.html', context)
@permission_required('packages.delete_packagerelation')
@require_POST
diff --git a/packages/views/display.py b/packages/views/display.py
index 31f18c79..585e0e4e 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -6,7 +6,6 @@
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.timezone import now
-from django.views.generic.simple import direct_to_template
from main.models import Package, PackageFile, Arch, Repo
from mirrors.utils import get_mirror_url_for_download
@@ -33,8 +32,7 @@ def split_package_details(request, name, repo, arch):
'arch': arch,
'packages': pkgs,
}
- return direct_to_template(request, 'packages/packages_list.html',
- context)
+ return render(request, 'packages/packages_list.html', context)
CUTOFF = datetime.timedelta(days=60)
@@ -67,8 +65,7 @@ def details(request, name='', repo='', arch=''):
pkg = Package.objects.select_related(
'arch', 'repo', 'packager').get(pkgname=name,
repo__name__iexact=repo, arch__name=arch)
- return direct_to_template(request, 'packages/details.html',
- {'pkg': pkg, })
+ return render(request, 'packages/details.html', {'pkg': pkg})
except Package.DoesNotExist:
arch_obj = get_object_or_404(Arch, name=arch)
# for arch='any' packages, we can issue a redirect to them if we
@@ -111,7 +108,7 @@ def groups(request, arch=None):
'groups': grps,
'arch': arch,
}
- return direct_to_template(request, 'packages/groups.html', context)
+ return render(request, 'packages/groups.html', context)
def group_details(request, arch, name):
@@ -128,7 +125,7 @@ def group_details(request, arch, name):
'arch': arch,
'packages': pkgs,
}
- return direct_to_template(request, 'packages/packages_list.html', context)
+ return render(request, 'packages/packages_list.html', context)
def files(request, name, repo, arch):
@@ -153,7 +150,7 @@ def files(request, name, repo, arch):
'dir_count': dir_count,
}
template = 'packages/files.html'
- return direct_to_template(request, template, context)
+ return render(request, template, context)
def details_json(request, name, repo, arch):
diff --git a/packages/views/flag.py b/packages/views/flag.py
index f3db93b3..b9542a62 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -3,10 +3,9 @@
from django.contrib.auth.decorators import permission_required
from django.core.mail import send_mail
from django.db import transaction
-from django.shortcuts import get_object_or_404, redirect
+from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader, Context
from django.utils.timezone import now
-from django.views.generic.simple import direct_to_template
from django.views.decorators.cache import cache_page, never_cache
from ..models import FlagRequest
@@ -34,7 +33,7 @@ def __init__(self, *args, **kwargs):
@cache_page(3600)
def flaghelp(request):
- return direct_to_template(request, 'packages/flaghelp.html')
+ return render(request, 'packages/flaghelp.html')
@never_cache
@@ -43,8 +42,7 @@ def flag(request, name, repo, arch):
pkgname=name, repo__name__iexact=repo, arch__name=arch)
if pkg.flag_date is not None:
# already flagged. do nothing.
- return direct_to_template(request, 'packages/flagged.html',
- {'pkg': pkg})
+ return render(request, 'packages/flagged.html', {'pkg': pkg})
# find all packages from (hopefully) the same PKGBUILD
pkgs = Package.objects.normal().filter(
pkgbase=pkg.pkgbase, flag_date__isnull=True,
@@ -129,7 +127,7 @@ def perform_updates():
'packages': pkgs,
'form': form
}
- return direct_to_template(request, 'packages/flag.html', context)
+ return render(request, 'packages/flag.html', context)
def flag_confirmed(request, name, repo, arch):
pkg = get_object_or_404(Package,
@@ -142,7 +140,7 @@ def flag_confirmed(request, name, repo, arch):
context = {'package': pkg, 'packages': pkgs}
- return direct_to_template(request, 'packages/flag_confirmed.html', context)
+ return render(request, 'packages/flag_confirmed.html', context)
@permission_required('main.change_package')
def unflag(request, name, repo, arch):
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 7aa39106..56eb060c 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -10,7 +10,6 @@
from django.shortcuts import get_list_or_404, redirect, render
from django.utils.timezone import now
from django.views.decorators.cache import never_cache
-from django.views.generic.simple import direct_to_template
from main.models import Package, Arch, Repo
from ..models import SignoffSpecification, Signoff
@@ -28,7 +27,7 @@ def signoffs(request):
'arches': Arch.objects.all(),
'repo_names': sorted(set(g.target_repo for g in signoff_groups)),
}
- return direct_to_template(request, 'packages/signoffs.html', context)
+ return render(request, 'packages/signoffs.html', context)
@permission_required('main.change_package')
@never_cache
@@ -144,7 +143,7 @@ def signoff_options(request, name, repo, arch):
'package': package,
'form': form,
}
- return direct_to_template(request, 'packages/signoff_options.html', context)
+ return render(request, 'packages/signoff_options.html', context)
class SignoffJSONEncoder(DjangoJSONEncoder):
'''Base JSONEncoder extended to handle all serialization of all classes
diff --git a/public/views.py b/public/views.py
index 3ea8f841..3f68545c 100644
--- a/public/views.py
+++ b/public/views.py
@@ -5,8 +5,8 @@
from django.contrib.auth.models import User
from django.db.models import Count, Q
from django.http import Http404
+from django.shortcuts import render
from django.views.decorators.cache import cache_control
-from django.views.generic.simple import direct_to_template
from devel.models import MasterKey, PGPSignature
from main.models import Arch, Repo, Donor
@@ -21,7 +21,7 @@ def index(request):
'news_updates': News.objects.order_by('-postdate', '-id')[:15],
'pkg_updates': pkgs,
}
- return direct_to_template(request, 'public/index.html', context)
+ return render(request, 'public/index.html', context)
USER_LISTS = {
'devs': {
@@ -55,14 +55,14 @@ def userlist(request, user_type='devs'):
users = users.distinct()
context = USER_LISTS[user_type].copy()
context['users'] = users
- return direct_to_template(request, 'public/userlist.html', context)
+ return render(request, 'public/userlist.html', context)
@cache_control(max_age=300)
def donate(request):
context = {
'donors': Donor.objects.filter(visible=True).order_by('name'),
}
- return direct_to_template(request, 'public/donate.html', context)
+ return render(request, 'public/donate.html', context)
@cache_control(max_age=300)
def download(request):
@@ -76,7 +76,7 @@ def download(request):
'releng_pxeboot_url': settings.PXEBOOT_URL,
'mirror_urls': mirror_urls,
}
- return direct_to_template(request, 'public/download.html', context)
+ return render(request, 'public/download.html', context)
@cache_control(max_age=300)
def feeds(request):
@@ -84,7 +84,7 @@ def feeds(request):
'arches': Arch.objects.all(),
'repos': Repo.objects.all(),
}
- return direct_to_template(request, 'public/feeds.html', context)
+ return render(request, 'public/feeds.html', context)
@cache_control(max_age=300)
def keys(request):
@@ -113,6 +113,6 @@ def keys(request):
'active_users': users,
'signatures': signatures,
}
- return direct_to_template(request, 'public/keys.html', context)
+ return render(request, 'public/keys.html', context)
# vim: set ts=4 sw=4 et:
diff --git a/releng/views.py b/releng/views.py
index fc81d410..a1bc3b81 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -2,8 +2,7 @@
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.shortcuts import get_object_or_404, redirect, render
from .models import (Architecture, BootType, Bootloader, ClockChoice,
Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source,
@@ -73,7 +72,7 @@ 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)
@@ -145,7 +144,7 @@ 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 +153,7 @@ 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 +169,10 @@ 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')
@@ -192,6 +191,6 @@ def iso_overview(request):
context = {
'isos': isos
}
- return direct_to_template(request, 'releng/iso_overview.html', context)
+ return render(request, 'releng/iso_overview.html', context)
# vim: set ts=4 sw=4 et:
diff --git a/retro/views.py b/retro/views.py
index 3bc59e9f..31226deb 100644
--- a/retro/views.py
+++ b/retro/views.py
@@ -1,6 +1,6 @@
from django.http import Http404
+from django.shortcuts import render
from django.views.decorators.cache import cache_page
-from django.views.generic.simple import direct_to_template
RETRO_YEAR_MAP = {
@@ -26,6 +26,6 @@ def retro_homepage(request, year):
context = {
'year': year,
}
- return direct_to_template(request, 'retro/%s' % template, context)
+ return render(request, 'retro/%s' % template, context)
# vim: set ts=4 sw=4 et:
diff --git a/todolists/views.py b/todolists/views.py
index 580ec00f..c7ba2560 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -3,12 +3,11 @@
from django import forms
from django.http import HttpResponse
from django.core.mail import send_mail
-from django.shortcuts import get_list_or_404, get_object_or_404, redirect
+from django.shortcuts import get_list_or_404, get_object_or_404, redirect, render
from django.contrib.auth.decorators import login_required, permission_required
from django.db import transaction
from django.views.decorators.cache import never_cache
from django.views.generic import DeleteView
-from django.views.generic.simple import direct_to_template
from django.template import Context, loader
from main.models import Todolist, TodolistPkg, Package, Repo
@@ -54,7 +53,7 @@ def view(request, list_id):
# we don't hold onto the result, but the objects are the same here,
# so accessing maintainers in the template is now cheap
attach_maintainers(tp.pkg for tp in todolist.packages)
- return direct_to_template(request, 'todolists/view.html', {
+ return render(request, 'todolists/view.html', {
'list': todolist,
'svn_roots': svn_roots,
})
@@ -72,7 +71,7 @@ def list_pkgbases(request, list_id, svn_root):
@login_required
def todolist_list(request):
lists = get_annotated_todolists()
- return direct_to_template(request, 'todolists/list.html', {'lists': lists})
+ return render(request, 'todolists/list.html', {'lists': lists})
@permission_required('main.add_todolist')
@never_cache
@@ -92,7 +91,7 @@ def add(request):
'form': form,
'submit_text': 'Create List'
}
- return direct_to_template(request, 'general_form.html', page_dict)
+ return render(request, 'general_form.html', page_dict)
# TODO: this calls for transaction management and async emailing
@permission_required('main.change_todolist')
@@ -115,7 +114,7 @@ def edit(request, list_id):
'form': form,
'submit_text': 'Save List'
}
- return direct_to_template(request, 'general_form.html', page_dict)
+ return render(request, 'general_form.html', page_dict)
class DeleteTodolist(DeleteView):
model = Todolist
@@ -185,7 +184,7 @@ def public_list(request):
# total hackjob, but it makes this a lot less query-intensive.
all_pkgs = [tp for tl in todo_lists for tp in tl.packages]
attach_maintainers([tp.pkg for tp in all_pkgs])
- return direct_to_template(request, "todolists/public_list.html",
+ return render(request, "todolists/public_list.html",
{"todo_lists": todo_lists})
# vim: set ts=4 sw=4 et:
diff --git a/visualize/views.py b/visualize/views.py
index 95f8c314..44e60472 100644
--- a/visualize/views.py
+++ b/visualize/views.py
@@ -4,14 +4,14 @@
from django.contrib.auth.models import User
from django.db.models import Count, Sum, Q
from django.http import HttpResponse
+from django.shortcuts import render
from django.views.decorators.cache import cache_page
-from django.views.generic.simple import direct_to_template
from main.models import Package, Arch, Repo
from devel.models import MasterKey, PGPSignature
def index(request):
- return direct_to_template(request, 'visualize/index.html', {})
+ return render(request, 'visualize/index.html')
def arch_repo_data():
qs = Package.objects.select_related().values(
--
cgit v1.2.3-54-g00ecf
From bc5dab41a82ff980c51dd4e408983e32886721bb Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 13 Aug 2012 10:11:20 -0500
Subject: releng views code cleanup
Signed-off-by: Dan McGee
---
releng/views.py | 54 +++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 37 insertions(+), 17 deletions(-)
(limited to 'releng')
diff --git a/releng/views.py b/releng/views.py
index a1bc3b81..67b3cb4a 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -8,11 +8,13 @@
Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source,
Test)
+
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'))
@@ -24,29 +26,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 the " \
- "bugtracker (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 the "
+ "bugtracker (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
@@ -59,6 +66,7 @@ class Meta:
"modules": forms.CheckboxSelectMultiple(),
}
+
def submit_test_result(request):
if request.POST:
form = TestForm(request.POST)
@@ -74,6 +82,7 @@ def submit_test_result(request):
context = {'form': form}
return render(request, 'releng/add.html', context)
+
def calculate_option_overview(field_name):
field = Test._meta.get_field(field_name)
model = field.rel.to
@@ -108,6 +117,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
@@ -128,13 +138,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))
@@ -146,6 +162,7 @@ def test_results_overview(request):
}
return render(request, 'releng/results.html', context)
+
def test_results_iso(request, iso_id):
iso = get_object_or_404(Iso, pk=iso_id)
test_list = iso.test_set.select_related()
@@ -155,6 +172,7 @@ def test_results_iso(request, iso_id):
}
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():
raise Http404
@@ -171,9 +189,11 @@ def test_results_for(request, option, value):
}
return render(request, 'releng/result_list.html', context)
+
def submit_test_thanks(request):
return render(request, "releng/thanks.html", None)
+
def iso_overview(request):
isos = Iso.objects.all().order_by('-pk')
successes = dict(Iso.objects.values_list('pk').filter(
@@ -186,7 +206,7 @@ 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
--
cgit v1.2.3-54-g00ecf
From 2e9094630b5117575f899f068046c6e1021387a1 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 15 Aug 2012 08:25:00 -0500
Subject: PEP8 spacing in releng models file
Signed-off-by: Dan McGee
---
releng/models.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index 56187766..98454e95 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -4,6 +4,7 @@
from main.utils import set_created_field
+
class IsoOption(models.Model):
name = models.CharField(max_length=200)
@@ -13,10 +14,12 @@ def __unicode__(self):
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,37 +35,48 @@ def __unicode__(self):
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()
--
cgit v1.2.3-54-g00ecf
From 79b0ff49bd3319133502243ded4506fc2d56cd9e Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 4 Sep 2012 08:37:09 -0500
Subject: Use GenericIPAddressField for releng test IP address field
We were already using this on package flag requests, and we can support
IPv6 addresses here as well with minimal hassle.
Signed-off-by: Dan McGee
---
.../0003_auto__chg_field_test_ip_address.py | 99 ++++++++++++++++++++++
releng/models.py | 6 +-
2 files changed, 103 insertions(+), 2 deletions(-)
create mode 100644 releng/migrations/0003_auto__chg_field_test_ip_address.py
(limited to 'releng')
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/models.py b/releng/models.py
index 98454e95..d602e9e5 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -79,8 +79,10 @@ class Bootloader(IsoOption):
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)
--
cgit v1.2.3-54-g00ecf
From df208fb0bd3f8e5783a6e41f411e7aa80179b2b6 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Thu, 1 Nov 2012 09:20:38 -0500
Subject: Signal attachment cleanup
Signed-off-by: Dan McGee
---
releng/models.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index d602e9e5..bd178add 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -104,9 +104,9 @@ 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")
+
+for model in (Iso, Test):
+ pre_save.connect(set_created_field, sender=model,
+ dispatch_uid="releng.models")
# vim: set ts=4 sw=4 et:
--
cgit v1.2.3-54-g00ecf
From f7331a0eca351300685ebee494e810d8c82c35b1 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 20 Nov 2012 19:16:25 -0600
Subject: Add Release model to releng
This should prevent the need for monthly template updates from Pierre
and Thomas; best to just let them enter the data themselves and have it
show up on the website.
Signed-off-by: Dan McGee
---
public/views.py | 6 ++
releng/admin.py | 23 +++---
releng/migrations/0004_auto__add_release.py | 121 ++++++++++++++++++++++++++++
releng/models.py | 19 ++++-
templates/public/download.html | 22 +++--
5 files changed, 167 insertions(+), 24 deletions(-)
create mode 100644 releng/migrations/0004_auto__add_release.py
(limited to 'releng')
diff --git a/public/views.py b/public/views.py
index 3e15f9df..fefe032e 100644
--- a/public/views.py
+++ b/public/views.py
@@ -13,6 +13,7 @@
from main.models import Arch, Repo, Donor
from mirrors.models import MirrorUrl
from news.models import News
+from releng.models import Release
from .utils import get_recent_updates
@@ -77,12 +78,17 @@ def donate(request):
@cache_control(max_age=300)
def download(request):
+ try:
+ release = Release.objects.filter(available=True).latest()
+ except Release.DoesNotExist:
+ release = None
mirror_urls = MirrorUrl.objects.select_related('mirror').filter(
protocol__default=True,
mirror__public=True, mirror__active=True, mirror__isos=True)
sort_by = attrgetter('real_country.name', 'mirror.name')
mirror_urls = sorted(mirror_urls, key=sort_by)
context = {
+ 'release': release,
'releng_iso_url': settings.ISO_LIST_URL,
'releng_pxeboot_url': settings.PXEBOOT_URL,
'mirror_urls': mirror_urls,
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 .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/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/models.py b/releng/models.py
index bd178add..2f9a0785 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -105,7 +105,24 @@ class Test(models.Model):
comments = models.TextField(null=True, blank=True)
-for model in (Iso, Test):
+class Release(models.Model):
+ release_date = models.DateField(db_index=True)
+ version = models.CharField(max_length=50)
+ kernel_version = models.CharField(max_length=50, blank=True)
+ torrent_infohash = models.CharField(max_length=64, blank=True)
+ created = models.DateTimeField(editable=False)
+ available = models.BooleanField(default=True)
+ info = models.TextField('Public information', blank=True)
+
+ class Meta:
+ get_latest_by = 'release_date'
+ ordering = ('-release_date', '-version')
+
+ def __unicode__(self):
+ return self.version
+
+
+for model in (Iso, Test, Release):
pre_save.connect(set_created_field, sender=model,
dispatch_uid="releng.models")
diff --git a/templates/public/download.html b/templates/public/download.html
index 2fddd4e9..d0754e5b 100644
--- a/templates/public/download.html
+++ b/templates/public/download.html
@@ -7,7 +7,6 @@
{% block navbarclass %}anb-download{% endblock %}
{% block content %}
-{% with version="2012.11.01" kernel_version="3.6.4" torrent_infohash="f86f84c74edc90336f94f0837afa3071ada2aaa8" %}
Arch Linux Downloads
@@ -20,8 +19,8 @@
Release Info
can always be updated with `pacman -Syu`.
- - Current Release: {{ version }}
- - Included Kernel: {{ kernel_version }}
+ - Current Release: {{ release.version }}
+ - Included Kernel: {{ release.kernel_version }}
- Installation Guide
- Resources:
-{% endwith %}
{% endblock %}
--
cgit v1.2.3-54-g00ecf
From 402487b007e206b013ecbf8b3017dc1231f4bbbc Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 20 Nov 2012 19:33:49 -0600
Subject: Move some logic out of the templates to the Release model
This includes magnet URI generation, ISO paths, etc.
Signed-off-by: Dan McGee
---
releng/models.py | 18 ++++++++++++++++++
templates/public/download.html | 16 ++++++++--------
2 files changed, 26 insertions(+), 8 deletions(-)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index 2f9a0785..c591bc0f 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -1,3 +1,5 @@
+from urllib import urlencode
+
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.signals import pre_save
@@ -121,6 +123,22 @@ class Meta:
def __unicode__(self):
return 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,
+ 'tr': ("udp://tracker.archlinux.org:6969",
+ "http://tracker.archlinux.org:6969/announce"),
+ }
+ if self.torrent_infohash:
+ query['xt'] = "urn:btih:%s" % self.torrent_infohash
+ return "magnet:?%s" % urlencode(query, doseq=True)
+
for model in (Iso, Test, Release):
pre_save.connect(set_created_field, sender=model,
diff --git a/templates/public/download.html b/templates/public/download.html
index d0754e5b..5733ee94 100644
--- a/templates/public/download.html
+++ b/templates/public/download.html
@@ -44,9 +44,9 @@ BitTorrent Download (recommended)
download is finished, so you can seed it back to others.
A web-seed capable client is recommended for fastest download speeds.
Netboot
@@ -69,11 +69,11 @@ Checksums
File integrity checksums for the latest releases can be found below:
@@ -85,8 +85,8 @@ Checksums
{% else %}Worldwide
{% endif %}
{% endfor %}
--
cgit v1.2.3-54-g00ecf
From c81a9271b8bbc03418442c01d50a4c4945999e71 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 21 Nov 2012 00:10:12 -0500
Subject: Show release notes on downloads page
Signed-off-by: Dan McGee
---
releng/models.py | 6 ++++++
templates/public/download.html | 6 ++++++
2 files changed, 12 insertions(+)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index c591bc0f..b3ab1a31 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -1,8 +1,10 @@
+import markdown
from urllib import urlencode
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.utils import set_created_field
@@ -139,6 +141,10 @@ def magnet_uri(self):
query['xt'] = "urn:btih:%s" % self.torrent_infohash
return "magnet:?%s" % urlencode(query, doseq=True)
+ def info_html(self):
+ return mark_safe(markdown.markdown(
+ self.info, safe_mode=True, enable_attributes=False))
+
for model in (Iso, Test, Release):
pre_save.connect(set_created_field, sender=model,
diff --git a/templates/public/download.html b/templates/public/download.html
index 5733ee94..5f6f2d02 100644
--- a/templates/public/download.html
+++ b/templates/public/download.html
@@ -32,6 +32,12 @@ Release Info
+ {% if release.info %}
+ Release Notes
+
+ {{ release.info_html }}
+ {% endif %}
+
Existing Arch Users
If you are an existing Arch user, there is no need to download a new ISO
--
cgit v1.2.3-54-g00ecf
From a732d3cebcb8ff3170502b13d01ba90ac8efe26f Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Wed, 21 Nov 2012 00:54:20 -0500
Subject: Fix new magnet link generation
Apparently clients don't like urlencoded values in the magnet link, so
%3A isn't treated the same as ':'.
Signed-off-by: Dan McGee
---
releng/models.py | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index b3ab1a31..a22e01d6 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -1,5 +1,4 @@
import markdown
-from urllib import urlencode
from django.core.urlresolvers import reverse
from django.db import models
@@ -132,14 +131,14 @@ 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,
- 'tr': ("udp://tracker.archlinux.org:6969",
- "http://tracker.archlinux.org:6969/announce"),
- }
+ query = [
+ ('dn', "archlinux-%s-dual.iso" % self.version),
+ ('tr', "udp://tracker.archlinux.org:6969"),
+ ('tr', "http://tracker.archlinux.org:6969/announce"),
+ ]
if self.torrent_infohash:
- query['xt'] = "urn:btih:%s" % self.torrent_infohash
- return "magnet:?%s" % urlencode(query, doseq=True)
+ 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(
--
cgit v1.2.3-54-g00ecf
From 8cfaa39a0464f7ee35af76473d3e73ae587a6cb8 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 19 Jan 2013 10:55:58 -0600
Subject: Add more metadata to releng Release model
Add a file_size field which we will use in the RSS feed, and also add a
field for future storage of the torrent data itself.
Signed-off-by: Dan McGee
---
feeds.py | 4 +-
...se_file_size__add_field_release_torrent_data.py | 127 +++++++++++++++++++++
releng/models.py | 3 +
templates/public/download.html | 4 +-
4 files changed, 136 insertions(+), 2 deletions(-)
create mode 100644 releng/migrations/0005_auto__add_field_release_file_size__add_field_release_torrent_data.py
(limited to 'releng')
diff --git a/feeds.py b/feeds.py
index 98e1fb82..c68d3b0e 100644
--- a/feeds.py
+++ b/feeds.py
@@ -192,7 +192,9 @@ def item_enclosure_url(self, item):
proto = 'https'
return "%s://%s/%s.torrent" % (proto, domain, item.iso_url())
+ def item_enclosure_length(self, item):
+ return item.file_size or ""
+
item_enclosure_mime_type = 'application/x-bittorrent'
- item_enclosure_length = 0
# vim: set ts=4 sw=4 et:
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/models.py b/releng/models.py
index a22e01d6..9f091371 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -5,6 +5,7 @@
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
@@ -113,9 +114,11 @@ class Release(models.Model):
version = models.CharField(max_length=50)
kernel_version = models.CharField(max_length=50, blank=True)
torrent_infohash = models.CharField(max_length=64, 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'
diff --git a/templates/public/download.html b/templates/public/download.html
index 8ddbc23e..29490849 100644
--- a/templates/public/download.html
+++ b/templates/public/download.html
@@ -58,7 +58,9 @@ BitTorrent Download (recommended)
A web-seed capable client is recommended for fastest download speeds.
Download torrent for {{ release.version }}
- (Magnet)
+ (Magnet)
+ {% if release.file_size %}({{ release.file_size|filesizeformat }}){% endif %}
+
Netboot
--
cgit v1.2.3-54-g00ecf
From 1a7e5d22f1c4e948c624d26b4d8c1ed30189acfe Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 19 Jan 2013 12:06:47 -0600
Subject: Add basic release list and details views
Signed-off-by: Dan McGee
---
feeds.py | 5 +---
releng/models.py | 3 +++
releng/urls.py | 6 +++++
releng/views.py | 13 ++++++++++-
templates/releng/release_detail.html | 24 ++++++++++++++++++++
templates/releng/release_list.html | 44 ++++++++++++++++++++++++++++++++++++
6 files changed, 90 insertions(+), 5 deletions(-)
create mode 100644 templates/releng/release_detail.html
create mode 100644 templates/releng/release_list.html
(limited to 'releng')
diff --git a/feeds.py b/feeds.py
index c68d3b0e..9721f41c 100644
--- a/feeds.py
+++ b/feeds.py
@@ -175,9 +175,6 @@ def item_title(self, item):
def item_description(self, item):
return item.info_html()
- # TODO: individual release pages
- item_link = '/download/'
-
def item_pubdate(self, item):
return datetime.combine(item.release_date, time()).replace(tzinfo=utc)
@@ -185,7 +182,7 @@ def item_guid(self, item):
# http://diveintomark.org/archives/2004/05/28/howto-atom-id
date = item.release_date
return 'tag:%s,%s:%s' % (Site.objects.get_current().domain,
- date.strftime('%Y-%m-%d'), item.iso_url())
+ date.strftime('%Y-%m-%d'), item.get_absolute_url())
def item_enclosure_url(self, item):
domain = Site.objects.get_current().domain
diff --git a/releng/models.py b/releng/models.py
index 9f091371..8bc54def 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -127,6 +127,9 @@ class Meta:
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
diff --git a/releng/urls.py b/releng/urls.py
index 8d1b8f24..8413d318 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'),
@@ -11,5 +13,9 @@
urlpatterns = patterns('',
(r'^feedback/', include(feedback_patterns)),
+ (r'^releases/$',
+ ReleaseListView.as_view(), {}, 'releng-release-list'),
+ (r'^releases/(?P[-.\w]+)/$',
+ ReleaseDetailView.as_view(), {}, 'releng-release-detail'),
)
# vim: set ts=4 sw=4 et:
diff --git a/releng/views.py b/releng/views.py
index 67b3cb4a..6c49275f 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -3,10 +3,11 @@
from django.db.models import Count, Max
from django.http import Http404
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):
@@ -213,4 +214,14 @@ def iso_overview(request):
}
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'
+
# vim: set ts=4 sw=4 et:
diff --git a/templates/releng/release_detail.html b/templates/releng/release_detail.html
new file mode 100644
index 00000000..fec9ce2b
--- /dev/null
+++ b/templates/releng/release_detail.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+{% block title %}Arch Linux - Release: {{ release.version }}{% endblock %}
+
+{% block content %}
+
+
{{ release.version }}
+
+
+ - Release Date: {{ release.release_date|date }}
+ {% if release.kernel_version %}- Kernel Version: {{ release.kernel_version }}
{% endif %}
+ - Available: {{ release.available|yesno }}
+ {% if release.available %}- Torrent
{% endif %}
+ {% if release.available %}- Magnet
{% endif %}
+ - Download Size: {% if release.file_size %}{{ release.file_size|filesizeformat }}{% else %}Unknown{% endif %}
+
+
+ {% if release.info %}
+
Release Notes
+
+
{{ release.info_html }}
+ {% endif %}
+
+{% endblock %}
diff --git a/templates/releng/release_list.html b/templates/releng/release_list.html
new file mode 100644
index 00000000..1657249f
--- /dev/null
+++ b/templates/releng/release_list.html
@@ -0,0 +1,44 @@
+{% extends "base.html" %}
+{% load url from future %}
+
+{% block title %}Arch Linux - Releases{% endblock %}
+
+{% block head %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
Releases
+
+
+
+
+ Release Date |
+ Version |
+ Kernel Version |
+ Available? |
+ Torrent |
+ Magnet |
+ Download Size |
+
+
+
+ {% for item in release_list %}
+
+ {{ item.release_date|date }} |
+ {{ item.version }} |
+ {{ item.kernel_version|default:"" }} |
+ {{ item.available|yesno }} |
+ {% if item.available %}Torrent{% endif %} |
+ {% if item.available %}Magnet{% endif %} |
+ {% if item.file_size %}{{ item.file_size|filesizeformat }}{% endif %} |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
--
cgit v1.2.3-54-g00ecf
From 71c0c7453a5bc28b6e6576fe4f1351139f33ade5 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 19 Jan 2013 13:08:06 -0600
Subject: Implement torrent data parsing and extraction via bencode
This allows uploading of the actual torrent file itself into the webapp
and then pulling the relevant pieces of information out of it.
Signed-off-by: Dan McGee
---
releng/models.py | 33 +++++++++++++++++++++++++++++++++
requirements.txt | 3 ++-
requirements_prod.txt | 3 ++-
templates/releng/release_detail.html | 24 +++++++++++++++++++++---
4 files changed, 58 insertions(+), 5 deletions(-)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index 8bc54def..c7d26966 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -1,4 +1,9 @@
+from base64 import b64decode
+from bencode import bdecode, bencode
+from datetime import datetime
+import hashlib
import markdown
+from pytz import utc
from django.core.urlresolvers import reverse
from django.db import models
@@ -150,6 +155,34 @@ 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)
+ except TypeError:
+ 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,
diff --git a/requirements.txt b/requirements.txt
index 1d5b068e..ae58ee58 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,7 @@
Django==1.4.3
Markdown==2.2.1
South==0.7.6
+bencode==1.0
django-countries==1.5
pgpdump==1.4
-pytz>=2012h
+pytz>=2012j
diff --git a/requirements_prod.txt b/requirements_prod.txt
index f84f46d8..ee1b17ad 100644
--- a/requirements_prod.txt
+++ b/requirements_prod.txt
@@ -1,9 +1,10 @@
Django==1.4.3
Markdown==2.2.1
South==0.7.6
+bencode==1.0
django-countries==1.5
pgpdump==1.4
psycopg2==2.4.6
pyinotify==0.9.4
python-memcached==1.48
-pytz>=2012h
+pytz>=2012j
diff --git a/templates/releng/release_detail.html b/templates/releng/release_detail.html
index fec9ce2b..f4de9e52 100644
--- a/templates/releng/release_detail.html
+++ b/templates/releng/release_detail.html
@@ -9,9 +9,10 @@ {{ release.version }}
Release Date: {{ release.release_date|date }}
{% if release.kernel_version %}Kernel Version: {{ release.kernel_version }}{% endif %}
Available: {{ release.available|yesno }}
- {% if release.available %}Torrent{% endif %}
- {% if release.available %}Magnet{% endif %}
+ {% if release.available %}Download: Torrent,
+ Magnet{% endif %}
+ {% if release.torrent_infohash %}Torrent Info Hash: {{ release.torrent_infohash }}{% endif %}
Download Size: {% if release.file_size %}{{ release.file_size|filesizeformat }}{% else %}Unknown{% endif %}
@@ -20,5 +21,22 @@ Release Notes
{{ release.info_html }}
{% endif %}
+
+ {% if release.torrent_data %}{% with release.torrent as torrent %}
+ Torrent Information
+
+
+ - Comment: {{ torrent.comment }}
+ - Creation Date: {{ torrent.creation_date|date:"DATETIME_FORMAT" }} UTC
+ - Created By: {{ torrent.created_by }}
+ - Announce URL: {{ torrent.announce }}
+ - File Name: {{ torrent.file_name }}
+ - File Length: {{ torrent.file_length|filesizeformat }}
+ - Piece Count: {{ torrent.piece_count }} pieces
+ - Piece Length: {{ torrent.piece_length|filesizeformat }}
+ - Computed Info Hash: {{ torrent.info_hash }}
+ - URL List Length: {{ torrent.url_list|length }} URLs
+
+ {% endwith %}{% endif %}
{% endblock %}
--
cgit v1.2.3-54-g00ecf
From 4d52242f4b5a20a591b5bd44cc0dc12f15a9c92c Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 19 Jan 2013 17:19:27 -0600
Subject: Mark release version string as unique
It should be unique anyway, but it is especially important now that we
are using it in URL patterns for lookup.
Signed-off-by: Dan McGee
---
.../0006_auto__add_unique_release_version.py | 117 +++++++++++++++++++++
releng/models.py | 2 +-
2 files changed, 118 insertions(+), 1 deletion(-)
create mode 100644 releng/migrations/0006_auto__add_unique_release_version.py
(limited to 'releng')
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/models.py b/releng/models.py
index c7d26966..a0dd57aa 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -116,7 +116,7 @@ class Test(models.Model):
class Release(models.Model):
release_date = models.DateField(db_index=True)
- version = models.CharField(max_length=50)
+ version = models.CharField(max_length=50, unique=True)
kernel_version = models.CharField(max_length=50, blank=True)
torrent_infohash = models.CharField(max_length=64, blank=True)
file_size = PositiveBigIntegerField(null=True, blank=True)
--
cgit v1.2.3-54-g00ecf
From b642c93aff6bd22013615ae8b51b7a02763e261c Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 19 Jan 2013 17:38:54 -0600
Subject: Add a view to download the torrent available for a given release
Signed-off-by: Dan McGee
---
releng/urls.py | 15 +++++++++++----
releng/views.py | 16 +++++++++++++++-
2 files changed, 26 insertions(+), 5 deletions(-)
(limited to 'releng')
diff --git a/releng/urls.py b/releng/urls.py
index 8413d318..76c36345 100644
--- a/releng/urls.py
+++ b/releng/urls.py
@@ -11,11 +11,18 @@
(r'^iso/overview/$', 'iso_overview', {}, 'releng-iso-overview'),
)
-urlpatterns = patterns('',
- (r'^feedback/', include(feedback_patterns)),
- (r'^releases/$',
+releases_patterns = patterns('releng.views',
+ (r'^$',
ReleaseListView.as_view(), {}, 'releng-release-list'),
- (r'^releases/(?P[-.\w]+)/$',
+ (r'^(?P[-.\w]+)/$',
ReleaseDetailView.as_view(), {}, 'releng-release-detail'),
+ (r'^(?P[-.\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 6c49275f..ad4b07d1 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -1,7 +1,9 @@
+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.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import DetailView, ListView
@@ -224,4 +226,16 @@ class ReleaseDetailView(DetailView):
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)
+ 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:
--
cgit v1.2.3-54-g00ecf
From 1a55c0f0bc8359f85f6c23d69cdf3ca05a4d25a2 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 28 Jan 2013 13:50:33 -0700
Subject: Don't error on empty torrent data
Signed-off-by: Dan McGee
---
releng/models.py | 2 ++
1 file changed, 2 insertions(+)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index a0dd57aa..19040905 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -160,6 +160,8 @@ def torrent(self):
data = b64decode(self.torrent_data)
except TypeError:
return None
+ if not data:
+ return None
data = bdecode(data)
# transform the data into a template-friendly dict
info = data.get('info', {})
--
cgit v1.2.3-54-g00ecf
From 7d4a8b9adf353d7adce4c3c22101e774092eb4de Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 28 Jan 2013 14:06:41 -0700
Subject: Add MD5 and SHA1 fields for releases
Signed-off-by: Dan McGee
---
...dd_field_release_md5__add_field_release_sha1.py | 122 +++++++++++++++++++++
releng/models.py | 4 +-
templates/releng/release_detail.html | 2 +
3 files changed, 127 insertions(+), 1 deletion(-)
create mode 100644 releng/migrations/0007_auto__add_field_release_md5__add_field_release_sha1.py
(limited to 'releng')
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 19040905..dd034e7f 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -118,7 +118,9 @@ 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=64, 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)
diff --git a/templates/releng/release_detail.html b/templates/releng/release_detail.html
index f4de9e52..ea54be66 100644
--- a/templates/releng/release_detail.html
+++ b/templates/releng/release_detail.html
@@ -13,6 +13,8 @@ {{ release.version }}
title="Download torrent for {{ release.version }}">Torrent,
Magnet{% endif %}
{% if release.torrent_infohash %}Torrent Info Hash: {{ release.torrent_infohash }}{% endif %}
+ {% if release.md5_sum %}MD5: {{ release.md5_sum }}{% endif %}
+ {% if release.sha1_sum %}SHA1: {{ release.sha1_sum }}{% endif %}
Download Size: {% if release.file_size %}{{ release.file_size|filesizeformat }}{% else %}Unknown{% endif %}
--
cgit v1.2.3-54-g00ecf
From bc539b6ed174fed1545aabaa4ceb7a7f925cbbed Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Mon, 28 Jan 2013 14:13:53 -0700
Subject: Extract torrent trackers into a settings variable
This allows them to be overridden and changed in a central location,
like we do with the SVN URL, PXE boot URL, etc.
Signed-off-by: Dan McGee
---
releng/models.py | 5 +++--
settings.py | 6 ++++++
2 files changed, 9 insertions(+), 2 deletions(-)
(limited to 'releng')
diff --git a/releng/models.py b/releng/models.py
index dd034e7f..b95f7d52 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -5,6 +5,7 @@
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
@@ -146,9 +147,9 @@ def iso_url(self):
def magnet_uri(self):
query = [
('dn', "archlinux-%s-dual.iso" % self.version),
- ('tr', "udp://tracker.archlinux.org:6969"),
- ('tr', "http://tracker.archlinux.org:6969/announce"),
]
+ 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])
diff --git a/settings.py b/settings.py
index dbc06159..c856bf57 100644
--- a/settings.py
+++ b/settings.py
@@ -164,6 +164,12 @@
# community bit on the end, repo.svn_root is appended)
SVN_BASE_URL = 'svn://svn.archlinux.org/'
+# Trackers used for ISO download magnet links
+TORRENT_TRACKERS = (
+ 'udp://tracker.archlinux.org:6969',
+ 'http://tracker.archlinux.org:6969/announce',
+)
+
## Import local settings
from local_settings import *
--
cgit v1.2.3-54-g00ecf
From 5566d43a7734f6bb2f48d5d511351da12ddc5cc1 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Sat, 9 Feb 2013 16:43:40 -0600
Subject: Use 'update_fields' model.save() kwarg
This was added in Django 1.5 and allows saving only a subset of a
model's fields. It makes sense in a few cases to utilize it.
Signed-off-by: Dan McGee
---
mirrors/management/commands/mirrorresolv.py | 2 +-
packages/views/signoff.py | 2 +-
releng/management/commands/syncisos.py | 2 +-
todolists/views.py | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
(limited to 'releng')
diff --git a/mirrors/management/commands/mirrorresolv.py b/mirrors/management/commands/mirrorresolv.py
index 0370f8ed..a6c2523e 100644
--- a/mirrors/management/commands/mirrorresolv.py
+++ b/mirrors/management/commands/mirrorresolv.py
@@ -53,7 +53,7 @@ def resolve_mirrors():
newvals = (mirrorurl.has_ipv4, mirrorurl.has_ipv6)
if newvals != oldvals:
logger.debug("values changed for %s", mirrorurl)
- mirrorurl.save(force_update=True)
+ mirrorurl.save(update_fields=('has_ipv4', 'has_ipv6'))
except socket.error, e:
logger.warn("error resolving %s: %s", mirrorurl.hostname, e)
diff --git a/packages/views/signoff.py b/packages/views/signoff.py
index 17f3095c..c37aa0fc 100644
--- a/packages/views/signoff.py
+++ b/packages/views/signoff.py
@@ -45,7 +45,7 @@ def signoff_package(request, name, repo, arch, revoke=False):
except Signoff.DoesNotExist:
raise Http404
signoff.revoked = now()
- signoff.save()
+ signoff.save(update_fields=('revoked',))
created = False
else:
# ensure we should even be accepting signoffs
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index 223c771b..c9f61964 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -53,7 +53,7 @@ def handle(self, *args, **options):
if not existing.active:
existing.active = True
existing.removed = None
- existing.save()
+ 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())
diff --git a/todolists/views.py b/todolists/views.py
index f333728a..9935987b 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -47,7 +47,7 @@ def flag(request, slug, pkg_id):
else:
tlpkg.status = TodolistPackage.INCOMPLETE
tlpkg.user = request.user
- tlpkg.save()
+ tlpkg.save(update_fields=('status', 'user', 'last_modified'))
if request.is_ajax():
data = {
'status': tlpkg.get_status_display(),
--
cgit v1.2.3-54-g00ecf
From b7b24740640e24883cd17fd683e1d465fbb343f8 Mon Sep 17 00:00:00 2001
From: Dan McGee
Date: Tue, 16 Apr 2013 22:12:01 -0500
Subject: Various minor code cleanups and fixes
Most of these were suggested by PyCharm, and include everything from
little syntax issues and other bad smells to dead or bad code.
Signed-off-by: Dan McGee
---
devel/management/commands/pgp_import.py | 1 +
devel/models.py | 1 -
devel/utils.py | 2 +-
devel/views.py | 2 +-
main/log.py | 1 -
main/migrations/0029_fill_in_repo_data.py | 1 -
main/models.py | 12 +++++-------
main/utils.py | 1 -
mirrors/management/commands/mirrorcheck.py | 12 +++---------
mirrors/models.py | 2 +-
mirrors/utils.py | 6 +++---
packages/migrations/0002_populate_package_relation.py | 2 --
packages/templatetags/package_extras.py | 4 ++--
packages/utils.py | 2 +-
packages/views/display.py | 2 --
packages/views/flag.py | 3 +--
public/views.py | 1 -
releng/management/commands/syncisos.py | 2 +-
releng/models.py | 2 +-
releng/views.py | 2 +-
retro/templates/retro/index-20030330.html | 1 -
sitestatic/archweb.js | 1 -
todolists/utils.py | 1 -
todolists/views.py | 1 -
visualize/static/visualize.js | 2 +-
25 files changed, 23 insertions(+), 44 deletions(-)
(limited to 'releng')
diff --git a/devel/management/commands/pgp_import.py b/devel/management/commands/pgp_import.py
index 10e6cfcb..b1f29d77 100644
--- a/devel/management/commands/pgp_import.py
+++ b/devel/management/commands/pgp_import.py
@@ -95,6 +95,7 @@ def parse_keydata(data):
# parse all of the output from our successful GPG command
logger.info("parsing command output")
+ node = None
for line in data.split('\n'):
parts = line.split(':')
if parts[0] == 'pub':
diff --git a/devel/models.py b/devel/models.py
index 67de40a6..4354e0f2 100644
--- a/devel/models.py
+++ b/devel/models.py
@@ -4,7 +4,6 @@
from django.db import models
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
-from django.utils.timezone import now
from django_countries import CountryField
from .fields import PGPKeyField
diff --git a/devel/utils.py b/devel/utils.py
index e8e3a6c4..340841f5 100644
--- a/devel/utils.py
+++ b/devel/utils.py
@@ -131,7 +131,7 @@ def find(self, userstring):
self.username_email, self.user_name)
for matcher in find_methods:
user = matcher(name, email)
- if user != None:
+ if user is not None:
break
self.cache[userstring] = user
diff --git a/devel/views.py b/devel/views.py
index 61c1e568..4258ea7f 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -34,7 +34,7 @@
@login_required
def index(request):
'''the developer dashboard'''
- if(request.user.is_authenticated()):
+ if request.user.is_authenticated():
inner_q = PackageRelation.objects.filter(user=request.user)
else:
inner_q = PackageRelation.objects.none()
diff --git a/main/log.py b/main/log.py
index 63634874..5c745cc8 100644
--- a/main/log.py
+++ b/main/log.py
@@ -46,7 +46,6 @@ def filter(self, record):
trace = '\n'.join(traceback.format_exception(*record.exc_info))
key = md5(trace).hexdigest()
- duplicate = False
cache = self.cache_module.cache
# Test if the cache works
diff --git a/main/migrations/0029_fill_in_repo_data.py b/main/migrations/0029_fill_in_repo_data.py
index 0887b28c..7da6b1c4 100644
--- a/main/migrations/0029_fill_in_repo_data.py
+++ b/main/migrations/0029_fill_in_repo_data.py
@@ -7,7 +7,6 @@
class Migration(DataMigration):
def forwards(self, orm):
- "Write your forwards methods here."
orm.Repo.objects.filter(name__istartswith='community').update(bugs_project=5, svn_root='community')
orm.Repo.objects.filter(name__iexact='multilib').update(bugs_project=5, svn_root='community')
diff --git a/main/models.py b/main/models.py
index 89215f05..24aeed89 100644
--- a/main/models.py
+++ b/main/models.py
@@ -7,7 +7,6 @@
from django.db.models import Q
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
-from django.utils.timezone import now
from .fields import PositiveBigIntegerField
from .utils import set_created_field
@@ -140,7 +139,7 @@ def get_full_url(self, proto='https'):
@property
def signature(self):
try:
- data = b64decode(self.pgp_signature)
+ data = b64decode(self.pgp_signature.encode('utf-8'))
except TypeError:
return None
if not data:
@@ -274,7 +273,6 @@ def get_depends(self):
Packages will match the testing status of this package if possible.
"""
deps = []
- arches = None
# TODO: we can use list comprehension and an 'in' query to make this
# more effective
for dep in self.depends.all():
@@ -400,13 +398,13 @@ def elsewhere(self):
'''attempt to locate this package anywhere else, regardless of
architecture or repository. Excludes this package from the list.'''
names = [self.pkgname]
- if self.pkgname.startswith('lib32-'):
+ if self.pkgname.startswith(u'lib32-'):
names.append(self.pkgname[6:])
- elif self.pkgname.endswith('-multilib'):
+ elif self.pkgname.endswith(u'-multilib'):
names.append(self.pkgname[:-9])
else:
- names.append('lib32-' + self.pkgname)
- names.append(self.pkgname + '-multilib')
+ names.append(u'lib32-' + self.pkgname)
+ names.append(self.pkgname + u'-multilib')
return Package.objects.normal().filter(
pkgname__in=names).exclude(id=self.id).order_by(
'arch__name', 'repo__name')
diff --git a/main/utils.py b/main/utils.py
index 8394e5cd..9ee8db58 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -3,7 +3,6 @@
except ImportError:
import pickle
-from datetime import datetime
import hashlib
from django.core.cache import cache
diff --git a/mirrors/management/commands/mirrorcheck.py b/mirrors/management/commands/mirrorcheck.py
index d6de8f22..e7dd7b49 100644
--- a/mirrors/management/commands/mirrorcheck.py
+++ b/mirrors/management/commands/mirrorcheck.py
@@ -106,19 +106,13 @@ def parse_lastsync(log, data):
def check_mirror_url(mirror_url, location, timeout):
- if location:
- if location.family == socket.AF_INET6:
- ipopt = '--ipv6'
- elif location.family == socket.AF_INET:
- ipopt = '--ipv4'
-
url = mirror_url.url + 'lastsync'
logger.info("checking URL %s", url)
log = MirrorLog(url=mirror_url, check_time=now(), location=location)
headers = {'User-Agent': 'archweb/1.0'}
req = urllib2.Request(url, None, headers)
+ start = time.time()
try:
- start = time.time()
result = urllib2.urlopen(req, timeout=timeout)
data = result.read()
result.close()
@@ -147,12 +141,12 @@ def check_mirror_url(mirror_url, location, timeout):
elif isinstance(e.reason, socket.error):
log.error = e.reason.args[1]
logger.debug("failed: %s, %s", url, log.error)
- except HTTPException as e:
+ except HTTPException:
# e.g., BadStatusLine
log.is_success = False
log.error = "Exception in processing HTTP request."
logger.debug("failed: %s, %s", url, log.error)
- except socket.timeout as e:
+ except socket.timeout:
log.is_success = False
log.error = "Connection timed out."
logger.debug("failed: %s, %s", url, log.error)
diff --git a/mirrors/models.py b/mirrors/models.py
index 791b0078..d8ac7952 100644
--- a/mirrors/models.py
+++ b/mirrors/models.py
@@ -92,7 +92,7 @@ def clean(self):
families = self.address_families()
self.has_ipv4 = socket.AF_INET in families
self.has_ipv6 = socket.AF_INET6 in families
- except socket.error as e:
+ except socket.error:
# We don't fail in this case; we'll just set both to False
self.has_ipv4 = False
self.has_ipv6 = False
diff --git a/mirrors/utils.py b/mirrors/utils.py
index 5a8bbf5d..531cf005 100644
--- a/mirrors/utils.py
+++ b/mirrors/utils.py
@@ -1,13 +1,13 @@
from datetime import timedelta
from django.db import connection
-from django.db.models import Avg, Count, Max, Min, StdDev
+from django.db.models import Count, Max, Min
from django.utils.dateparse import parse_datetime
from django.utils.timezone import now
from django_countries.fields import Country
from main.utils import cache_function, database_vendor
-from .models import MirrorLog, MirrorProtocol, MirrorUrl
+from .models import MirrorLog, MirrorUrl
DEFAULT_CUTOFF = timedelta(hours=24)
@@ -165,7 +165,7 @@ def get_mirror_errors(cutoff=DEFAULT_CUTOFF, mirror_id=None):
).order_by('-last_occurred', '-error_count')
if mirror_id:
- urls = urls.filter(mirror_id=mirror_id)
+ errors = errors.filter(url__mirror_id=mirror_id)
errors = list(errors)
for err in errors:
diff --git a/packages/migrations/0002_populate_package_relation.py b/packages/migrations/0002_populate_package_relation.py
index 738e068f..b0d32c7a 100644
--- a/packages/migrations/0002_populate_package_relation.py
+++ b/packages/migrations/0002_populate_package_relation.py
@@ -11,7 +11,6 @@ class Migration(DataMigration):
)
def forwards(self, orm):
- "Write your forwards methods here."
# search by pkgbase first and insert those records
qs = orm['main.Package'].objects.exclude(maintainer=None).exclude(
pkgbase=None).distinct().values('pkgbase', 'maintainer_id')
@@ -29,7 +28,6 @@ def forwards(self, orm):
defaults={'user_id': row['maintainer_id']})
def backwards(self, orm):
- "Write your backwards methods here."
if not db.dry_run:
orm.PackageRelation.objects.all().delete()
pass
diff --git a/packages/templatetags/package_extras.py b/packages/templatetags/package_extras.py
index f14fab1e..ef0e1aea 100644
--- a/packages/templatetags/package_extras.py
+++ b/packages/templatetags/package_extras.py
@@ -53,10 +53,10 @@ def do_buildsortqs(parser, token):
tagname, sortfield = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
- "%r tag requires a single argument" % tagname)
+ "%r tag requires a single argument" % token)
if not (sortfield[0] == sortfield[-1] and sortfield[0] in ('"', "'")):
raise template.TemplateSyntaxError(
- "%r tag's argument should be in quotes" % tagname)
+ "%r tag's argument should be in quotes" % token)
return BuildQueryStringNode(sortfield[1:-1])
diff --git a/packages/utils.py b/packages/utils.py
index a4217fbd..4f3b8665 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -391,7 +391,7 @@ def signoffs_id_query(model, repos):
"""
cursor = connection.cursor()
# query pre-process- fill in table name and placeholders for IN
- repo_sql = ','.join(['%s' for r in repos])
+ repo_sql = ','.join(['%s' for _ in repos])
sql = sql % (model._meta.db_table, repo_sql, repo_sql)
repo_ids = [r.pk for r in repos]
# repo_ids are needed twice, so double the array
diff --git a/packages/views/display.py b/packages/views/display.py
index 87424483..021c7ed8 100644
--- a/packages/views/display.py
+++ b/packages/views/display.py
@@ -228,8 +228,6 @@ def download(request, name, repo, arch):
if pkg.arch.agnostic:
# grab the first non-any arch to fake the download path
arch = Arch.objects.exclude(agnostic=True)[0].name
- values = {
- }
url = '{host}{repo}/os/{arch}/{filename}'.format(host=url.url,
repo=pkg.repo.name.lower(), arch=arch, filename=pkg.filename)
return redirect(url)
diff --git a/packages/views/flag.py b/packages/views/flag.py
index 5c76e1d5..39cdcef8 100644
--- a/packages/views/flag.py
+++ b/packages/views/flag.py
@@ -110,7 +110,7 @@ def perform_updates():
subject = '%s package [%s] marked out-of-date' % \
(pkg.repo.name, pkg.pkgname)
for maint in maints:
- if maint.userprofile.notify == True:
+ if maint.userprofile.notify is True:
toemail.append(maint.email)
if toemail:
@@ -133,7 +133,6 @@ def perform_updates():
return redirect('package-flag-confirmed', name=name, repo=repo,
arch=arch)
else:
- initial = {}
form = FlagForm(authenticated=authenticated)
context = {
diff --git a/public/views.py b/public/views.py
index 22cb8759..39273396 100644
--- a/public/views.py
+++ b/public/views.py
@@ -125,7 +125,6 @@ def keys(request):
master_keys = MasterKey.objects.select_related('owner', 'revoker',
'owner__userprofile', 'revoker__userprofile').filter(
revoked__isnull=True)
- master_key_ids = frozenset(key.pgp_key[-16:] for key in master_keys)
sig_counts = PGPSignature.objects.filter(not_expired, valid=True,
signee__in=user_key_ids).order_by().values_list('signer').annotate(
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
index c9f61964..f182cc33 100644
--- a/releng/management/commands/syncisos.py
+++ b/releng/management/commands/syncisos.py
@@ -20,7 +20,7 @@ def handle_starttag(self, tag, attrs):
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):
diff --git a/releng/models.py b/releng/models.py
index b95f7d52..5ee2f325 100644
--- a/releng/models.py
+++ b/releng/models.py
@@ -160,7 +160,7 @@ def info_html(self):
def torrent(self):
try:
- data = b64decode(self.torrent_data)
+ data = b64decode(self.torrent_data.encode('utf-8'))
except TypeError:
return None
if not data:
diff --git a/releng/views.py b/releng/views.py
index ad4b07d1..b1c76a4a 100644
--- a/releng/views.py
+++ b/releng/views.py
@@ -231,7 +231,7 @@ 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)
+ 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
diff --git a/retro/templates/retro/index-20030330.html b/retro/templates/retro/index-20030330.html
index 449731af..51cc8ba3 100644
--- a/retro/templates/retro/index-20030330.html
+++ b/retro/templates/retro/index-20030330.html
@@ -232,7 +232,6 @@
-
diff --git a/sitestatic/archweb.js b/sitestatic/archweb.js
index dda22d9e..aa225f5f 100644
--- a/sitestatic/archweb.js
+++ b/sitestatic/archweb.js
@@ -146,7 +146,6 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
(function($) {
$.fn.enableCheckboxRangeSelection = function() {
var lastCheckbox = null,
- lastElement = null,
spec = this;
spec.unbind("click.checkboxrange");
diff --git a/todolists/utils.py b/todolists/utils.py
index 51a75a3c..7b98c887 100644
--- a/todolists/utils.py
+++ b/todolists/utils.py
@@ -1,5 +1,4 @@
from django.db import connections, router
-from django.db.models import Count
from .models import Todolist, TodolistPackage
from packages.models import Package
diff --git a/todolists/views.py b/todolists/views.py
index 7636d38e..d5b39934 100644
--- a/todolists/views.py
+++ b/todolists/views.py
@@ -9,7 +9,6 @@
from django.views.decorators.cache import never_cache
from django.views.generic import DeleteView
from django.template import Context, loader
-from django.template.defaultfilters import slugify
from django.utils.timezone import now
from main.models import Package, Repo
diff --git a/visualize/static/visualize.js b/visualize/static/visualize.js
index 7e240d44..5004fe6c 100644
--- a/visualize/static/visualize.js
+++ b/visualize/static/visualize.js
@@ -55,7 +55,7 @@ function packages_treemap(chart_id, orderings, default_order) {
var nodes = d3_div.data([json]).selectAll("div")
.data(treemap.nodes, key_func);
/* start out new nodes in the center of the picture area */
- var w_center = jq_div.width() / 2;
+ var w_center = jq_div.width() / 2,
h_center = jq_div.height() / 2;
nodes.enter().append("div")
.attr("class", "treemap-cell")
--
cgit v1.2.3-54-g00ecf