import datetime
from django.conf import settings
from django.contrib.sites.managers import CurrentSiteManager
from django.contrib.sites.models import Site
from django.db import models
from django.utils import dates
from people.models import Contributor
from utils import cache_with_key, date_tuple, get_current_user, parse_ymd
import os

HIGHLIGHT_COUNT = 4

class Series(models.Model):
    """A recurring column, comic, feature, etc."""
    name = models.CharField(max_length=50, db_index=True)
    slug = models.SlugField(max_length=50, db_index=True, unique=True,
            help_text="Determines what the name will look like in a URL.")
    contributors = models.ManyToManyField(Contributor, related_name='series',
            blank=True)

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('area-detail', (), {'slug': self.slug})

    class Meta:
        ordering = ('name',)
        verbose_name_plural = 'series'

class Section(models.Model):
    """One section of the newspaper."""
    name = models.CharField(max_length=50, db_index=True)
    slug = models.SlugField(max_length=50, db_index=True,
            help_text="Determines what the name will look like in a URL.")
    editors = models.ManyToManyField(Contributor, through='SectionEditorship',
            related_name='sections')

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('area-detail', (), {'slug': self.slug})

    class Meta:
        ordering = ('name',)

    def get_highlights(self, issue=None):
        qs = self.pieces.all()
        if issue:
            qs = qs.filter(issue=issue)
        return qs[:HIGHLIGHT_COUNT]

class SectionEditorship(models.Model):
    """Represents one (co/assistant/etc.) editorship position."""
    contributor = models.ForeignKey(Contributor)
    section = models.ForeignKey(Section)
    assistant = models.BooleanField(default=False)
    co = models.BooleanField(u"Co-?", default=False,
            help_text=u'Adds a "co-" prefix to the editorial title')

    @property
    def title(self):
        prefix = u"Co-" if self.co else u""
        if self.assistant: prefix += u"Assistant "
        return u"%s%s Editor" % (prefix, self.section.name)

    def __unicode__(self):
        return "%s, %s" % (self.contributor, self.title)

    class Meta:
        ordering = ('section', 'assistant')

def filter_live():
    """Returns kwargs suitable for .filter(), for issue permissions"""
    user = get_current_user()
    return {} if user and user.is_staff else {'is_live': True}

class IssueManager(CurrentSiteManager):
    def latest_issue(self):
        user = get_current_user()
        # Staff can see non-live issues
        if user and user.is_staff:
            return self.all()[0]
        # Otherwise, only show live issues
        return self.filter(is_live=True)[0]

    @cache_with_key(lambda s,y,m,d: 'site%d/issue/%d/%s/%d' % (
            Site.objects.get_current().id, int(y), m, int(d)))
    def get_by_date(self, y, m, d):
        date = parse_ymd(y, m, d)
        if not date:
            raise Issue.DoesNotExist
        issue = self.get(date__exact=date, site=Site.objects.get_current(),
                **filter_live())
        dummy = issue.previous; dummy = issue.next
        return issue

def latest_issue_or(f, default=None):
    """Returns a function that applies `f` to the latest issue if there is one,
    or the default otherwise."""
    def deferred():
        try:
            return f(Issue.objects.latest_issue())
        except Issue.DoesNotExist:
            return default
    return deferred

def get_issue_dir(instance, filename):
    return instance.get_subdir_filename(filename)

class Issue(models.Model):
    """One newspaper issue."""
    objects = IssueManager()

    date = models.DateField(db_index=True)
    number = models.PositiveSmallIntegerField(db_index=True,
            default=latest_issue_or(lambda i: i.number + 1, 1))
    volume = models.PositiveSmallIntegerField(db_index=True,
            default=latest_issue_or(lambda i: i.volume, 32))
    is_live = models.BooleanField('Live on site', default=False,
            help_text='Enable this once all content is uploaded.')
    site = models.ForeignKey(Site, related_name='issues',
            default=Site.objects.get_current)
    sections = models.ManyToManyField(Section, related_name='issues',
            through='IssueSection')
    thumbnail = models.ImageField(blank=True, null=True,
            help_text='Should be 170 px wide.',
            upload_to=lambda i, f: i.get_subdir_filename(f))

    class Meta:
        ordering = ['-date']
        unique_together = (('volume', 'number'), ('volume', 'date'))

    def __unicode__(self):
        return u"Volume %s issue %s (%s)" % (self.volume, self.number,
                self.date)

    @models.permalink
    def get_absolute_url(self):
        y, m, d = date_tuple(self.date)
        return ('issue-detail', date_tuple(self.date))

    def save(self, **kwargs):
        make_sections = False
        if not self.id:
            try:
                prev = Issue.objects.latest_issue()
                make_sections = True
            except Issue.DoesNotExist:
                pass
        super(Issue, self).save(**kwargs)
        # Copy over navigation from the previous issue
        if make_sections:
            for i,s in enumerate(IssueSection.objects.filter(issue=prev)):
                IssueSection.objects.create(issue=self, section=s.section,
                        alt_section_name=s.alt_section_name, order=i+1)
        try:
            os.makedirs(os.path.join(settings.MEDIA_ROOT, self.media_dir))
        except OSError, e:
            if e.errno != 17:
                raise

    @property
    def media_dir(self):
        return os.path.join(settings.SITE_MEDIA_SUBDIR,
                'vol%02d' % self.volume, 'issue%02d' % self.number)

    def get_subdir_filename(self, filename):
        dir = self.media_dir
        while True:
            fnm = os.path.join(dir, filename)
            if not os.path.exists(os.path.join(settings.MEDIA_ROOT, fnm)):
                return fnm
            # File with this name already exists...
            base, ext = os.path.splitext(filename)
            filename = base + '_' + ext

    @property
    def previous(self):
        if not hasattr(self, '_previous'):
            try:
                self._previous = self.get_previous_by_date(**filter_live())
            except Issue.DoesNotExist:
                self._previous = None
        return self._previous

    @property
    def next(self):
        if not hasattr(self, '_next'):
            try:
                self._next = self.get_next_by_date(**filter_live())
            except Issue.DoesNotExist:
                self._next = None
        return self._next

    @property
    def nav_sections(self):
        return IssueSection.objects.select_related().filter(issue=self)

    @property
    def highlights(self):
        return self.pieces.order_by('-is_featured', 'order')[:HIGHLIGHT_COUNT]

