tag.py
149 lines
| 4.4 KiB
| text/x-python
|
PythonLexer
neko259
|
r1338 | import hashlib | |
neko259
|
r1590 | from boards.models.attachment import FILE_TYPES_IMAGE | |
neko259
|
r692 | from django.template.loader import render_to_string | |
neko259
|
r386 | from django.db import models | |
neko259
|
r928 | from django.db.models import Count | |
neko259
|
r692 | from django.core.urlresolvers import reverse | |
neko259
|
r732 | ||
neko259
|
r1590 | from boards.models import Attachment | |
neko259
|
r692 | from boards.models.base import Viewable | |
neko259
|
r1414 | from boards.models.thread import STATUS_ACTIVE, STATUS_BUMPLIMIT, STATUS_ARCHIVE | |
neko259
|
r973 | from boards.utils import cached_result | |
neko259
|
r1264 | import boards | |
neko259
|
r732 | ||
neko259
|
r385 | __author__ = 'neko259' | |
neko259
|
r1270 | RELATED_TAGS_COUNT = 5 | |
neko259
|
r385 | class TagManager(models.Manager): | |
def get_not_empty_tags(self): | |||
neko259
|
r623 | """ | |
Gets tags that have non-archived threads. | |||
""" | |||
neko259
|
r1269 | return self.annotate(num_threads=Count('thread_tags')).filter(num_threads__gt=0)\ | |
neko259
|
r932 | .order_by('-required', 'name') | |
neko259
|
r385 | ||
neko259
|
r1027 | def get_tag_url_list(self, tags: list) -> str: | |
""" | |||
Gets a comma-separated list of tag links. | |||
""" | |||
return ', '.join([tag.get_view() for tag in tags]) | |||
neko259
|
r385 | ||
neko259
|
r692 | class Tag(models.Model, Viewable): | |
neko259
|
r385 | """ | |
neko259
|
r398 | A tag is a text node assigned to the thread. The tag serves as a board | |
section. There can be multiple tags for each thread | |||
neko259
|
r385 | """ | |
objects = TagManager() | |||
class Meta: | |||
app_label = 'boards' | |||
neko259
|
r649 | ordering = ('name',) | |
neko259
|
r385 | ||
neko259
|
r983 | name = models.CharField(max_length=100, db_index=True, unique=True) | |
required = models.BooleanField(default=False, db_index=True) | |||
neko259
|
r1258 | description = models.TextField(blank=True) | |
neko259
|
r385 | ||
neko259
|
r1367 | parent = models.ForeignKey('Tag', null=True, blank=True, | |
related_name='children') | |||
neko259
|
r1348 | ||
neko259
|
r875 | def __str__(self): | |
neko259
|
r385 | return self.name | |
neko259
|
r908 | def is_empty(self) -> bool: | |
neko259
|
r623 | """ | |
Checks if the tag has some threads. | |||
""" | |||
neko259
|
r606 | return self.get_thread_count() == 0 | |
neko259
|
r385 | ||
neko259
|
r1414 | def get_thread_count(self, status=None) -> int: | |
neko259
|
r1260 | threads = self.get_threads() | |
neko259
|
r1414 | if status is not None: | |
threads = threads.filter(status=status) | |||
neko259
|
r1260 | return threads.count() | |
def get_active_thread_count(self) -> int: | |||
neko259
|
r1414 | return self.get_thread_count(status=STATUS_ACTIVE) | |
neko259
|
r1383 | ||
def get_bumplimit_thread_count(self) -> int: | |||
neko259
|
r1414 | return self.get_thread_count(status=STATUS_BUMPLIMIT) | |
neko259
|
r385 | ||
neko259
|
r1364 | def get_archived_thread_count(self) -> int: | |
neko259
|
r1414 | return self.get_thread_count(status=STATUS_ARCHIVE) | |
neko259
|
r1364 | ||
neko259
|
r1160 | def get_absolute_url(self): | |
neko259
|
r692 | return reverse('tag', kwargs={'tag_name': self.name}) | |
neko259
|
r908 | def get_threads(self): | |
neko259
|
r1269 | return self.thread_tags.order_by('-bump_time') | |
neko259
|
r922 | ||
def is_required(self): | |||
return self.required | |||
def get_view(self): | |||
link = '<a class="tag" href="{}">{}</a>'.format( | |||
neko259
|
r1150 | self.get_absolute_url(), self.name) | |
neko259
|
r922 | if self.is_required(): | |
link = '<b>{}</b>'.format(link) | |||
return link | |||
def get_search_view(self, *args, **kwargs): | |||
return render_to_string('boards/tag.html', { | |||
neko259
|
r928 | 'tag': self, | |
}) | |||
neko259
|
r960 | ||
neko259
|
r1107 | @cached_result() | |
neko259
|
r960 | def get_post_count(self): | |
neko259
|
r1345 | return self.get_threads().aggregate(num_posts=Count('multi_replies'))['num_posts'] | |
neko259
|
r1258 | ||
def get_description(self): | |||
return self.description | |||
neko259
|
r1264 | ||
neko259
|
r1417 | def get_random_image_post(self, status=[STATUS_ACTIVE, STATUS_BUMPLIMIT]): | |
neko259
|
r1590 | posts = boards.models.Post.objects.filter(attachments__mimetype__in=FILE_TYPES_IMAGE)\ | |
.annotate(images_count=Count( | |||
'attachments')).filter(images_count__gt=0, threads__tags__in=[self]) | |||
neko259
|
r1414 | if status is not None: | |
neko259
|
r1417 | posts = posts.filter(thread__status__in=status) | |
neko259
|
r1265 | return posts.order_by('?').first() | |
neko259
|
r1264 | ||
neko259
|
r1267 | def get_first_letter(self): | |
return self.name and self.name[0] or '' | |||
neko259
|
r1269 | ||
def get_related_tags(self): | |||
neko259
|
r1270 | return set(Tag.objects.filter(thread_tags__in=self.get_threads()).exclude( | |
neko259
|
r1338 | id=self.id).order_by('?')[:RELATED_TAGS_COUNT]) | |
@cached_result() | |||
def get_color(self): | |||
""" | |||
Gets color hashed from the tag name. | |||
""" | |||
return hashlib.md5(self.name.encode()).hexdigest()[:6] | |||
neko259
|
r1348 | ||
def get_parent(self): | |||
return self.parent | |||
def get_all_parents(self): | |||
neko259
|
r1361 | parents = list() | |
neko259
|
r1348 | parent = self.get_parent() | |
if parent and parent not in parents: | |||
neko259
|
r1361 | parents.insert(0, parent) | |
parents = parent.get_all_parents() + parents | |||
neko259
|
r1348 | ||
return parents | |||
def get_children(self): | |||
return self.children | |||
neko259
|
r1419 | ||
def get_images(self): | |||
neko259
|
r1590 | return Attachment.objects.filter( | |
neko259
|
r1591 | attachment_posts__thread__tags__in=[self]).filter( | |
mimetype__in=FILE_TYPES_IMAGE).order_by('-attachment_posts__pub_time') |