class Account(models.Model):
    objects = AccountManager()

    twitter_id = models.CharField(max_length=20, primary_key=True)

    screen_name = models.CharField(max_length=50)
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    location = models.CharField(max_length=100, blank=True)

    protected = models.BooleanField()
    verified = models.BooleanField()

    profile_image_url = models.URLField(max_length=300, null=True)
    url = models.URLField(max_length=300, blank=True, null=True)

    followers_count = models.PositiveIntegerField()
    friends_count = models.PositiveIntegerField()
    statuses_count = models.PositiveIntegerField()

    last_updated = models.DateTimeField(auto_now=True)

    endorser = models.ForeignKey(Endorser)

    class Meta:
        ordering = ['-followers_count']

    def __unicode__(self):
        return '@{username} ({name})'.format(
            username=self.screen_name,
            name=self.name
        )

    def get_large_image(self):
        return self.profile_image_url.replace('normal', '400x400')

    def get_absolute_url(self):
        return "https://twitter.com/{username}".format(
            username=self.screen_name
        )

    def get_profile_image(self):
        return '<img src="%s" height="100" />' % self.profile_image_url
    get_profile_image.allow_tags = True
    get_profile_image.short_description = 'Profile image'

    def save_image_to_s3(self):
        """TODO"""
        import boto
        s3_connection = boto.connect_s3()
        bucket = s3_connection.get_bucket('endorsementdb.com')

        url = self.get_large_image()

        response = requests.get(url, stream=True)
        with open('/tmp/profile_image.png', 'wb') as out_file:
            shutil.copyfileobj(response.raw, out_file)
            del response

        key = bucket.new_key('images/endorsers/%d.png' % self.endorser.pk)
        key.set_contents_from_filename(out_file.name)
        key.make_public() class Candidate(models.Model):
    endorser_link = models.OneToOneField(Endorser)
    name = models.CharField(max_length=50)
    description = models.TextField()
    color = models.CharField(max_length=6)
    rgb = models.CharField(max_length=13)
    still_running = models.BooleanField(default=False)
    position = models.OneToOneField('Position', blank=True, null=True)

    def __unicode__(self):
        return self.name class Endorsement(models.Model):
    endorser = models.ForeignKey(Endorser)
    quote = models.ForeignKey(Quote)
    position = models.ForeignKey(Position)
    confirmed = models.BooleanField(default=True)
    notes = models.TextField(null=True, blank=True)

    class Meta:
        ordering = ['-quote']

    def get_date(self):
        return self.quote.date

    def __unicode__(self):
        return unicode(self.position)

    def get_truncated_quote(self):
        if len(self.quote.text) > 100:
            return self.quote.text[:100] + '...'
        else:
            return self.quote.text class Endorser(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(null=True, blank=True)
    url = models.URLField(null=True, blank=True)
    is_personal = models.BooleanField(default=True)
    max_followers = models.PositiveIntegerField(default=0)
    tags = models.ManyToManyField(Tag, blank=True)
    missing_image = models.BooleanField(default=True)
    current_position = models.ForeignKey('Position', blank=True, null=True)

    class Meta:
        ordering = ['-max_followers']

    def __unicode__(self):
        return self.name

    def has_url(self):
        return bool(self.url)
    has_url.boolean = True

    def get_tags(self):
        return ' / '.join(tag.name for tag in self.tags.all())

    def get_current_endorsement(self):
        try:
            return self.endorsement_set.latest('quote')
        except Endorsement.DoesNotExist:
            pass

    def get_image(self):
        return '<img src="{url}" width="100" />'.format(
            url=self.get_image_url()
        )
    get_image.allow_tags = True

    def get_image_url(self):
        image_name = 'missing' if self.missing_image else self.pk
        return 'https://s3.amazonaws.com/endorsementdb.com/images/endorsers/%s.png' % image_name

    def needs_quotes(self):
        current_endorsement = self.get_current_endorsement()
        return not current_endorsement.quote.text
    needs_quotes.boolean = True

    def get_absolute_url(self):
        return reverse('view-endorser', args=[self.pk]) class Source(models.Model):
    date = models.DateField(null=True, blank=True)
    url = models.URLField(unique=True, blank=True, max_length=300)
    name = models.CharField(max_length=100, default='')

    class Meta:
        ordering = ['url', '-date']

    def get_date_display(self):
        if self.date:
            return self.date.strftime('%b %d, %Y')

    def __unicode__(self):
        return "{url} on {date}".format(
            url=self.url,
            date=self.date,
        ) class Quote(models.Model):
    context = models.TextField(null=True, blank=True)
    text = models.TextField(blank=True, null=True)
    source = models.ForeignKey(Source)
    # Defaults to the date of the source, but can be overridden.
    date = models.DateField(null=True, blank=True)
    event = models.ForeignKey(Event, null=True, blank=True)

    def get_date_display(self):
        if self.date:
            return self.date.strftime('%b %d, %Y')

    def get_source_display(self):
        return '<a href="{url}">{name}</a>'.format(
            url=self.source.url,
            name=self.source.name or self.source.url[:30],
        )
    get_source_display.allow_tags = True

    def __unicode__(self):
        return self.text[:150]

    class Meta:
        ordering = ['date']

    def get_event_context(self):
        event = self.event
        if event:
            if self.date >= event.end_date:
                return 'After'
            elif self.date <= event.start_date:
                return 'Before'
            else:
                return 'During'
    get_event_context.short_description = 'Timing'

    def get_display(self):
        if self.context:
            if len(self.context) > 100:
                context = self.context[:100] + '...<br />'
            else:
                context = self.context + '<br />'
        else:
            context = ''

        if self.text:
            if len(self.text) > 100:
                text = self.text[:100] + '...'
            else:
                text = self.text
        else:
            text = ''

        return context + text
    get_display.short_description = 'Context + text'
    get_display.allow_tags = True class Comment(models.Model):
    quote = models.ForeignKey(Quote)
    candidate = models.ForeignKey(Candidate)
    polarity = models.CharField(
        max_length=1,
        choices=(
            ('+', 'Positive'),
            ('-', 'Negative'),
            ('?', 'Unclear or neutral'),
        )
    )
    endorser = models.ForeignKey(Endorser, null=True, blank=True)

    def get_truncated_quote(self):
        if len(self.quote.text) > 100:
            return self.quote.text[:100] + '...'
        else:
            return self.quote.text class Position(models.Model):
    colour = models.CharField(max_length=20, blank=True)
    past_tense_prefix = models.CharField(max_length=10, blank=True)
    present_tense_prefix = models.CharField(max_length=10, blank=True)
    suffix = models.CharField(max_length=30)
    slug = models.SlugField()
    show_on_load = models.BooleanField(default=False)

    def __unicode__(self):
        return self.get_name_display()

    def get_name_display(self):
        if self.present_tense_prefix:
            return "{present} {suffix}".format(
                present=self.present_tense_prefix,
                suffix=self.suffix
            )
        else:
            return self.suffix

    def get_present_display(self):
        if self.present_tense_prefix:
            return self.present_tense_prefix + ' ' + self.suffix
        else:
            return self.suffix

    def get_past_display(self):
        if self.past_tense_prefix:
            return self.past_tense_prefix + ' ' + self.suffix
        else:
            return self.suffix class Tag(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(blank=True)
    is_personal = models.BooleanField(default=True)
    category = models.ForeignKey(Category, null=True, blank=True)

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ['name']

    def get_mode(self):
        allow_personal = self.category.allow_personal
        allow_org = self.category.allow_org
        if allow_personal and allow_org:
            return 'none'
        elif allow_personal:
            return 'personal'
        else:
            return 'organization' class Category(models.Model):
    is_exclusive = models.BooleanField(default=False)
    name = models.CharField(max_length=30)
    allow_personal = models.BooleanField(default=True)
    allow_org = models.BooleanField(default=True)

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name_plural = 'categories' class Event(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(blank=True)
    start_date = models.DateField()
    end_date = models.DateField()

    def __unicode__(self):
        return self.name
from django import forms
from django.contrib import admin, messages
from django.utils.html import format_html
from endorsements.models import Account, Candidate, Endorsement, Endorser, \
                                Source, Quote, Comment, Position, Tag, \
                                Category, Event
        else:
            failures.append(instance.name)

    modeladmin.message_user(
        request,
        "Added tag {tag} for {n} endorsers (failures: {failures})".format(
            tag=tag.name,
            n=queryset.count(),
            failures=', '.join(failures),
        ),
        messages.SUCCESS,
    )


class ExcludedTagsFilter(admin.SimpleListFilter):
    title = 'excluded tags'
    parameter_name = 'excludedtags'

    def lookups(self, request, model_admin):
        return [(tag.pk, tag.name) for tag in Tag.objects.all()]

    def queryset(self, request, queryset):
        if self.value():
            return queryset.exclude(tags=self.value())
        else:
            return queryset


class ExcludedCategoriesFilter(admin.SimpleListFilter):
    title = 'excluded categories'
    parameter_name = 'excludedcategories'

    def lookups(self, request, model_admin):
        return [
            (category.pk, category.name)
            for category in Category.objects.all()
        ]

    def queryset(self, request, queryset):
        if self.value():
            tag_pks = [
                tag.pk
                for tag in Tag.objects.filter(category=self.value())
            ]
            return queryset.exclude(tags__in=tag_pks)
        else:
            return queryset


@admin.register(Endorser)
class EndorserAdmin(admin.ModelAdmin):
    action_form = EndorserActionForm
    actions = [add_tag]
    form = EndorserAdminForm
    list_filter = ('tags', 'is_personal', ExcludedTagsFilter,
                    ExcludedCategoriesFilter)
    list_display = ('name', 'get_image', 'max_followers', 'get_tags',
                    'current_position', 'is_personal')
    inlines = [
        EndorsementInline,
        CommentInline
    ]


class QuoteInline(admin.TabularInline):
    model = Quote
    extra = 1


@admin.register(Source)
class SourceAdmin(admin.ModelAdmin):
    list_display = ('url', 'date', 'name')
    inlines = [
        QuoteInline,
    ]


class QuoteActionForm(admin.helpers.ActionForm):
    event = forms.ModelChoiceField(Event.objects.all())


def change_event(modeladmin, request, queryset):
    event_pk = request.POST['event']
    try:
        event = Event.objects.get(pk=event_pk)
    except Event.DoesNotExist:
        modeladmin.message_user(
            request,
            "Could not find event with pk {event_pk}".format(
                event_pk=event_pk
            ),
            messages.ERROR,
        )
        return

    queryset.update(event=event)

    modeladmin.message_user(
        request,
        "Added event {event} for {n} quotes".format(
            event=event.name,
            n=queryset.count(),
        ),
        messages.SUCCESS,
    )


@admin.register(Quote)
class QuoteAdmin(admin.ModelAdmin):
    action_form = QuoteActionForm
    actions = [change_event]
    list_display = ('get_display', 'get_source_display',
                    'get_event_context', 'event', 'date')
    list_filter = ('event',)
    inlines = [
        EndorsementInline,
        CommentInline,
    ]



@admin.register(Position)