terom@27: import datetime
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@55: # Django forms doesn't really understand multi-value fields, so we just hack this ourselves.
terom@55: #image = forms.ImageField()
terom@49:
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@56: data['publishing_date'], data['publishing_time'], url_feed.publishing_offset, len(images))
terom@49:
terom@49: for image, publish in zip(images, publishing) :
terom@55: url_image = URLImage(image=image)
terom@49: url_image.save()
terom@49:
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)