terom@27: import datetime terom@89: import random terom@49: import urllib terom@27: terom@49: from django import http, shortcuts, forms terom@49: from django.conf import settings, urls terom@2: from django.contrib import admin terom@49: from django.core.urlresolvers import reverse terom@47: from django.utils import timezone, formats terom@27: import django.utils.html terom@27: import django.forms.models terom@27: terom@30: from qrurls.models import URL, URLItem, URLImage terom@27: terom@54: """ terom@54: Private backend UI, using django-admin. terom@54: """ terom@54: terom@27: class URLItemFormset (django.forms.models.BaseInlineFormSet) : terom@47: """ terom@47: Uses the existing URLItems for the URLFeed to determine the initial publishing terom@47: times for new items. terom@47: """ terom@47: terom@27: def __init__ (self, *args, **kwargs) : terom@49: extra = kwargs.get('extra', 5) terom@27: terom@56: # hack to get at the URLFeed to determine our initial values.. terom@56: urlfeed = kwargs.get('instance') terom@56: if not isinstance(urlfeed, URL) : terom@56: urlfeed = None terom@56: terom@65: # XXX: initial values must be stable across form GET/POST, or django will be confused and think the form has been filled out terom@27: kwargs.update(initial=[ terom@56: # Either generic from today, or based on the actual urlfeed terom@56: dict(published=publish) for publish in URLAdmin.publishing_schedule(urlfeed, count=extra) terom@27: ]) terom@27: super(URLItemFormset, self).__init__(*args, **kwargs) terom@2: terom@3: class URLItemInline (admin.TabularInline) : terom@47: """ terom@47: Inline set of URLItems for an URLFeed. terom@47: """ terom@47: terom@3: model = URLItem terom@27: formset = URLItemFormset terom@3: terom@2: class URLAdmin (admin.ModelAdmin) : terom@49: @classmethod terom@56: def publishing_schedule (cls, urlfeed, count): terom@56: """Yield URLItem.published values for feed, or defaults.""" terom@56: if urlfeed : terom@56: date, time, offset = urlfeed.publishing_schedule() terom@65: terom@65: return URL.apply_publishing_schedule(date, time, offset, count) terom@49: else: terom@65: # no data... no defaults terom@65: return [] terom@49: terom@47: def timezone (self, obj) : terom@47: now = timezone.localtime(obj.now()) terom@47: tz = now.tzinfo terom@47: td = now.tzinfo.utcoffset(now) terom@47: terom@47: if td : terom@47: minutes, seconds = divmod(td.total_seconds(), 60) terom@47: hours, minutes = divmod(minutes, 60) terom@47: offset = "(UTC%+03d:%02d)" % (hours, minutes) terom@47: else : terom@47: offset = "" terom@47: terom@47: return u"%s %s" % (tz, offset) terom@47: terom@22: def qrcode_url (self, obj) : terom@22: warn = None terom@22: terom@22: if obj.shorturl.upper() != obj.shorturl : terom@22: warn = "Shorturl should be UPPERCASE for most compact QR code" terom@22: terom@22: return '{url}{warn}'.format( terom@22: url = django.utils.html.escape(obj.qrcode_url()), terom@22: warn = '

{warn}

