qrurls/admin.py
author Tero Marttila <terom@paivola.fi>
Wed, 05 Nov 2014 10:35:51 +0100
changeset 90 a193a8dd737c
parent 89 187e874f770c
permissions -rw-r--r--
qrurls.admin: minor usability tweaks to the import url_images select-multiple...
import datetime
import random
import urllib

from django import http, shortcuts, forms
from django.conf import settings, urls
from django.contrib import admin
from django.core.urlresolvers import reverse
from django.utils import timezone, formats
import django.utils.html
import django.forms.models

from qrurls.models import URL, URLItem, URLImage

"""
    Private backend UI, using django-admin.
"""

class URLItemFormset (django.forms.models.BaseInlineFormSet) :
    """
        Uses the existing URLItems for the URLFeed to determine the initial publishing
        times for new items.
    """

    def __init__ (self, *args, **kwargs) :
        extra = kwargs.get('extra', 5)

        # hack to get at the URLFeed to determine our initial values..
        urlfeed = kwargs.get('instance')
        if not isinstance(urlfeed, URL) :
            urlfeed = None
        
        # XXX: initial values must be stable across form GET/POST, or django will be confused and think the form has been filled out
        kwargs.update(initial=[
            # Either generic from today, or based on the actual urlfeed
            dict(published=publish) for publish in URLAdmin.publishing_schedule(urlfeed, count=extra)
        ])
        super(URLItemFormset, self).__init__(*args, **kwargs)

class URLItemInline (admin.TabularInline) :
    """
        Inline set of URLItems for an URLFeed.
    """

    model = URLItem
    formset = URLItemFormset