class IssueSection(models.Model):
    """Stores info about the order and naming of section navigation links."""
    issue = models.ForeignKey(Issue)
    section = models.ForeignKey(Section)
    order = models.PositiveSmallIntegerField(blank=True, null=True)
    alt_section_name = models.CharField('Title', max_length=50,
            blank=True, null=True,
            help_text='Optional alternate title in the navigation bar only.')

    @property
    def section_name(self):
        return self.alt_section_name or self.section.name

    def __unicode__(self):
        return self.section_name

    def save(self, **kwargs):
        if not self.id and self.order is None:
            # Put this at the bottom
            try:
                self.order = IssueSection.objects.filter(issue=self.issue,
                        section=self.section).order_by('-order')[0].order + 1
            except IndexError:
                self.order = 1
        super(IssueSection, self).save(**kwargs)

    class Meta:
        ordering = ('order', 'id')

# vi: set sw=4 ts=4 sts=4 tw=79 ai et nocindent:

59723da7c95eaf5f511a34437b5c596bed17f750
import datetime
from django.conf import settings
from django.contrib.sites.managers import CurrentSiteManager
from django.contrib.sites.models import Site
from django.db import models
from django.utils import dates
from people.models import Contributor
from utils import cache_with_key, date_tuple, get_current_user, parse_ymd
import os

HIGHLIGHT_COUNT = 4

class Series(models.Model):
    """A recurring column, comic, feature, etc."""
    name = models.CharField(max_length=50, db_index=True)
    slug = models.SlugField(max_length=50, db_index=True, unique=True,
            help_text="Determines what the name will look like in a URL.")
    contributors = models.ManyToManyField(Contributor, related_name='series',
            blank=True)

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('area-detail', (), {'slug': self.slug})

    class Meta:
        ordering = ('name',)
        verbose_name_plural = 'series'

class Section(models.Model):
    """One section of the newspaper."""
    name = models.CharField(max_length=50, db_index=True)
    slug = models.SlugField(max_length=50, db_index=True,
            help_text="Determines what the name will look like in a URL.")
    editors = models.ManyToManyField(Contributor, through='SectionEditorship',
            related_name='sections')

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('area-detail', (), {'slug': self.slug})

    class Meta:
        ordering = ('name',)

    def get_highlights(self, issue=None):
        qs = self.pieces.all()
        if issue:
            qs = qs.filter(issue=issue)
        return qs[:HIGHLIGHT_COUNT]

class SectionEditorship(models.Model):
    """Represents one (co/assistant/etc.) editorship position."""
    contributor = models.ForeignKey(Contributor)
    section = models.ForeignKey(Section)
    assistant = models.BooleanField(default=False)
    co = models.BooleanField(u"Co-?", default=False,
            help_text=u'Adds a "co-" prefix to the editorial title')

    @property
    def title(self):
        prefix = u"Co-" if self.co else u""
        if self.assistant: prefix += u"Assistant "
        return u"%s%s Editor" % (prefix, self.section.name)

    def __unicode__(self):
        return "%s, %s" % (self.contributor, self.title)

    class Meta:
        ordering = ('section', 'assistant')

def filter_live():
    """Returns kwargs suitable for .filter(), for issue permissions"""
    user = get_current_user()
    return {} if user and user.is_staff else {'is_live': True}