'.format(warn=warn) if warn else '', terom@22: ) terom@22: qrcode_url.allow_tags = True terom@22: terom@4: def qrcode_img (self, obj) : terom@4: return ''.format( terom@4: img=django.utils.html.escape(obj.qrcode_img()), terom@4: ) terom@4: qrcode_img.allow_tags = True terom@47: terom@47: # XXX: a whole bunch of ugly datetime-formatting for display... terom@47: def active (self, obj) : terom@47: item = obj.active_item() terom@47: if item : terom@47: return "%s %s" % (formats.localize(timezone.localtime(item.published)), item) terom@47: else : terom@47: return "" terom@47: terom@47: def now (self, obj) : terom@47: return formats.localize(timezone.localtime(obj.now())) terom@47: terom@47: def upcoming (self, obj) : terom@47: item = obj.upcoming_item() terom@47: if item : terom@47: return "%s %s" % (formats.localize(timezone.localtime(item.published)), item) terom@47: else : terom@47: return "" terom@4: terom@4: readonly_fields = ( terom@47: 'timezone', terom@4: 'qrcode_url', terom@4: 'qrcode_img', terom@7: 'active_item', terom@7: 'upcoming_item', terom@47: 'active', terom@47: 'now', terom@47: 'upcoming', terom@4: ) terom@2: list_display = ( terom@11: 'get_absolute_url', terom@8: 'active_item', terom@8: 'upcoming_item', terom@2: ) terom@4: fieldsets = ( terom@4: (None, { terom@47: 'fields': ( terom@63: 'shorturl', terom@63: 'publishing_days', terom@47: 'publishing_time', terom@47: 'timezone', terom@51: 'title', terom@47: ), terom@4: }), terom@4: ("QRCode", { terom@7: 'fields': ('qrcode_url', 'qrcode_img'), terom@4: }), terom@47: ("Item publishing overview", { terom@47: 'fields': ( terom@47: 'active', terom@47: 'now', terom@47: 'upcoming', terom@47: ), terom@47: }), terom@4: ) terom@3: inlines = (URLItemInline, ) terom@2: terom@49: ## Import terom@49: class ImportForm (forms.Form) : terom@49: shorturl = forms.ModelChoiceField(URL.objects) terom@49: publishing_date = forms.DateField(widget=admin.widgets.AdminDateWidget) terom@49: publishing_time = forms.TimeField(widget=admin.widgets.AdminTimeWidget) terom@89: terom@55: # Django forms doesn't really understand multi-value fields, so we just hack this ourselves. terom@55: #image = forms.ImageField() terom@49: terom@89: # Existing URLImages terom@90: url_images = forms.ModelMultipleChoiceField(queryset=URLImage.objects.all(), terom@90: widget = forms.SelectMultiple(attrs={'size': 20}), terom@90: ) terom@89: terom@55: def import_images_handler (self, url_feed, data, images=()) : terom@49: """Custom for backend for mass-importing images into a feed.""" terom@49: terom@56: # series of datetimes terom@56: publishing = url_feed.apply_publishing_schedule( terom@89: data['publishing_date'], data['publishing_time'], url_feed.publishing_offset) terom@49: terom@89: url_images = list(data['url_images']) terom@89: terom@89: for image in images: terom@55: url_image = URLImage(image=image) terom@49: url_image.save() terom@49: terom@89: url_images.append(url_image) terom@89: terom@89: # randomize terom@89: random.shuffle(url_images) terom@89: terom@89: for url_image, publish in zip(url_images, publishing) : terom@49: url_item = URLItem(shorturl=url_feed, published=publish, image=url_image) terom@49: url_item.save() terom@49: terom@49: return http.HttpResponseRedirect(reverse('admin:qrurls_url_change', args=(url_feed.id, ))) terom@49: terom@49: def import_images (self, request, shorturl_id) : terom@49: """Custom form frontend for mass-importing images into a feed.""" terom@49: url_feed = URL.objects.get(id=int(shorturl_id)) terom@49: terom@49: if request.method == 'POST' : terom@55: form = self.ImportForm(request.POST) terom@55: images = request.FILES.getlist('image') terom@49: terom@49: if form.is_valid() : terom@55: return self.import_images_handler(url_feed, form.cleaned_data, images) terom@49: else : terom@56: publishing_date, publishing_time, publishing_offset = url_feed.publishing_schedule() terom@49: terom@49: form = self.ImportForm(initial=dict( terom@49: shorturl = url_feed.id, terom@49: publishing_date = publishing_date, terom@49: publishing_time = publishing_time, terom@49: )) terom@49: terom@49: return shortcuts.render(request, 'admin/qrurls_import_images.html', dict( terom@49: current_app = self.admin_site.name, terom@49: terom@49: media = self.media + form.media, terom@49: form_url = reverse('admin:qrurls_import_images', kwargs=dict(shorturl_id=url_feed.id)), terom@49: form = form, terom@49: )) terom@49: terom@49: def get_urls (self) : terom@49: return urls.patterns('', terom@49: urls.url(r'^(?P\d+)/import$', self.admin_site.admin_view(self.import_images), terom@49: name='qrurls_import_images'), terom@49: ) + super(URLAdmin, self).get_urls() terom@49: terom@49: def import_images_action (self, request, queryset) : terom@49: url_feed, = [url_feed for url_feed in queryset] terom@49: return http.HttpResponseRedirect( terom@49: reverse('admin:qrurls_import_images', kwargs=dict(shorturl_id=url_feed.id)) # ) + '?' + urllib.urlencode(qs, doseq=True) terom@49: ) terom@49: import_images_action.short_description = "Import images for selected feeds" terom@49: terom@49: actions = [import_images_action] terom@49: terom@2: class URLItemAdmin (admin.ModelAdmin) : terom@2: list_display = ( terom@47: 'shorturl', 'published_state', 'get_absolute_url', 'image', 'published', terom@3: ) terom@61: readonly_fields = ('published_state', ) terom@3: fieldsets = ( terom@3: ("Publishing", { terom@3: 'fields': ('shorturl', 'published', ), terom@3: }), terom@3: ("Target", { terom@32: 'fields': ('url', 'image'), terom@3: }), terom@2: ) terom@2: terom@30: class URLImageAdmin (admin.ModelAdmin) : terom@64: def show_image(self, obj): terom@64: return """""".format(url=obj.get_absolute_url()) terom@64: show_image.short_description = "Image" terom@64: show_image.allow_tags = True terom@64: terom@38: list_display = ( terom@66: 'name', terom@39: 'uploaded', terom@38: ) terom@30: # hide the "uploaded" field terom@38: fields = ( terom@38: 'image', terom@66: 'name', terom@66: 'uploaded', terom@40: 'title', terom@64: 'show_image', terom@38: ) terom@30: readonly_fields = ( terom@66: 'name', terom@30: 'uploaded', terom@64: 'show_image', terom@30: ) terom@32: terom@32: inlines = (URLItemInline, ) terom@30: terom@2: admin.site.register(URL, URLAdmin) terom@2: admin.site.register(URLItem, URLItemAdmin) terom@32: admin.site.register(URLImage, URLImageAdmin)