class URLAdmin (admin.ModelAdmin) :
    @classmethod
    def publishing_schedule (cls, urlfeed, count):
        """Yield URLItem.published values for feed, or defaults."""
        if urlfeed :
            date, time, offset = urlfeed.publishing_schedule()

            return URL.apply_publishing_schedule(date, time, offset, count)
        else:
            # no data... no defaults
            return []

    def timezone (self, obj) :
        now = timezone.localtime(obj.now())
        tz = now.tzinfo
        td = now.tzinfo.utcoffset(now)

        if td :
            minutes, seconds = divmod(td.total_seconds(), 60)
            hours, minutes = divmod(minutes, 60)
            offset = "(UTC%+03d:%02d)" % (hours, minutes)
        else :
            offset = ""

        return u"%s %s" % (tz, offset)

    def qrcode_url (self, obj) :
        warn = None

        if obj.shorturl.upper() != obj.shorturl :
            warn = "Shorturl should be UPPERCASE for most compact QR code"

        return '<a href="{url}">{url}</a>{warn}'.format(
                url     = django.utils.html.escape(obj.qrcode_url()),
                warn    = '<p class="errornote">{warn}</p>'.format(warn=warn) if warn else '',
        )
    qrcode_url.allow_tags = True

    def qrcode_img (self, obj) :
        return '<img src="{img}" />'.format(
                img=django.utils.html.escape(obj.qrcode_img()),
        )
    qrcode_img.allow_tags = True
    
    # XXX: a whole bunch of ugly datetime-formatting for display...
    def active (self, obj) :
        item = obj.active_item()
        if item :
            return "%s %s" % (formats.localize(timezone.localtime(item.published)), item)
        else :
            return ""

    def now (self, obj) :
        return formats.localize(timezone.localtime(obj.now()))

    def upcoming (self, obj) :
        item = obj.upcoming_item()
        if item :
            return "%s %s" % (formats.localize(timezone.localtime(item.published)), item)
        else :
            return ""

    readonly_fields = (
        'timezone',
        'qrcode_url',
        'qrcode_img',
        'active_item',
        'upcoming_item',
        'active',
        'now',
        'upcoming',
    )
    list_display = (
        'get_absolute_url',
        'active_item',
        'upcoming_item',
    )
    fieldsets = (
        (None, {
            'fields': (
                'shorturl',
                'publishing_days',
                'publishing_time', 
                'timezone',
                'title',
            ),
        }),
        ("QRCode", {
            'fields': ('qrcode_url', 'qrcode_img'),
        }),
        ("Item publishing overview", {
            'fields': (
                'active',
                'now',
                'upcoming',
            ),
        }),
    )
    inlines = (URLItemInline, )

    ## Import
    class ImportForm (forms.Form) :
        shorturl = forms.ModelChoiceField(URL.objects)
        publishing_date = forms.DateField(widget=admin.widgets.AdminDateWidget)
        publishing_time = forms.TimeField(widget=admin.widgets.AdminTimeWidget)
        
        # Django forms doesn't really understand multi-value fields, so we just hack this ourselves.
        #image = forms.ImageField()

        # Existing URLImages
        url_images = forms.ModelMultipleChoiceField(queryset=URLImage.objects.all(),
                widget  = forms.SelectMultiple(attrs={'size': 20}),
        )

    def import_images_handler (self, url_feed, data, images=()) :
        """Custom for backend for mass-importing images into a feed."""
        
        # series of datetimes
        publishing = url_feed.apply_publishing_schedule(
            data['publishing_date'], data['publishing_time'], url_feed.publishing_offset)

        url_images = list(data['url_images'])
        
        for image in images:
            url_image = URLImage(image=image)
            url_image.save()

            url_images.append(url_image)
        
        # randomize
        random.shuffle(url_images)

        for url_image, publish in zip(url_images, publishing) :
            url_item = URLItem(shorturl=url_feed, published=publish, image=url_image)
            url_item.save()

        return http.HttpResponseRedirect(reverse('admin:qrurls_url_change', args=(url_feed.id, )))

    def import_images (self, request, shorturl_id) :
        """Custom form frontend for mass-importing images into a feed."""
        url_feed = URL.objects.get(id=int(shorturl_id))
        
        if request.method == 'POST' :
            form = self.ImportForm(request.POST)
            images = request.FILES.getlist('image')

            if form.is_valid() :
                return self.import_images_handler(url_feed, form.cleaned_data, images)
        else :
            publishing_date, publishing_time, publishing_offset = url_feed.publishing_schedule()

            form = self.ImportForm(initial=dict(
                shorturl        = url_feed.id,
                publishing_date = publishing_date,
                publishing_time = publishing_time,
            ))
        
        return shortcuts.render(request, 'admin/qrurls_import_images.html', dict(
            current_app     = self.admin_site.name,

            media           = self.media + form.media,
            form_url        = reverse('admin:qrurls_import_images', kwargs=dict(shorturl_id=url_feed.id)),
            form            = form,
        ))

    def get_urls (self) :
        return urls.patterns('',
            urls.url(r'^(?P<shorturl_id>\d+)/import$', self.admin_site.admin_view(self.import_images),
                name='qrurls_import_images'),
        ) + super(URLAdmin, self).get_urls()

    def import_images_action (self, request, queryset) :
        url_feed, = [url_feed for url_feed in queryset]
        return http.HttpResponseRedirect(
                reverse('admin:qrurls_import_images', kwargs=dict(shorturl_id=url_feed.id)) # ) + '?' + urllib.urlencode(qs, doseq=True)
        )
    import_images_action.short_description = "Import images for selected feeds"
    
    actions = [import_images_action]

class URLItemAdmin (admin.ModelAdmin) :
    list_display = (
        'shorturl', 'published_state', 'get_absolute_url', 'image', 'published',
    )
    readonly_fields = ('published_state', )
    fieldsets = (
        ("Publishing", {
            'fields': ('shorturl', 'published', ),
        }),
        ("Target", {
            'fields': ('url', 'image'),
        }),
    )

class URLImageAdmin (admin.ModelAdmin) :
    def show_image(self, obj):
        return """<img src="{url}" style="max-width: 480px"/>""".format(url=obj.get_absolute_url())
    show_image.short_description = "Image"
    show_image.allow_tags = True

    list_display = (
        'name',
        'uploaded',
    )
    # hide the "uploaded" field
    fields = (
        'image',
        'name',
        'uploaded',
        'title',
        'show_image',
    )
    readonly_fields = (
        'name',
        'uploaded',
        'show_image',
    )
    
    inlines = (URLItemInline, )

admin.site.register(URL, URLAdmin)
admin.site.register(URLItem, URLItemAdmin)
admin.site.register(URLImage, URLImageAdmin)