clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
authorTero Marttila <terom@fixme.fi>
Sun, 18 Aug 2013 23:12:54 +0300
changeset 47 35a8e63648c8
parent 46 24061881238b
child 48 5df9ed6b5976
clarify timezones used for URLItem.published, but no actual explicit per-URLFeed timezone..
cmpuqrct/settings/base.py
qrurls/admin.py
qrurls/migrations/0007_rename_url_field__publishing_schedule__publishing_time.py
qrurls/models.py
--- a/cmpuqrct/settings/base.py	Sun Aug 18 23:06:29 2013 +0300
+++ b/cmpuqrct/settings/base.py	Sun Aug 18 23:12:54 2013 +0300
@@ -22,7 +22,7 @@
 
 # Language code for this installation. All choices can be found here:
 # http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-uk'
+LANGUAGE_CODE = 'C' # 'en-uk'
 
 # Non-local-specifc defaults
 DATETIME_FORMAT = 'Y-m-d H:i:s O'
--- a/qrurls/admin.py	Sun Aug 18 23:06:29 2013 +0300
+++ b/qrurls/admin.py	Sun Aug 18 23:12:54 2013 +0300
@@ -1,21 +1,29 @@
 import datetime
 
+from django.conf import settings
 from django.contrib import admin
+from django.utils import timezone, formats
 import django.utils.html
 import django.forms.models
 
 from qrurls.models import URL, URLItem, URLImage
 
 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) :
         urlfeed = kwargs.get('instance')
 
         publishing_date = None
         publishing_time = datetime.time()
         
+        # hack to get at the URLFeed to determine our initial values..
         if urlfeed and isinstance(urlfeed, URL) :
             publishing_date = urlfeed.last_item().published.date()
-            publishing_time = urlfeed.publishing_schedule
+            publishing_time = urlfeed.publishing_time
         
         def publishing_schedule (count) :
             if not publishing_date :
@@ -39,10 +47,28 @@
         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) :
+    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
 
@@ -60,12 +86,34 @@
                 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',
@@ -74,19 +122,30 @@
     )
     fieldsets = (
         (None, {
-            'fields': ('shorturl', 'active_item', 'upcoming_item', 'publishing_schedule')
+            'fields': (
+                'shorturl', 
+                'publishing_time', 
+                'timezone',
+            ),
         }),
         ("QRCode", {
             'fields': ('qrcode_url', 'qrcode_img'),
         }),
+        ("Item publishing overview", {
+            'fields': (
+                'active',
+                'now',
+                'upcoming',
+            ),
+        }),
     )
     inlines = (URLItemInline, )
 
 class URLItemAdmin (admin.ModelAdmin) :
     list_display = (
-        'shorturl', 'get_absolute_url', 'image', 'published_state', 
+        'shorturl', 'published_state', 'get_absolute_url', 'image', 'published',
     )
-    readonly_fields = ('published_state',)
+    readonly_fields = ('published_state', 'published')
     fieldsets = (
         ("Publishing", {
             'fields': ('shorturl', 'published', ),
@@ -95,7 +154,6 @@
             'fields': ('url', 'image'),
         }),
     )
-    
 
 class URLImageAdmin (admin.ModelAdmin) :
     list_display = (
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qrurls/migrations/0007_rename_url_field__publishing_schedule__publishing_time.py	Sun Aug 18 23:12:54 2013 +0300
@@ -0,0 +1,44 @@
+# -*- 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):
+        # Fix naming
+        db.rename_column('qrurls_url', 'publishing_schedule', 'publishing_time')
+
+
+    def backwards(self, orm):
+        # Revert naming
+        db.rename_column('qrurls_url', 'publishing_time', 'publishing_schedule')
+
+
+    models = {
+        u'qrurls.url': {
+            'Meta': {'object_name': 'URL'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'publishing_time': ('django.db.models.fields.TimeField', [], {'default': 'datetime.time(0, 0)'}),
+            'shorturl': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+        },
+        u'qrurls.urlimage': {
+            'Meta': {'ordering': "['uploaded']", 'object_name': 'URLImage'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'uploaded': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+        },
+        u'qrurls.urlitem': {
+            'Meta': {'ordering': "['published']", 'object_name': 'URLItem'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['qrurls.URLImage']", 'null': 'True', 'blank': 'True'}),
+            'published': ('django.db.models.fields.DateTimeField', [], {}),
+            'shorturl': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['qrurls.URL']"}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['qrurls']
--- a/qrurls/models.py	Sun Aug 18 23:06:29 2013 +0300
+++ b/qrurls/models.py	Sun Aug 18 23:12:54 2013 +0300
@@ -43,8 +43,8 @@
 
 class URL(models.Model):
     shorturl = models.SlugField(unique=True)
-    publishing_schedule = models.TimeField(default=datetime.time(),
-            help_text="Default time to publish new URLItems")
+    publishing_time = models.TimeField(default=datetime.time(),
+            help_text="Default time to publish new URLItems (in timezone)")
 
     class Meta:
         verbose_name = u"URL Feed"
@@ -65,10 +65,21 @@
     def get_absolute_url (self) :
         return reverse('shorturl', args=[self.shorturl])
 
-    def active_item(self, now=None) :
+    def now (self, now=None) :
+        """
+            Return database-compatible concept of "now".
+
+            All datetimes are strictly stored and compared as UTC. Any
+            timezone-aware logic should happen in the admin.
+        """
+        if now :
+            return now
+        else :
+            return timezone.now()
+    
+    def active_item (self, now=None) :
         """Currently published URLItem."""
-        if now is None :
-            now = timezone.now()
+        now = self.now(now)
 
         try :
             return URLItem.objects.filter(shorturl=self, published__lt=now).order_by('-published')[0]
@@ -76,9 +87,8 @@
             return None
 
     def upcoming_item (self, now=None) :
-        """Following published URLItem."""
-        if now is None :
-            now = timezone.now()
+        """Next-up to-be-published URLItem."""
+        now = self.now(now)
 
         try :
             return URLItem.objects.filter(shorturl=self, published__gt=now).order_by('published')[0]
@@ -114,7 +124,7 @@
 
 class URLItem(models.Model):
     shorturl = models.ForeignKey(URL)
-    published = models.DateTimeField()
+    published = models.DateTimeField() # UTC
 
     # either-or
     url = models.URLField(blank=True) # populated from image
@@ -132,9 +142,9 @@
             return reverse('shorturl_item', kwargs=dict(shorturl=self.shorturl, item_id=self.id))
         else :
             return None
-    
+   
     def published_age (self) :
-        now = timezone.now()
+        now = self.shorturl.now() # UTC
 
         if now > self.published:
             td = now - self.published