# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models from django.core.exceptions import ValidationError, NON_FIELD_ERRORS from django.db.utils import IntegrityError from django.utils.text import Truncator import re #import logging #logger = logging.getLogger('console') unique_constraint_re = re.compile(r'^UNIQUE\s+constraint\s+failed:(.*)$', re.IGNORECASE) class ModelEx(models.Model): class Meta: abstract = True def get_duplicate_key_error_dict(self, fields): return {NON_FIELD_ERRORS: 'Duplicate value for %s' % ', '.join(fields)} def save(self, *args, **kwargs): try: super(ModelEx, self).save(*args, **kwargs) except IntegrityError as e: match = unique_constraint_re.search(e.message) if match: fields = [x.strip().split('.')[-1] for x in match.group(1).split(',')] raise ValidationError(self.get_duplicate_key_error_dict(fields)) else: raise class FeedCategory(ModelEx): name = models.CharField(max_length=50, unique=True) description = models.CharField(max_length=255, blank=True) days_required = models.PositiveIntegerField(default=366) class Meta: verbose_name = 'Category' verbose_name_plural = 'Categories' def get_duplicate_key_error_dict(self, fields): if fields[0] == 'name': return {'name': 'There is already a category with this name'} else: return super(FeedCategory, self).get_duplicate_key_error_dict(fields) @property def status(self): last_day = 0 total_missing = 0 missing_days = [] for item in FeedItem.objects.filter(feed_category=self).order_by('feed_category_id', 'day_number'): day = item.day_number missing_count = day - last_day - 1 total_missing += missing_count # The unique key and order_by on FeedItem ensure that missing_count # cannot be less than 0. if missing_count == 1: missing_days.append(str(day - 1)) elif missing_count > 1: missing_days.append('{0}-{1}'.format(last_day + 1, day - 1)) last_day = day missing_count = self.days_required - last_day if missing_count > 0: total_missing += missing_count if missing_count == 1: missing_days.append(str(self.days_required)) elif missing_count > 1: missing_days.append('{0}-{1}'.format(last_day + 1, self.days_required)) if missing_days: return "WARNING: Missing {0} day{1}: {2}".format(total_missing, '' if total_missing == 1 else 's', ', '.join(missing_days)) else: return "Complete" def __unicode__(self): return self.name class FeedItem(ModelEx): feed_category = models.ForeignKey(FeedCategory, on_delete=models.CASCADE) day_number = models.PositiveIntegerField() message_text = models.CharField(max_length=160) class Meta: verbose_name = 'Item' verbose_name_plural = 'Items' unique_together = ('feed_category', 'day_number') def get_duplicate_key_error_dict(self, fields): return {'day_number': 'There is already an item with this day number'} def __unicode__(self): return u'{0}: {1}'.format(self.day_number, Truncator(self.message_text).chars(20))