class IssueManager(CurrentSiteManager):
    def latest_issue(self):
        user = get_current_user()
        # Staff can see non-live issues
        if user and user.is_staff:
            return self.all()[0]
        # Otherwise, only show live issues
        return self.filter(is_live=True)[0]

    @cache_with_key(lambda s,y,m,d: 'site%d/issue/%d/%s/%d' % (
            Site.objects.get_current().id, int(y), m, int(d)))
    def get_by_date(self, y, m, d):
        date = parse_ymd(y, m, d)
        if not date:
            raise Issue.DoesNotExist
        issue = self.get(date__exact=date, site=Site.objects.get_current(),
                **filter_live())
        dummy = issue.previous; dummy = issue.next
        return issue

def latest_issue_or(f, default=None):
    """Returns a function that applies `f` to the latest issue if there is one,
    or the default otherwise."""
    def deferred():
        try:
            return f(Issue.objects.latest_issue())
        except Issue.DoesNotExist:
            return default
    return deferred

def get_issue_dir(instance, filename):
    return instance.get_subdir_filename(filename)

class Issue(models.Model):
    """One newspaper issue."""
    objects = IssueManager()

    date = models.DateField(db_index=True)
    number = models.PositiveSmallIntegerField(db_index=True,
            default=latest_issue_or(lambda i: i.number + 1, 1))
    volume = models.PositiveSmallIntegerField(db_index=True,
            default=latest_issue_or(lambda i: i.volume, 32))
    is_live = models.BooleanField('Live on site', default=False,
            help_text='Enable this once all content is uploaded.')
    site = models.ForeignKey(Site, related_name='issues',
            default=Site.objects.get_current)
    sections = models.ManyToManyField(Section, related_name='issues',
            through='IssueSection')
    thumbnail = models.ImageField(blank=True, null=True,
            help_text='Should be 170 px wide.',
            upload_to=lambda i, f: i.get_subdir_filename(f))

    class Meta:
        ordering = ['-date']
        unique_together = (('volume', 'number'), ('volume', 'date'))

    def __unicode__(self):
        return u"Volume %s issue %s (%s)" % (self.volume, self.number,
                self.date)

    @models.permalink
    def get_absolute_url(self):
        y, m, d = date_tuple(self.date)
        return ('issue-detail', date_tuple(self.date))

    def save(self, **kwargs):
        make_sections = False
        if not self.id:
            try:
                prev = Issue.objects.latest_issue()
                make_sections = True
            except Issue.DoesNotExist:
                pass
        super(Issue, self).save(**kwargs)
        # Copy over navigation from the previous issue
        if make_sections:
            for i,s in enumerate(IssueSection.objects.filter(issue=prev)):
                IssueSection.objects.create(issue=self, section=s.section,
                        alt_section_name=s.alt_section_name, order=i+1)
        try:
            os.makedirs(os.path.join(settings.MEDIA_ROOT, self.media_dir))
        except OSError, e:
            if e.errno != 17:
                raise

    @property
    def media_dir(self):
        return os.path.join(settings.SITE_MEDIA_SUBDIR,
                'vol%02d' % self.volume, 'issue%02d' % self.number)

    def get_subdir_filename(self, filename):
        dir = self.media_dir
        while True:
            fnm = os.path.join(dir, filename)
            if not os.path.exists(os.path.join(settings.MEDIA_ROOT, fnm)):
                return fnm
            # File with this name already exists...
            base, ext = os.path.splitext(filename)
            filename = base + '_' + ext

    @property
    def previous(self):
        if not hasattr(self, '_previous'):
            try:
                self._previous = self.get_previous_by_date(**filter_live())
            except Issue.DoesNotExist:
                self._previous = None
        return self._previous

    @property
    def next(self):
        if not hasattr(self, '_next'):
            try:
                self._next = self.get_next_by_date(**filter_live())
            except Issue.DoesNotExist:
                self._next = None
        return self._next

    @property
    def nav_sections(self):
        return IssueSection.objects.select_related().filter(issue=self)

    @property
    def highlights(self):
        return self.pieces.order_by('-is_featured', 'order')[:HIGHLIGHT_COUNT]

class IssueSection(models.Model):
    """Stores info about the order and naming of section navigation links."""
    issue = models.ForeignKey(Issue)
    section = models.ForeignKey(Section)
    order = models.PositiveSmallIntegerField(blank=True, null=True)
    alt_section_name = models.CharField('Title', max_length=50,
            blank=True, null=True,
            help_text='Optional alternate title in the navigation bar only.')

    @property
    def section_name(self):
        return self.alt_section_name or self.section.name

    def __unicode__(self):
        return self.section_name

    def save(self, **kwargs):
        if not self.id and self.order is None:
            # Put this at the bottom
            try:
                self.order = IssueSection.objects.filter(issue=self.issue,
                        section=self.section).order_by('-order')[0].order + 1
            except IndexError:
                self.order = 1
        super(IssueSection, self).save(**kwargs)

    class Meta:
        ordering = ('order', 'id')

# vi: set sw=4 ts=4 sts=4 tw=79 ai et nocindent:
