qrurls/models.py
changeset 87 88d9c9974d6a
parent 86 656c8ff72f77
child 88 06ef4789a353
equal deleted inserted replaced
86:656c8ff72f77 87:88d9c9974d6a
    62 
    62 
    63     @classmethod
    63     @classmethod
    64     def cache_key (self, shorturl) :
    64     def cache_key (self, shorturl) :
    65         return 'qrurls/url/{shorturl}'.format(shorturl=shorturl)
    65         return 'qrurls/url/{shorturl}'.format(shorturl=shorturl)
    66 
    66 
    67     def qrcode_img (self, size=512) :
       
    68         return QRCODE_API.format(
       
    69                 width=size, height=size,
       
    70                 url=django.utils.http.urlquote(self.qrcode_url()),
       
    71         )
       
    72 
       
    73     def qrcode_url (self) :
       
    74         return 'HTTP://{domain}{url}'.format(
       
    75                 domain = get_current_site(None).domain.upper(),
       
    76                 url = self.get_absolute_url(),
       
    77         )
       
    78 
       
    79     def get_absolute_url (self) :
       
    80         return reverse('shorturl', args=[self.shorturl])
       
    81 
       
    82     def now (self, now=None) :
       
    83         """
       
    84             Return database-compatible concept of "now".
       
    85 
       
    86             All datetimes are strictly stored and compared as UTC. Any
       
    87             timezone-aware logic should happen in the admin.
       
    88         """
       
    89         if now :
       
    90             return now
       
    91         else :
       
    92             return timezone.now()
       
    93     
       
    94     def active_item (self, now=None) :
       
    95         """Currently published URLItem."""
       
    96         now = self.now(now)
       
    97 
       
    98         try :
       
    99             return self.urlitem_set.filter(published__lt=now).order_by('-published')[0]
       
   100         except IndexError :
       
   101             return None
       
   102 
       
   103     def upcoming_item (self, now=None) :
       
   104         """Next-up to-be-published URLItem."""
       
   105         now = self.now(now)
       
   106 
       
   107         try :
       
   108             return self.urlitem_set.filter(published__gt=now).order_by('published')[0]
       
   109         except IndexError :
       
   110             return None
       
   111 
       
   112     def last_item (self) :
       
   113         """The last URLItem available."""
       
   114 
       
   115         try :
       
   116             return self.urlitem_set.order_by('-published')[0]
       
   117         except IndexError :
       
   118             return None
       
   119 
       
   120     @property
       
   121     def publishing_timetz (self) :
       
   122         """publishing_time, with tzinfo on the correct timezone."""
       
   123         return self.publishing_time.replace(tzinfo=timezone.get_current_timezone())
       
   124     
       
   125     @property
       
   126     def publishing_offset (self) :
       
   127         return datetime.timedelta(days=self.publishing_days)
       
   128 
       
   129     def publishing_schedule (self) :
       
   130         """Calculate initial URLItem.published values for feed."""
       
   131 
       
   132         # following the last item in the queue
       
   133         item = self.last_item()
       
   134 
       
   135         if item and item.published > self.now():
       
   136             # starting from the following day
       
   137             date = item.published.date() + self.publishing_offset
       
   138         else :
       
   139             # defaults to today
       
   140             date = self.now().date()
       
   141         
       
   142         return date, self.publishing_timetz, self.publishing_offset
       
   143     
       
   144     @classmethod
       
   145     def apply_publishing_schedule (self, date, time, offset, count) :
       
   146         """Yield publishing times off given date/time to offset * count."""
       
   147         for index in xrange(0, count) :
       
   148             yield datetime.datetime.combine(date + offset * index, time)
       
   149 
       
   150     def __unicode__ (self) :
       
   151         return self.shorturl
       
   152 
       
   153 class URLImage(models.Model):
       
   154     image = models.ImageField(upload_to=IMAGES_MEDIA, storage=SecretFileSystemStorage())
       
   155     name = models.CharField(max_length=512, blank=False)
       
   156     title = models.CharField(max_length=1024, blank=True)
       
   157     uploaded = models.DateTimeField(auto_now_add=True)
       
   158 
       
   159     class Meta:
       
   160         verbose_name = u"URL Image"
       
   161         verbose_name_plural = u"URL Images"
       
   162         ordering = ['uploaded']
       
   163 
       
   164     def save (self) :
       
   165         # keep real filename before saving with hash
       
   166         # but not when updating!
       
   167         if not self.name:
       
   168             self.name = self.image.name
       
   169 
       
   170         super(URLImage, self).save()
       
   171 
       
   172     def get_absolute_url (self) :
       
   173         return self.image.url
       
   174 
       
   175     def __unicode__ (self) :
       
   176         return "[%s] %s" % (self.uploaded.strftime("%Y-%m-%d"), self.name)
       
   177 
       
   178 class URLItem(models.Model):
       
   179     shorturl = models.ForeignKey(URL)
       
   180     published = models.DateTimeField(db_index=True) # UTC
       
   181 
       
   182     # either-or
       
   183     url = models.URLField(blank=True) # populated from image
       
   184     image = models.ForeignKey(URLImage, null=True, blank=True)
       
   185     
       
   186     class Meta:
       
   187         verbose_name = u"URL Item"
       
   188         verbose_name_plural = u"URL Items"
       
   189         ordering = ['published']
       
   190 
       
   191     @classmethod
       
   192     def cache_key (cls, shorturl, item_id) :
       
   193         return 'qrurls/url/{shorturl}/{item}'.format(shorturl=shorturl, item=item_id)
       
   194 
       
   195     @classmethod
       
   196     def get (cls, shorturl, item_id=None, related=()) :
       
   197         """
       
   198             Return the URLItem for a given shorturl, either the given specific one,
       
   199             or the latest, from the database.
       
   200 
       
   201             Raises URLItem.NotFound
       
   202         """
       
   203         # JOIN against shorturl, urlimage
       
   204         url_item = cls.objects.select_related(*related)
       
   205 
       
   206         if not shorturl:
       
   207             raise cls.DoesNotExist()
       
   208         elif shorturl.isdigit():
       
   209             shorturl_id = int(shorturl)
       
   210             url_item = url_item.filter(shorturl__id=shorturl_id)
       
   211         else:
       
   212             url_item = url_item.filter(shorturl__shorturl=shorturl)
       
   213         
       
   214         # match for published items
       
   215         now = timezone.now()
       
   216         url_item = url_item.filter(published__lt=now).order_by('-published')
       
   217        
       
   218         if item_id :
       
   219             # specific, but still the published on
       
   220             log.debug("search @ %d", item_id)
       
   221 
       
   222             return url_item.get(id=item_id) # raises DoesNotExist
       
   223         else :
       
   224             # most recent
       
   225             log.debug("search @ %s", now)
       
   226             try:
       
   227                 return url_item[0]
       
   228             except IndexError:
       
   229                 raise cls.DoesNotExist()
       
   230 
       
   231     @classmethod
    67     @classmethod
   232     def get_url (cls, shorturl) :
    68     def get_url (cls, shorturl) :
   233         """
    69         """
   234             Return the current URL for a given shorturl, from cache or DB.
    70             Return the current URL for a given shorturl, from cache or DB.
   235             
    71             
   236             Returns url:str, modified:datetime
    72             Returns url:str, modified:datetime
   237             Raises URLItem.NotFound
    73             Raises URLItem.DoesNotExist
   238         """
    74         """
   239         key = URL.cache_key(shorturl)
    75         key = cls.cache_key(shorturl)
   240         get = cache.get(key)
    76         get = cache.get(key)
   241         
    77         
   242         if get :
    78         if get :
   243             log.debug("get cache: %s", key)
    79             log.debug("get cache: %s", key)
   244             return get
    80             return get
   245         else:
    81         else:
   246             # from db
    82             # from db
   247             url_item = cls.get(shorturl=shorturl)
    83             url_item = URLItem.get(shorturl=shorturl)
   248             set = (
    84             set = (
   249                 url_item.get_absolute_url(),
    85                 url_item.get_absolute_url(),
   250                 url_item.last_modified()
    86                 url_item.last_modified()
   251             )
    87             )
   252             
    88             
   253             log.debug("set cache: %s", key)
    89             log.debug("set cache: %s", key)
   254             cache.set(key, set)
    90             cache.set(key, set)
   255             return set
    91             return set
   256 
    92 
       
    93 
       
    94     def qrcode_img (self, size=512) :
       
    95         return QRCODE_API.format(
       
    96                 width=size, height=size,
       
    97                 url=django.utils.http.urlquote(self.qrcode_url()),
       
    98         )
       
    99 
       
   100     def qrcode_url (self) :
       
   101         return 'HTTP://{domain}{url}'.format(
       
   102                 domain = get_current_site(None).domain.upper(),
       
   103                 url = self.get_absolute_url(),
       
   104         )
       
   105 
       
   106     def get_absolute_url (self) :
       
   107         return reverse('shorturl', args=[self.shorturl])
       
   108 
       
   109     def now (self, now=None) :
       
   110         """
       
   111             Return database-compatible concept of "now".
       
   112 
       
   113             All datetimes are strictly stored and compared as UTC. Any
       
   114             timezone-aware logic should happen in the admin.
       
   115         """
       
   116         if now :
       
   117             return now
       
   118         else :
       
   119             return timezone.now()
       
   120     
       
   121     def active_item (self, now=None) :
       
   122         """Currently published URLItem."""
       
   123         now = self.now(now)
       
   124 
       
   125         try :
       
   126             return self.urlitem_set.filter(published__lt=now).order_by('-published')[0]
       
   127         except IndexError :
       
   128             return None
       
   129 
       
   130     def upcoming_item (self, now=None) :
       
   131         """Next-up to-be-published URLItem."""
       
   132         now = self.now(now)
       
   133 
       
   134         try :
       
   135             return self.urlitem_set.filter(published__gt=now).order_by('published')[0]
       
   136         except IndexError :
       
   137             return None
       
   138 
       
   139     def last_item (self) :
       
   140         """The last URLItem available."""
       
   141 
       
   142         try :
       
   143             return self.urlitem_set.order_by('-published')[0]
       
   144         except IndexError :
       
   145             return None
       
   146 
       
   147     @property
       
   148     def publishing_timetz (self) :
       
   149         """publishing_time, with tzinfo on the correct timezone."""
       
   150         return self.publishing_time.replace(tzinfo=timezone.get_current_timezone())
       
   151     
       
   152     @property
       
   153     def publishing_offset (self) :
       
   154         return datetime.timedelta(days=self.publishing_days)
       
   155 
       
   156     def publishing_schedule (self) :
       
   157         """Calculate initial URLItem.published values for feed."""
       
   158 
       
   159         # following the last item in the queue
       
   160         item = self.last_item()
       
   161 
       
   162         if item and item.published > self.now():
       
   163             # starting from the following day
       
   164             date = item.published.date() + self.publishing_offset
       
   165         else :
       
   166             # defaults to today
       
   167             date = self.now().date()
       
   168         
       
   169         return date, self.publishing_timetz, self.publishing_offset
       
   170     
       
   171     @classmethod
       
   172     def apply_publishing_schedule (self, date, time, offset, count) :
       
   173         """Yield publishing times off given date/time to offset * count."""
       
   174         for index in xrange(0, count) :
       
   175             yield datetime.datetime.combine(date + offset * index, time)
       
   176 
       
   177     def __unicode__ (self) :
       
   178         return self.shorturl
       
   179 
       
   180 class URLImage(models.Model):
       
   181     image = models.ImageField(upload_to=IMAGES_MEDIA, storage=SecretFileSystemStorage())
       
   182     name = models.CharField(max_length=512, blank=False)
       
   183     title = models.CharField(max_length=1024, blank=True)
       
   184     uploaded = models.DateTimeField(auto_now_add=True)
       
   185 
       
   186     class Meta:
       
   187         verbose_name = u"URL Image"
       
   188         verbose_name_plural = u"URL Images"
       
   189         ordering = ['uploaded']
       
   190 
       
   191     def save (self) :
       
   192         # keep real filename before saving with hash
       
   193         # but not when updating!
       
   194         if not self.name:
       
   195             self.name = self.image.name
       
   196 
       
   197         super(URLImage, self).save()
       
   198 
       
   199     def get_absolute_url (self) :
       
   200         return self.image.url
       
   201 
       
   202     def __unicode__ (self) :
       
   203         return "[%s] %s" % (self.uploaded.strftime("%Y-%m-%d"), self.name)
       
   204 
       
   205 class URLItem(models.Model):
       
   206     shorturl = models.ForeignKey(URL)
       
   207     published = models.DateTimeField(db_index=True) # UTC
       
   208 
       
   209     # either-or
       
   210     url = models.URLField(blank=True) # populated from image
       
   211     image = models.ForeignKey(URLImage, null=True, blank=True)
       
   212     
       
   213     class Meta:
       
   214         verbose_name = u"URL Item"
       
   215         verbose_name_plural = u"URL Items"
       
   216         ordering = ['published']
       
   217 
       
   218     @classmethod
       
   219     def get (cls, shorturl, item_id=None, related=()) :
       
   220         """
       
   221             Return the URLItem for a given shorturl, either the given specific one,
       
   222             or the latest, from the database in one SQL query.
       
   223 
       
   224             Raises URLItem.DoesNotExist
       
   225         """
       
   226         # JOIN against shorturl, urlimage
       
   227         url_item = cls.objects.select_related(*related)
       
   228 
       
   229         if not shorturl:
       
   230             raise cls.DoesNotExist()
       
   231         elif shorturl.isdigit():
       
   232             shorturl_id = int(shorturl)
       
   233             url_item = url_item.filter(shorturl__id=shorturl_id)
       
   234         else:
       
   235             url_item = url_item.filter(shorturl__shorturl=shorturl)
       
   236         
       
   237         # match for published items
       
   238         now = timezone.now()
       
   239         url_item = url_item.filter(published__lt=now).order_by('-published')
       
   240        
       
   241         if item_id :
       
   242             # specific, but still the published on
       
   243             log.debug("search @ %d", item_id)
       
   244 
       
   245             return url_item.get(id=item_id) # raises DoesNotExist
       
   246         else :
       
   247             # most recent
       
   248             log.debug("search @ %s", now)
       
   249             try:
       
   250                 return url_item[0]
       
   251             except IndexError:
       
   252                 raise cls.DoesNotExist()
       
   253 
       
   254     @classmethod
       
   255     def cache_key (cls, shorturl, item_id) :
       
   256         return 'qrurls/url/{shorturl}/{item}'.format(shorturl=shorturl, item=item_id)
       
   257 
   257     @classmethod
   258     @classmethod
   258     def get_item (cls, shorturl, item_id) :
   259     def get_item (cls, shorturl, item_id) :
   259         """
   260         """
   260             Return a data dict for the given URLItem, from cache or DB.
   261             Return a data dict for the given URLItem, from cache or DB.
   261 
   262 
   262             Returns { url: str, title: str, image: str, last_modified: datetime }
   263             Returns { url: str, title: str, image: str, last_modified: datetime }
   263             Raises URLItem.NotFound
   264             Raises URLItem.DoesNotExist
   264         """
   265         """
   265 
   266 
   266         key = cls.cache_key(shorturl, item_id)
   267         key = cls.cache_key(shorturl, item_id)
   267         get = cache.get(key)
   268         get = cache.get(key)
   268 
   269