new data model: more tests
This commit is contained in:
parent
727254f3ce
commit
3511ac16a0
12 changed files with 133 additions and 22 deletions
|
@ -25,6 +25,7 @@ from .utils import *
|
||||||
|
|
||||||
class Edition(Item):
|
class Edition(Item):
|
||||||
category = ItemCategory.Book
|
category = ItemCategory.Book
|
||||||
|
url_path = 'book'
|
||||||
isbn = PrimaryLookupIdDescriptor(IdType.ISBN)
|
isbn = PrimaryLookupIdDescriptor(IdType.ISBN)
|
||||||
asin = PrimaryLookupIdDescriptor(IdType.ASIN)
|
asin = PrimaryLookupIdDescriptor(IdType.ASIN)
|
||||||
cubn = PrimaryLookupIdDescriptor(IdType.CUBN)
|
cubn = PrimaryLookupIdDescriptor(IdType.CUBN)
|
||||||
|
@ -60,6 +61,7 @@ class Edition(Item):
|
||||||
|
|
||||||
class Work(Item):
|
class Work(Item):
|
||||||
category = ItemCategory.Book
|
category = ItemCategory.Book
|
||||||
|
url_path = 'book/work'
|
||||||
douban_work = PrimaryLookupIdDescriptor(IdType.DoubanBook_Work)
|
douban_work = PrimaryLookupIdDescriptor(IdType.DoubanBook_Work)
|
||||||
goodreads_work = PrimaryLookupIdDescriptor(IdType.Goodreads_Work)
|
goodreads_work = PrimaryLookupIdDescriptor(IdType.Goodreads_Work)
|
||||||
editions = models.ManyToManyField(Edition, related_name='works')
|
editions = models.ManyToManyField(Edition, related_name='works')
|
||||||
|
@ -67,6 +69,7 @@ class Work(Item):
|
||||||
|
|
||||||
class Series(Item):
|
class Series(Item):
|
||||||
category = ItemCategory.Book
|
category = ItemCategory.Book
|
||||||
|
url_path = 'book/series'
|
||||||
# douban_serie = LookupIdDescriptor(IdType.DoubanBook_Serie)
|
# douban_serie = LookupIdDescriptor(IdType.DoubanBook_Serie)
|
||||||
# goodreads_serie = LookupIdDescriptor(IdType.Goodreads_Serie)
|
# goodreads_serie = LookupIdDescriptor(IdType.Goodreads_Serie)
|
||||||
|
|
||||||
|
|
6
catalog/collection/models.py
Normal file
6
catalog/collection/models.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from catalog.common import *
|
||||||
|
|
||||||
|
|
||||||
|
class Collection(Item):
|
||||||
|
category = ItemCategory.Collection
|
||||||
|
url_path = 'collection'
|
|
@ -66,6 +66,7 @@ class ItemType(models.TextChoices):
|
||||||
FanFic = 'fanfic', _('网文')
|
FanFic = 'fanfic', _('网文')
|
||||||
Performance = 'performance', _('演出')
|
Performance = 'performance', _('演出')
|
||||||
Exhibition = 'exhibition', _('展览')
|
Exhibition = 'exhibition', _('展览')
|
||||||
|
Collection = 'collection', _('收藏单')
|
||||||
|
|
||||||
|
|
||||||
class ItemCategory(models.TextChoices):
|
class ItemCategory(models.TextChoices):
|
||||||
|
@ -79,6 +80,7 @@ class ItemCategory(models.TextChoices):
|
||||||
FanFic = 'fanfic', _('网文')
|
FanFic = 'fanfic', _('网文')
|
||||||
Performance = 'performance', _('演出')
|
Performance = 'performance', _('演出')
|
||||||
Exhibition = 'exhibition', _('展览')
|
Exhibition = 'exhibition', _('展览')
|
||||||
|
Collection = 'collection', _('收藏单')
|
||||||
|
|
||||||
|
|
||||||
class SubItemType(models.TextChoices):
|
class SubItemType(models.TextChoices):
|
||||||
|
|
|
@ -3,6 +3,7 @@ from catalog.common import *
|
||||||
|
|
||||||
class Game(Item):
|
class Game(Item):
|
||||||
category = ItemCategory.Game
|
category = ItemCategory.Game
|
||||||
|
url_path = 'game'
|
||||||
igdb = PrimaryLookupIdDescriptor(IdType.IGDB)
|
igdb = PrimaryLookupIdDescriptor(IdType.IGDB)
|
||||||
steam = PrimaryLookupIdDescriptor(IdType.Steam)
|
steam = PrimaryLookupIdDescriptor(IdType.Steam)
|
||||||
douban_game = PrimaryLookupIdDescriptor(IdType.DoubanGame)
|
douban_game = PrimaryLookupIdDescriptor(IdType.DoubanGame)
|
||||||
|
|
|
@ -5,6 +5,7 @@ from .music.models import Album
|
||||||
from .game.models import Game
|
from .game.models import Game
|
||||||
from .podcast.models import Podcast
|
from .podcast.models import Podcast
|
||||||
from .performance.models import Performance
|
from .performance.models import Performance
|
||||||
|
from .collection.models import Collection as CatalogCollection
|
||||||
|
|
||||||
|
|
||||||
# class Exhibition(Item):
|
# class Exhibition(Item):
|
||||||
|
|
|
@ -3,6 +3,7 @@ from catalog.common import *
|
||||||
|
|
||||||
class Movie(Item):
|
class Movie(Item):
|
||||||
category = ItemCategory.Movie
|
category = ItemCategory.Movie
|
||||||
|
url_path = 'movie'
|
||||||
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
||||||
tmdb_movie = PrimaryLookupIdDescriptor(IdType.TMDB_Movie)
|
tmdb_movie = PrimaryLookupIdDescriptor(IdType.TMDB_Movie)
|
||||||
douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie)
|
douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from catalog.common import *
|
||||||
|
|
||||||
|
|
||||||
class Album(Item):
|
class Album(Item):
|
||||||
|
url_path = 'album'
|
||||||
category = ItemCategory.Music
|
category = ItemCategory.Music
|
||||||
barcode = PrimaryLookupIdDescriptor(IdType.GTIN)
|
barcode = PrimaryLookupIdDescriptor(IdType.GTIN)
|
||||||
douban_music = PrimaryLookupIdDescriptor(IdType.DoubanMusic)
|
douban_music = PrimaryLookupIdDescriptor(IdType.DoubanMusic)
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
class Performance(Item):
|
class Performance(Item):
|
||||||
category = ItemCategory.Performance
|
category = ItemCategory.Performance
|
||||||
|
url_path = 'performance'
|
||||||
douban_drama = LookupIdDescriptor(IdType.DoubanDrama)
|
douban_drama = LookupIdDescriptor(IdType.DoubanDrama)
|
||||||
versions = jsondata.ArrayField(_('版本'), null=False, blank=False, default=list)
|
versions = jsondata.ArrayField(_('版本'), null=False, blank=False, default=list)
|
||||||
directors = jsondata.ArrayField(_('导演'), null=False, blank=False, default=list)
|
directors = jsondata.ArrayField(_('导演'), null=False, blank=False, default=list)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from catalog.common import *
|
||||||
|
|
||||||
class Podcast(Item):
|
class Podcast(Item):
|
||||||
category = ItemCategory.Podcast
|
category = ItemCategory.Podcast
|
||||||
|
url_path = 'podcast'
|
||||||
feed_url = PrimaryLookupIdDescriptor(IdType.Feed)
|
feed_url = PrimaryLookupIdDescriptor(IdType.Feed)
|
||||||
apple_podcast = PrimaryLookupIdDescriptor(IdType.ApplePodcast)
|
apple_podcast = PrimaryLookupIdDescriptor(IdType.ApplePodcast)
|
||||||
# ximalaya = LookupIdDescriptor(IdType.Ximalaya)
|
# ximalaya = LookupIdDescriptor(IdType.Ximalaya)
|
||||||
|
|
|
@ -30,6 +30,7 @@ from django.db import models
|
||||||
|
|
||||||
class TVShow(Item):
|
class TVShow(Item):
|
||||||
category = ItemCategory.TV
|
category = ItemCategory.TV
|
||||||
|
url_path = 'tv'
|
||||||
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
||||||
tmdb_tv = PrimaryLookupIdDescriptor(IdType.TMDB_TV)
|
tmdb_tv = PrimaryLookupIdDescriptor(IdType.TMDB_TV)
|
||||||
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
||||||
|
@ -38,6 +39,7 @@ class TVShow(Item):
|
||||||
|
|
||||||
class TVSeason(Item):
|
class TVSeason(Item):
|
||||||
category = ItemCategory.TV
|
category = ItemCategory.TV
|
||||||
|
url_path = 'tv/season'
|
||||||
douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie)
|
douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie)
|
||||||
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
imdb = PrimaryLookupIdDescriptor(IdType.IMDB)
|
||||||
tmdb_tvseason = PrimaryLookupIdDescriptor(IdType.TMDB_TVSeason)
|
tmdb_tvseason = PrimaryLookupIdDescriptor(IdType.TMDB_TVSeason)
|
||||||
|
@ -58,6 +60,7 @@ class TVSeason(Item):
|
||||||
|
|
||||||
class TVEpisode(Item):
|
class TVEpisode(Item):
|
||||||
category = ItemCategory.TV
|
category = ItemCategory.TV
|
||||||
|
url_path = 'tv/episode'
|
||||||
show = models.ForeignKey(TVShow, null=True, on_delete=models.SET_NULL, related_name='episodes')
|
show = models.ForeignKey(TVShow, null=True, on_delete=models.SET_NULL, related_name='episodes')
|
||||||
season = models.ForeignKey(TVSeason, null=True, on_delete=models.SET_NULL, related_name='episodes')
|
season = models.ForeignKey(TVSeason, null=True, on_delete=models.SET_NULL, related_name='episodes')
|
||||||
episode_number = models.PositiveIntegerField()
|
episode_number = models.PositiveIntegerField()
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.db import models
|
||||||
from polymorphic.models import PolymorphicModel
|
from polymorphic.models import PolymorphicModel
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from catalog.common.models import Item, ItemCategory
|
from catalog.common.models import Item, ItemCategory
|
||||||
|
from catalog.collection.models import Collection as CatalogCollection
|
||||||
from decimal import *
|
from decimal import *
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from markdownx.models import MarkdownxField
|
from markdownx.models import MarkdownxField
|
||||||
|
@ -11,17 +12,27 @@ from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
from django.db.models import Count
|
||||||
|
|
||||||
|
|
||||||
class UserOwnedEntity(PolymorphicModel):
|
class Piece(PolymorphicModel):
|
||||||
class Meta:
|
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||||
abstract = True
|
|
||||||
|
|
||||||
owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name='%(class)ss')
|
|
||||||
visibility = models.PositiveSmallIntegerField(default=0) # 0: Public / 1: Follower only / 2: Self only
|
visibility = models.PositiveSmallIntegerField(default=0) # 0: Public / 1: Follower only / 2: Self only
|
||||||
metadata = models.JSONField(default=dict)
|
metadata = models.JSONField(default=dict)
|
||||||
created_time = models.DateTimeField(auto_now_add=True)
|
created_time = models.DateTimeField(auto_now_add=True)
|
||||||
edited_time = models.DateTimeField(auto_now=True)
|
edited_time = models.DateTimeField(auto_now=True)
|
||||||
|
is_deleted = models.BooleanField(default=False, db_index=True)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete(self, using=None, soft=True, *args, **kwargs):
|
||||||
|
if soft:
|
||||||
|
self.clear()
|
||||||
|
self.is_deleted = True
|
||||||
|
self.save(using=using)
|
||||||
|
else:
|
||||||
|
return super().delete(using=using, *args, **kwargs)
|
||||||
|
|
||||||
def is_visible_to(self, viewer):
|
def is_visible_to(self, viewer):
|
||||||
if not viewer.is_authenticated:
|
if not viewer.is_authenticated:
|
||||||
|
@ -52,7 +63,7 @@ class UserOwnedEntity(PolymorphicModel):
|
||||||
return visible_entities
|
return visible_entities
|
||||||
|
|
||||||
|
|
||||||
class Content(UserOwnedEntity):
|
class Content(Piece):
|
||||||
target: models.ForeignKey(Item, on_delete=models.PROTECT)
|
target: models.ForeignKey(Item, on_delete=models.PROTECT)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -71,7 +82,7 @@ class Review(Content):
|
||||||
|
|
||||||
|
|
||||||
class Rating(Content):
|
class Rating(Content):
|
||||||
grade = models.IntegerField(default=1, validators=[MaxValueValidator(10), MinValueValidator(0)])
|
grade = models.IntegerField(default=0, validators=[MaxValueValidator(10), MinValueValidator(0)])
|
||||||
|
|
||||||
|
|
||||||
class Reply(Content):
|
class Reply(Content):
|
||||||
|
@ -86,10 +97,16 @@ List (abstract class)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class List(UserOwnedEntity):
|
class List(Piece):
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
_owner = models.ForeignKey(User, on_delete=models.PROTECT) # duplicated owner field to make unique key possible for subclasses
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self._owner = self.owner
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
MEMBER_CLASS = None # subclass must override this
|
MEMBER_CLASS = None # subclass must override this
|
||||||
# subclass must add this:
|
# subclass must add this:
|
||||||
# items = models.ManyToManyField(Item, through='ListMember')
|
# items = models.ManyToManyField(Item, through='ListMember')
|
||||||
|
@ -100,7 +117,7 @@ class List(UserOwnedEntity):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ordered_items(self):
|
def ordered_items(self):
|
||||||
return self.items.all().order_by('collectionmember__position')
|
return self.items.all().order_by(self.MEMBER_CLASS.__name__.lower() + '__position')
|
||||||
|
|
||||||
def has_item(self, item):
|
def has_item(self, item):
|
||||||
return self.members.filter(item=item).count() > 0
|
return self.members.filter(item=item).count() > 0
|
||||||
|
@ -151,7 +168,7 @@ class ListMember(models.Model):
|
||||||
item = models.ForeignKey(Item, on_delete=models.PROTECT)
|
item = models.ForeignKey(Item, on_delete=models.PROTECT)
|
||||||
position = models.PositiveIntegerField()
|
position = models.PositiveIntegerField()
|
||||||
metadata = models.JSONField(default=dict)
|
metadata = models.JSONField(default=dict)
|
||||||
comment = models.ForeignKey(Review, on_delete=models.SET_NULL, null=True)
|
comment = models.ForeignKey(Review, on_delete=models.PROTECT, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
@ -195,10 +212,10 @@ class QueueMember(ListMember):
|
||||||
|
|
||||||
class Queue(List):
|
class Queue(List):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [['owner', 'item_category', 'queue_type']]
|
unique_together = [['_owner', 'item_category', 'queue_type']]
|
||||||
|
|
||||||
MEMBER_CLASS = QueueMember
|
MEMBER_CLASS = QueueMember
|
||||||
items = models.ManyToManyField(Item, through='QueueMember', related_name=None)
|
items = models.ManyToManyField(Item, through='QueueMember', related_name="+")
|
||||||
item_category = models.CharField(choices=ItemCategory.choices, max_length=100, null=False, blank=False)
|
item_category = models.CharField(choices=ItemCategory.choices, max_length=100, null=False, blank=False)
|
||||||
queue_type = models.CharField(choices=QueueType.choices, max_length=100, null=False, blank=False)
|
queue_type = models.CharField(choices=QueueType.choices, max_length=100, null=False, blank=False)
|
||||||
|
|
||||||
|
@ -216,7 +233,7 @@ class Queue(List):
|
||||||
|
|
||||||
|
|
||||||
class QueueLogEntry(models.Model):
|
class QueueLogEntry(models.Model):
|
||||||
owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name='%(class)ss')
|
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||||
queue = models.ForeignKey(Queue, on_delete=models.PROTECT, related_name='entries', null=True) # None means removed from any queue
|
queue = models.ForeignKey(Queue, on_delete=models.PROTECT, related_name='entries', null=True) # None means removed from any queue
|
||||||
item = models.ForeignKey(Item, on_delete=models.PROTECT)
|
item = models.ForeignKey(Item, on_delete=models.PROTECT)
|
||||||
metadata = models.JSONField(default=dict)
|
metadata = models.JSONField(default=dict)
|
||||||
|
@ -241,16 +258,17 @@ class QueueManager:
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
for ic in ItemCategory:
|
for ic in ItemCategory:
|
||||||
for qt in QueueType:
|
if ic != ItemCategory.Collection:
|
||||||
Queue.objects.create(owner=self.owner, item_category=ic, queue_type=qt)
|
for qt in QueueType:
|
||||||
|
Queue.objects.create(owner=self.owner, item_category=ic, queue_type=qt)
|
||||||
|
|
||||||
def _queue_member_for_item(self, item):
|
def _queue_member_for_item(self, item):
|
||||||
return QueueMember.objects.filter(item=item, queue__in=self.owner.queues.all()).first()
|
return QueueMember.objects.filter(item=item, queue__in=self.owner.queue_set.all()).first()
|
||||||
|
|
||||||
def _queue_for_item_and_type(item, queue_type):
|
def _queue_for_item_and_type(item, queue_type):
|
||||||
if not item or not queue_type:
|
if not item or not queue_type:
|
||||||
return None
|
return None
|
||||||
return self.owner.queues.all().filter(item_category=item.category, queue_type=queue_type)
|
return self.owner.queue_set.all().filter(item_category=item.category, queue_type=queue_type)
|
||||||
|
|
||||||
def update_for_item(self, item, queue_type, metadata=None):
|
def update_for_item(self, item, queue_type, metadata=None):
|
||||||
# None means no change for metadata, comment
|
# None means no change for metadata, comment
|
||||||
|
@ -281,7 +299,7 @@ class QueueManager:
|
||||||
return QueueLogEntry.objects.filter(owner=self.owner, item=item)
|
return QueueLogEntry.objects.filter(owner=self.owner, item=item)
|
||||||
|
|
||||||
def get_queue(self, item_category, queue_type):
|
def get_queue(self, item_category, queue_type):
|
||||||
return self.owner.queues.all().filter(item_category=item_category, queue_type=queue_type).first()
|
return self.owner.queue_set.all().filter(item_category=item_category, queue_type=queue_type).first()
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -293,8 +311,11 @@ class CollectionMember(ListMember):
|
||||||
collection = models.ForeignKey('Collection', related_name='members', on_delete=models.CASCADE)
|
collection = models.ForeignKey('Collection', related_name='members', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
class Collection(Item, List):
|
class Collection(List):
|
||||||
MEMBER_CLASS = CollectionMember
|
MEMBER_CLASS = CollectionMember
|
||||||
|
catalog_item = models.OneToOneField(CatalogCollection, on_delete=models.PROTECT)
|
||||||
|
title = models.CharField(_("title in primary language"), max_length=1000, default="")
|
||||||
|
brief = models.TextField(_("简介"), blank=True, default="")
|
||||||
items = models.ManyToManyField(Item, through='CollectionMember', related_name="collections")
|
items = models.ManyToManyField(Item, through='CollectionMember', related_name="collections")
|
||||||
collaborative = models.PositiveSmallIntegerField(default=0) # 0: Editable by owner only / 1: Editable by bi-direction followers
|
collaborative = models.PositiveSmallIntegerField(default=0) # 0: Editable by owner only / 1: Editable by bi-direction followers
|
||||||
|
|
||||||
|
@ -303,6 +324,15 @@ class Collection(Item, List):
|
||||||
html = markdown(self.description)
|
html = markdown(self.description)
|
||||||
return RE_HTML_TAG.sub(' ', html)
|
return RE_HTML_TAG.sub(' ', html)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if getattr(self, 'catalog_item', None) is None:
|
||||||
|
self.catalog_item = CatalogCollection()
|
||||||
|
if self.catalog_item.title != self.title or self.catalog_item.brief != self.brief:
|
||||||
|
self.catalog_item.title = self.title
|
||||||
|
self.catalog_item.brief = self.brief
|
||||||
|
self.catalog_item.save()
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Tag
|
Tag
|
||||||
|
@ -317,10 +347,37 @@ TagValidators = [RegexValidator(regex=r'\s+', inverse_match=True)]
|
||||||
|
|
||||||
|
|
||||||
class Tag(List):
|
class Tag(List):
|
||||||
MEMBER_CLASS = CollectionMember
|
MEMBER_CLASS = TagMember
|
||||||
|
items = models.ManyToManyField(Item, through='TagMember')
|
||||||
title = models.CharField(max_length=100, null=False, blank=False, validators=TagValidators)
|
title = models.CharField(max_length=100, null=False, blank=False, validators=TagValidators)
|
||||||
# TODO case convert and space removal on save
|
# TODO case convert and space removal on save
|
||||||
# TODO check on save
|
# TODO check on save
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [['owner', 'title']]
|
unique_together = [['_owner', 'title']]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cleanup_title(title):
|
||||||
|
return title.strip().lower()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def public_tags_for_item(item):
|
||||||
|
tags = item.tag_set.all().filter(visibility=0).values('title').annotate(frequency=Count('owner')).order_by('-frequency')
|
||||||
|
return list(map(lambda t: t['title'], tags))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def all_tags_for_user(user):
|
||||||
|
tags = user.tag_set.all().values('title').annotate(frequency=Count('members')).order_by('-frequency')
|
||||||
|
return list(map(lambda t: t['title'], tags))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_tag_by_user(item, tag_title, user, default_visibility=0):
|
||||||
|
title = Tag.cleanup_title(tag_title)
|
||||||
|
tag = Tag.objects.filter(owner=user, title=title).first()
|
||||||
|
if not tag:
|
||||||
|
tag = Tag.objects.create(owner=user, title=title, visibility=default_visibility)
|
||||||
|
tag.append_item(item)
|
||||||
|
|
||||||
|
|
||||||
|
Item.tags = property(Tag.public_tags_for_item)
|
||||||
|
User.tags = property(Tag.all_tags_for_user)
|
||||||
|
|
|
@ -13,6 +13,8 @@ class CollectionTest(TestCase):
|
||||||
|
|
||||||
def test_collection(self):
|
def test_collection(self):
|
||||||
collection = Collection.objects.create(title="test", owner=self.user)
|
collection = Collection.objects.create(title="test", owner=self.user)
|
||||||
|
collection = Collection.objects.filter(title="test", owner=self.user).first()
|
||||||
|
self.assertEqual(collection.catalog_item.title, "test")
|
||||||
collection.append_item(self.book1)
|
collection.append_item(self.book1)
|
||||||
collection.append_item(self.book2)
|
collection.append_item(self.book2)
|
||||||
self.assertEqual(list(collection.ordered_items), [self.book1, self.book2])
|
self.assertEqual(list(collection.ordered_items), [self.book1, self.book2])
|
||||||
|
@ -30,7 +32,7 @@ class QueueTest(TestCase):
|
||||||
user = User.objects.create(mastodon_site="site", username="name")
|
user = User.objects.create(mastodon_site="site", username="name")
|
||||||
queue_manager = QueueManager(user=user)
|
queue_manager = QueueManager(user=user)
|
||||||
queue_manager.initialize()
|
queue_manager.initialize()
|
||||||
self.assertEqual(user.queues.all().count(), 30)
|
self.assertEqual(user.queue_set.all().count(), 30)
|
||||||
book1 = Edition.objects.create(title="Hyperion")
|
book1 = Edition.objects.create(title="Hyperion")
|
||||||
book2 = Edition.objects.create(title="Andymion")
|
book2 = Edition.objects.create(title="Andymion")
|
||||||
q1 = queue_manager.get_queue(ItemCategory.Book, QueueType.WISHED)
|
q1 = queue_manager.get_queue(ItemCategory.Book, QueueType.WISHED)
|
||||||
|
@ -63,3 +65,35 @@ class QueueTest(TestCase):
|
||||||
queue_manager.update_for_item(book1, QueueType.STARTED, metadata={'progress': 100})
|
queue_manager.update_for_item(book1, QueueType.STARTED, metadata={'progress': 100})
|
||||||
log = queue_manager.get_log_for_item(book1)
|
log = queue_manager.get_log_for_item(book1)
|
||||||
self.assertEqual(log.count(), 5)
|
self.assertEqual(log.count(), 5)
|
||||||
|
|
||||||
|
|
||||||
|
class TagTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.book1 = Edition.objects.create(title="Hyperion")
|
||||||
|
self.book2 = Edition.objects.create(title="Andymion")
|
||||||
|
self.movie1 = Edition.objects.create(title="Hyperion, The Movie")
|
||||||
|
self.user1 = User.objects.create(mastodon_site="site", username="name")
|
||||||
|
self.user2 = User.objects.create(mastodon_site="site2", username="name2")
|
||||||
|
self.user3 = User.objects.create(mastodon_site="site2", username="name3")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_tag(self):
|
||||||
|
t1 = 'sci-fi'
|
||||||
|
t2 = 'private'
|
||||||
|
t3 = 'public'
|
||||||
|
Tag.add_tag_by_user(self.book1, t3, self.user2)
|
||||||
|
Tag.add_tag_by_user(self.book1, t1, self.user1)
|
||||||
|
Tag.add_tag_by_user(self.book1, t1, self.user2)
|
||||||
|
Tag.add_tag_by_user(self.book1, t2, self.user1, default_visibility=2)
|
||||||
|
self.assertEqual(self.book1.tags, [t1, t3])
|
||||||
|
Tag.add_tag_by_user(self.book1, t3, self.user1)
|
||||||
|
Tag.add_tag_by_user(self.book1, t3, self.user3)
|
||||||
|
self.assertEqual(self.book1.tags, [t3, t1])
|
||||||
|
Tag.add_tag_by_user(self.book1, t3, self.user3)
|
||||||
|
Tag.add_tag_by_user(self.book1, t3, self.user3)
|
||||||
|
self.assertEqual(Tag.objects.count(), 6)
|
||||||
|
Tag.add_tag_by_user(self.book2, t1, self.user2)
|
||||||
|
self.assertEqual(self.user2.tags, [t1, t3])
|
||||||
|
Tag.add_tag_by_user(self.book2, t3, self.user2)
|
||||||
|
Tag.add_tag_by_user(self.movie1, t3, self.user2)
|
||||||
|
self.assertEqual(self.user2.tags, [t3, t1])
|
||||||
|
|
Loading…
Add table
Reference in a new issue