song->album; refactor shelf
This commit is contained in:
parent
f24df6c0fd
commit
bde7ce47a3
7 changed files with 261 additions and 203 deletions
|
@ -65,7 +65,7 @@ def query_item_category(item_category):
|
||||||
# return q
|
# return q
|
||||||
ct = all_content_types()
|
ct = all_content_types()
|
||||||
contenttype_ids = [ct[cls] for cls in classes]
|
contenttype_ids = [ct[cls] for cls in classes]
|
||||||
return Q(item__polymorphic_ctype__in=sorted(contenttype_ids))
|
return Q(item__polymorphic_ctype__in=contenttype_ids)
|
||||||
|
|
||||||
|
|
||||||
class Piece(PolymorphicModel, UserOwnedObjectMixin):
|
class Piece(PolymorphicModel, UserOwnedObjectMixin):
|
||||||
|
@ -325,6 +325,9 @@ class List(Piece):
|
||||||
def recent_members(self):
|
def recent_members(self):
|
||||||
return self.members.all().order_by("-created_time")
|
return self.members.all().order_by("-created_time")
|
||||||
|
|
||||||
|
def get_members_in_category(self, item_category):
|
||||||
|
return self.members.all().filter(query_item_category(item_category))
|
||||||
|
|
||||||
def get_member_for_item(self, item):
|
def get_member_for_item(self, item):
|
||||||
return self.members.filter(item=item).first()
|
return self.members.filter(item=item).first()
|
||||||
|
|
||||||
|
@ -459,44 +462,16 @@ class ShelfMember(ListMember):
|
||||||
|
|
||||||
class Shelf(List):
|
class Shelf(List):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [["owner", "item_category", "shelf_type"]]
|
unique_together = [["owner", "shelf_type"]]
|
||||||
|
|
||||||
MEMBER_CLASS = ShelfMember
|
MEMBER_CLASS = ShelfMember
|
||||||
items = models.ManyToManyField(Item, through="ShelfMember", related_name="+")
|
items = models.ManyToManyField(Item, through="ShelfMember", related_name="+")
|
||||||
item_category = models.CharField(
|
|
||||||
choices=ItemCategory.choices, max_length=100, null=False, blank=False
|
|
||||||
)
|
|
||||||
shelf_type = models.CharField(
|
shelf_type = models.CharField(
|
||||||
choices=ShelfType.choices, max_length=100, null=False, blank=False
|
choices=ShelfType.choices, max_length=100, null=False, blank=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.id} {self.title}"
|
return f"{self.id} [{self.owner} {self.shelf_type} list]"
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def item_category_label(self):
|
|
||||||
return ItemCategory(self.item_category).label
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def shelf_label(self):
|
|
||||||
return next(
|
|
||||||
iter(
|
|
||||||
[
|
|
||||||
n[2]
|
|
||||||
for n in iter(ShelfTypeNames)
|
|
||||||
if n[0] == self.item_category and n[1] == self.shelf_type
|
|
||||||
]
|
|
||||||
),
|
|
||||||
self.shelf_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def title(self):
|
|
||||||
q = _("{shelf_label}的{item_category}").format(
|
|
||||||
shelf_label=self.shelf_label, item_category=self.item_category_label
|
|
||||||
)
|
|
||||||
return q
|
|
||||||
# return _("{user}'s {shelf_name}").format(user=self.owner.mastodon_username, shelf_name=q)
|
|
||||||
|
|
||||||
|
|
||||||
class ShelfLogEntry(models.Model):
|
class ShelfLogEntry(models.Model):
|
||||||
|
@ -526,39 +501,31 @@ class ShelfManager:
|
||||||
|
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.owner = user
|
self.owner = user
|
||||||
|
qs = Shelf.objects.filter(owner=self.owner)
|
||||||
|
self.shelf_list = {v.shelf_type: v for v in qs}
|
||||||
|
if len(self.shelf_list) == 0:
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
for ic in ItemCategory:
|
for qt in ShelfType:
|
||||||
for qt in ShelfType:
|
self.shelf_list[qt] = Shelf.objects.create(owner=self.owner, shelf_type=qt)
|
||||||
Shelf.objects.create(owner=self.owner, item_category=ic, shelf_type=qt)
|
|
||||||
|
|
||||||
def _shelf_member_for_item(self, item):
|
def locate_item(self, item) -> ShelfMember:
|
||||||
return ShelfMember.objects.filter(
|
return ShelfMember.objects.filter(
|
||||||
item=item, parent__in=self.owner.shelf_set.all()
|
item=item, parent__in=list(self.shelf_list.values())
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
def _shelf_for_item_and_type(self, item, shelf_type):
|
|
||||||
if not item or not shelf_type:
|
|
||||||
return None
|
|
||||||
return self.owner.shelf_set.all().filter(
|
|
||||||
item_category=item.category, shelf_type=shelf_type
|
|
||||||
)
|
|
||||||
|
|
||||||
def locate_item(self, item):
|
|
||||||
member = ShelfMember.objects.filter(owner=self.owner, item=item).first()
|
|
||||||
return member # .parent if member else None
|
|
||||||
|
|
||||||
def move_item(self, item, shelf_type, visibility=0, metadata=None):
|
def move_item(self, item, shelf_type, visibility=0, metadata=None):
|
||||||
# shelf_type=None means remove from current shelf
|
# shelf_type=None means remove from current shelf
|
||||||
# metadata=None means no change
|
# metadata=None means no change
|
||||||
if not item:
|
if not item:
|
||||||
raise ValueError("empty item")
|
raise ValueError("empty item")
|
||||||
new_shelfmember = None
|
new_shelfmember = None
|
||||||
last_shelfmember = self._shelf_member_for_item(item)
|
last_shelfmember = self.locate_item(item)
|
||||||
last_shelf = last_shelfmember.parent if last_shelfmember else None
|
last_shelf = last_shelfmember.parent if last_shelfmember else None
|
||||||
last_metadata = last_shelfmember.metadata if last_shelfmember else None
|
last_metadata = last_shelfmember.metadata if last_shelfmember else None
|
||||||
last_visibility = last_shelfmember.visibility if last_shelfmember else None
|
last_visibility = last_shelfmember.visibility if last_shelfmember else None
|
||||||
shelf = self.get_shelf(item.category, shelf_type) if shelf_type else None
|
shelf = self.shelf_list[shelf_type] if shelf_type else None
|
||||||
changed = False
|
changed = False
|
||||||
if last_shelf != shelf: # change shelf
|
if last_shelf != shelf: # change shelf
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -596,20 +563,29 @@ class ShelfManager:
|
||||||
"timestamp"
|
"timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_shelf(self, item_category, shelf_type):
|
def get_shelf(self, shelf_type):
|
||||||
return (
|
return self.shelf_list[shelf_type]
|
||||||
self.owner.shelf_set.all()
|
|
||||||
.filter(item_category=item_category, shelf_type=shelf_type)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_items_on_shelf(self, item_category, shelf_type):
|
def get_members(self, shelf_type, item_category):
|
||||||
shelf = (
|
return self.shelf_list[shelf_type].get_members_in_category(item_category)
|
||||||
self.owner.shelf_set.all()
|
|
||||||
.filter(item_category=item_category, shelf_type=shelf_type)
|
# def get_items_on_shelf(self, item_category, shelf_type):
|
||||||
.first()
|
# shelf = (
|
||||||
|
# self.owner.shelf_set.all()
|
||||||
|
# .filter(item_category=item_category, shelf_type=shelf_type)
|
||||||
|
# .first()
|
||||||
|
# )
|
||||||
|
# return shelf.members.all().order_by
|
||||||
|
|
||||||
|
def get_title(self, shelf_type, item_category):
|
||||||
|
ic = ItemCategory(item_category).label
|
||||||
|
sts = [
|
||||||
|
n[2] for n in ShelfTypeNames if n[0] == item_category and n[1] == shelf_type
|
||||||
|
]
|
||||||
|
st = sts[0] if sts else shelf_type
|
||||||
|
return _("{shelf_label}的{item_category}").format(
|
||||||
|
shelf_label=st, item_category=ic
|
||||||
)
|
)
|
||||||
return shelf.members.all().order_by
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_manager_for_user(user):
|
def get_manager_for_user(user):
|
||||||
|
@ -820,7 +796,11 @@ class Mark:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shelf_label(self):
|
def shelf_label(self):
|
||||||
return self.shelfmember.parent.shelf_label if self.shelfmember else None
|
return (
|
||||||
|
self.owner.shelf_manager.get_title(self.shelf_type, self.item.category)
|
||||||
|
if self.shelfmember
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_time(self):
|
def created_time(self):
|
||||||
|
|
|
@ -35,12 +35,11 @@ class ShelfTest(TestCase):
|
||||||
def test_shelf(self):
|
def test_shelf(self):
|
||||||
user = User.objects.create(mastodon_site="site", username="name")
|
user = User.objects.create(mastodon_site="site", username="name")
|
||||||
shelf_manager = ShelfManager(user=user)
|
shelf_manager = ShelfManager(user=user)
|
||||||
shelf_manager.initialize()
|
self.assertEqual(user.shelf_set.all().count(), 3)
|
||||||
self.assertEqual(user.shelf_set.all().count(), 33)
|
|
||||||
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 = shelf_manager.get_shelf(ItemCategory.Book, ShelfType.WISHLIST)
|
q1 = shelf_manager.get_shelf(ShelfType.WISHLIST)
|
||||||
q2 = shelf_manager.get_shelf(ItemCategory.Book, ShelfType.PROGRESS)
|
q2 = shelf_manager.get_shelf(ShelfType.PROGRESS)
|
||||||
self.assertIsNotNone(q1)
|
self.assertIsNotNone(q1)
|
||||||
self.assertIsNotNone(q2)
|
self.assertIsNotNone(q2)
|
||||||
self.assertEqual(q1.members.all().count(), 0)
|
self.assertEqual(q1.members.all().count(), 0)
|
||||||
|
@ -123,7 +122,6 @@ class MarkTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.book1 = Edition.objects.create(title="Hyperion")
|
self.book1 = Edition.objects.create(title="Hyperion")
|
||||||
self.user1 = User.objects.create(mastodon_site="site", username="name")
|
self.user1 = User.objects.create(mastodon_site="site", username="name")
|
||||||
self.user1.shelf_manager.initialize()
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_mark(self):
|
def test_mark(self):
|
||||||
|
@ -139,7 +137,7 @@ class MarkTest(TestCase):
|
||||||
|
|
||||||
mark = Mark(self.user1, self.book1)
|
mark = Mark(self.user1, self.book1)
|
||||||
self.assertEqual(mark.shelf_type, ShelfType.WISHLIST)
|
self.assertEqual(mark.shelf_type, ShelfType.WISHLIST)
|
||||||
self.assertEqual(mark.shelf_label, "想读")
|
self.assertEqual(mark.shelf_label, "想读的书")
|
||||||
self.assertEqual(mark.text, "a gentle comment")
|
self.assertEqual(mark.text, "a gentle comment")
|
||||||
self.assertEqual(mark.rating, 9)
|
self.assertEqual(mark.rating, 9)
|
||||||
self.assertEqual(mark.visibility, 1)
|
self.assertEqual(mark.visibility, 1)
|
||||||
|
|
|
@ -380,8 +380,7 @@ def _render_list(
|
||||||
):
|
):
|
||||||
return render_user_blocked(request)
|
return render_user_blocked(request)
|
||||||
if type == "mark":
|
if type == "mark":
|
||||||
shelf = user.shelf_manager.get_shelf(item_category, shelf_type)
|
queryset = user.shelf_manager.get_members(shelf_type, item_category)
|
||||||
queryset = ShelfMember.objects.filter(owner=user, parent=shelf)
|
|
||||||
elif type == "tagmember":
|
elif type == "tagmember":
|
||||||
tag = Tag.objects.filter(owner=user, title=tag_title).first()
|
tag = Tag.objects.filter(owner=user, title=tag_title).first()
|
||||||
if not tag:
|
if not tag:
|
||||||
|
@ -555,10 +554,9 @@ def home(request, user_name):
|
||||||
for category in visbile_categories:
|
for category in visbile_categories:
|
||||||
shelf_list[category] = {}
|
shelf_list[category] = {}
|
||||||
for shelf_type in ShelfType:
|
for shelf_type in ShelfType:
|
||||||
shelf = user.shelf_manager.get_shelf(category, shelf_type)
|
members = user.shelf_manager.get_members(shelf_type, category).filter(qv)
|
||||||
members = shelf.recent_members.filter(qv)
|
|
||||||
shelf_list[category][shelf_type] = {
|
shelf_list[category][shelf_type] = {
|
||||||
"title": shelf.title,
|
"title": user.shelf_manager.get_title(shelf_type, category),
|
||||||
"count": members.count(),
|
"count": members.count(),
|
||||||
"members": members[:5].prefetch_related("item"),
|
"members": members[:5].prefetch_related("item"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,115 +16,133 @@ from django.db.models import Q, Count, Sum
|
||||||
from django.utils import dateparse, timezone
|
from django.utils import dateparse, timezone
|
||||||
import re
|
import re
|
||||||
from legacy.models import *
|
from legacy.models import *
|
||||||
|
from django.db import DatabaseError, transaction
|
||||||
|
from music.models import SongMark
|
||||||
|
from collection.models import CollectionItem
|
||||||
|
|
||||||
BATCH_SIZE = 1000
|
BATCH_SIZE = 1000
|
||||||
|
|
||||||
|
|
||||||
def _book_convert(entity):
|
def _book_convert(entity):
|
||||||
content = ResourceContent(metadata={
|
content = ResourceContent(
|
||||||
'title': entity.title,
|
metadata={
|
||||||
'brief': entity.brief,
|
"title": entity.title,
|
||||||
'cover_image_path': str(entity.cover),
|
"brief": entity.brief,
|
||||||
|
"cover_image_path": str(entity.cover),
|
||||||
'subtitle': entity.subtitle,
|
"subtitle": entity.subtitle,
|
||||||
'orig_title': entity.orig_title,
|
"orig_title": entity.orig_title,
|
||||||
'author': entity.author,
|
"author": entity.author,
|
||||||
'translator': entity.translator,
|
"translator": entity.translator,
|
||||||
'language': entity.language,
|
"language": entity.language,
|
||||||
'pub_house': entity.pub_house,
|
"pub_house": entity.pub_house,
|
||||||
'pub_year': entity.pub_year,
|
"pub_year": entity.pub_year,
|
||||||
'pub_month': entity.pub_month,
|
"pub_month": entity.pub_month,
|
||||||
'binding': entity.binding,
|
"binding": entity.binding,
|
||||||
'price': entity.price,
|
"price": entity.price,
|
||||||
'pages': entity.pages,
|
"pages": entity.pages,
|
||||||
'contents': entity.contents,
|
"contents": entity.contents,
|
||||||
'series': entity.other_info.get('丛书') if entity.other_info else None,
|
"series": entity.other_info.get("丛书") if entity.other_info else None,
|
||||||
'imprint': entity.other_info.get('出品方') if entity.other_info else None,
|
"imprint": entity.other_info.get("出品方") if entity.other_info else None,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
if entity.isbn:
|
if entity.isbn:
|
||||||
t, v = detect_isbn_asin(entity.isbn)
|
t, v = detect_isbn_asin(entity.isbn)
|
||||||
if t:
|
if t:
|
||||||
content.lookup_ids[t] = v
|
content.lookup_ids[t] = v
|
||||||
if entity.other_info and entity.other_info.get('统一书号'):
|
if entity.other_info and entity.other_info.get("统一书号"):
|
||||||
content.lookup_ids[IdType.CUBN] = entity.other_info.get('统一书号')
|
content.lookup_ids[IdType.CUBN] = entity.other_info.get("统一书号")
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def _album_convert(entity):
|
def _album_convert(entity):
|
||||||
content = ResourceContent(metadata={
|
content = ResourceContent(
|
||||||
'title': entity.title,
|
metadata={
|
||||||
'brief': entity.brief,
|
"title": entity.title,
|
||||||
'cover_image_path': str(entity.cover),
|
"brief": entity.brief,
|
||||||
|
"cover_image_path": str(entity.cover),
|
||||||
'other_title': entity.other_info.get('又名') if entity.other_info else None,
|
"other_title": entity.other_info.get("又名") if entity.other_info else None,
|
||||||
'album_type': entity.other_info.get('专辑类型') if entity.other_info else None,
|
"album_type": entity.other_info.get("专辑类型") if entity.other_info else None,
|
||||||
'media': entity.other_info.get('介质') if entity.other_info else None,
|
"media": entity.other_info.get("介质") if entity.other_info else None,
|
||||||
'disc_count': entity.other_info.get('碟片数') if entity.other_info else None,
|
"disc_count": entity.other_info.get("碟片数") if entity.other_info else None,
|
||||||
'artist': entity.artist,
|
"artist": entity.artist,
|
||||||
'genre': entity.genre,
|
"genre": entity.genre,
|
||||||
'release_date': entity.release_date.strftime('%Y-%m-%d') if entity.release_date else None,
|
"release_date": entity.release_date.strftime("%Y-%m-%d")
|
||||||
'duration': entity.duration,
|
if entity.release_date
|
||||||
'company': entity.company,
|
else None,
|
||||||
'track_list': entity.track_list,
|
"duration": entity.duration,
|
||||||
'bandcamp_album_id': entity.other_info.get('bandcamp_album_id') if entity.other_info else None,
|
"company": entity.company,
|
||||||
})
|
"track_list": entity.track_list,
|
||||||
if entity.other_info and entity.other_info.get('ISRC'):
|
"bandcamp_album_id": entity.other_info.get("bandcamp_album_id")
|
||||||
content.lookup_ids[IdType.ISRC] = entity.other_info.get('ISRC')
|
if entity.other_info
|
||||||
if entity.other_info and entity.other_info.get('条形码'):
|
else None,
|
||||||
content.lookup_ids[IdType.GTIN] = entity.other_info.get('条形码')
|
}
|
||||||
if entity.other_info and entity.other_info.get('UPC'):
|
)
|
||||||
content.lookup_ids[IdType.GTIN] = entity.other_info.get('UPC')
|
if entity.other_info and entity.other_info.get("ISRC"):
|
||||||
|
content.lookup_ids[IdType.ISRC] = entity.other_info.get("ISRC")
|
||||||
|
if entity.other_info and entity.other_info.get("条形码"):
|
||||||
|
content.lookup_ids[IdType.GTIN] = entity.other_info.get("条形码")
|
||||||
|
if entity.other_info and entity.other_info.get("UPC"):
|
||||||
|
content.lookup_ids[IdType.GTIN] = entity.other_info.get("UPC")
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def _game_convert(entity):
|
def _game_convert(entity):
|
||||||
content = ResourceContent(metadata={
|
content = ResourceContent(
|
||||||
'title': entity.title,
|
metadata={
|
||||||
'brief': entity.brief,
|
"title": entity.title,
|
||||||
'cover_image_path': str(entity.cover),
|
"brief": entity.brief,
|
||||||
|
"cover_image_path": str(entity.cover),
|
||||||
'other_title': entity.other_title,
|
"other_title": entity.other_title,
|
||||||
'developer': entity.developer,
|
"developer": entity.developer,
|
||||||
'publisher': entity.publisher,
|
"publisher": entity.publisher,
|
||||||
'release_date': entity.release_date.strftime('%Y-%m-%d') if entity.release_date else None,
|
"release_date": entity.release_date.strftime("%Y-%m-%d")
|
||||||
'genre': entity.genre,
|
if entity.release_date
|
||||||
'platform': entity.platform,
|
else None,
|
||||||
'official_site': entity.other_info.get('official_site') if entity.other_info else None,
|
"genre": entity.genre,
|
||||||
})
|
"platform": entity.platform,
|
||||||
if entity.other_info and entity.other_info.get('steam_url'):
|
"official_site": entity.other_info.get("official_site")
|
||||||
content.lookup_ids[IdType.Steam] = re.search(r'store\.steampowered\.com/app/(\d+)', entity.other_info.get('steam_url'))[1]
|
if entity.other_info
|
||||||
|
else None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if entity.other_info and entity.other_info.get("steam_url"):
|
||||||
|
content.lookup_ids[IdType.Steam] = re.search(
|
||||||
|
r"store\.steampowered\.com/app/(\d+)", entity.other_info.get("steam_url")
|
||||||
|
)[1]
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def _movie_tv_convert(entity):
|
def _movie_tv_convert(entity):
|
||||||
content = ResourceContent(metadata={
|
content = ResourceContent(
|
||||||
'title': entity.title,
|
metadata={
|
||||||
'brief': entity.brief,
|
"title": entity.title,
|
||||||
'cover_image_path': str(entity.cover),
|
"brief": entity.brief,
|
||||||
|
"cover_image_path": str(entity.cover),
|
||||||
'orig_title': entity.orig_title,
|
"orig_title": entity.orig_title,
|
||||||
'other_title': entity.other_title,
|
"other_title": entity.other_title,
|
||||||
'director': entity.director,
|
"director": entity.director,
|
||||||
'playwright': entity.playwright,
|
"playwright": entity.playwright,
|
||||||
'actor': entity.actor,
|
"actor": entity.actor,
|
||||||
'genre': entity.genre,
|
"genre": entity.genre,
|
||||||
'showtime': entity.showtime,
|
"showtime": entity.showtime,
|
||||||
'site': entity.site,
|
"site": entity.site,
|
||||||
'area': entity.area,
|
"area": entity.area,
|
||||||
'language': entity.language,
|
"language": entity.language,
|
||||||
'year': entity.year,
|
"year": entity.year,
|
||||||
'duration': entity.duration,
|
"duration": entity.duration,
|
||||||
'season_count': entity.other_info.get('Seasons') if entity.other_info else None,
|
"season_count": entity.other_info.get("Seasons")
|
||||||
'season_number': entity.season,
|
if entity.other_info
|
||||||
'episode_count': entity.episodes,
|
else None,
|
||||||
'single_episode_length': entity.single_episode_length,
|
"season_number": entity.season,
|
||||||
'is_series': entity.is_series,
|
"episode_count": entity.episodes,
|
||||||
})
|
"single_episode_length": entity.single_episode_length,
|
||||||
|
"is_series": entity.is_series,
|
||||||
|
}
|
||||||
|
)
|
||||||
if entity.imdb_code:
|
if entity.imdb_code:
|
||||||
content.lookup_ids[IdType.IMDB] = entity.imdb_code
|
content.lookup_ids[IdType.IMDB] = entity.imdb_code
|
||||||
if entity.other_info and entity.other_info.get('TMDB_ID'):
|
if entity.other_info and entity.other_info.get("TMDB_ID"):
|
||||||
content.lookup_ids[IdType.TMDB_TV] = entity.other_info.get('TMDB_ID')
|
content.lookup_ids[IdType.TMDB_TV] = entity.other_info.get("TMDB_ID")
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,33 +165,77 @@ model_link = {
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Migrate legacy catalog'
|
help = "Migrate legacy catalog"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('--book', dest='types', action='append_const', const=Legacy_Book)
|
parser.add_argument(
|
||||||
parser.add_argument('--movie', dest='types', action='append_const', const=Legacy_Movie)
|
"--book", dest="types", action="append_const", const=Legacy_Book
|
||||||
parser.add_argument('--album', dest='types', action='append_const', const=Legacy_Album)
|
)
|
||||||
parser.add_argument('--game', dest='types', action='append_const', const=Legacy_Game)
|
parser.add_argument(
|
||||||
parser.add_argument('--id', help='id to convert; or, if using with --max-id, the min id')
|
"--movie", dest="types", action="append_const", const=Legacy_Movie
|
||||||
parser.add_argument('--maxid', help='max id to convert')
|
)
|
||||||
parser.add_argument('--failstop', help='stop on fail', action='store_true')
|
parser.add_argument(
|
||||||
parser.add_argument('--clearlink', help='clear legacy link table', action='store_true')
|
"--album", dest="types", action="append_const", const=Legacy_Album
|
||||||
parser.add_argument('--reload', help='reload and ignore existing ExternalResource', action='store_true')
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--game", dest="types", action="append_const", const=Legacy_Game
|
||||||
|
)
|
||||||
|
parser.add_argument("--song", help="process songs", action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"--id", help="id to convert; or, if using with --max-id, the min id"
|
||||||
|
)
|
||||||
|
parser.add_argument("--maxid", help="max id to convert")
|
||||||
|
parser.add_argument("--failstop", help="stop on fail", action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"--clearlink", help="clear legacy link table", action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--reload",
|
||||||
|
help="reload and ignore existing ExternalResource",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
|
def process_song(self, entity):
|
||||||
|
if entity.album:
|
||||||
|
new_uid = AlbumLink.objects.get(old_id=entity.album.id).new_uid
|
||||||
|
else:
|
||||||
|
new_uid = Album.objects.create(
|
||||||
|
title=entity.title,
|
||||||
|
brief=entity.brief,
|
||||||
|
cover=entity.cover,
|
||||||
|
artist=entity.artist,
|
||||||
|
).uid
|
||||||
|
if SongLink.objects.filter(old_id=entity.id).count() == 0:
|
||||||
|
SongLink.objects.create(old_id=entity.id, new_uid=new_uid)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
types = options['types'] or [Legacy_Game, Legacy_Album, Legacy_Movie, Legacy_Book]
|
if options["song"]:
|
||||||
reload = options['reload']
|
for sm in SongMark.objects.all():
|
||||||
|
self.process_song(sm.song)
|
||||||
|
for ci in CollectionItem.objects.filter(song__isnull=False):
|
||||||
|
self.process_song(ci.song)
|
||||||
|
return
|
||||||
|
|
||||||
|
types = options["types"] or [
|
||||||
|
Legacy_Game,
|
||||||
|
Legacy_Album,
|
||||||
|
Legacy_Movie,
|
||||||
|
Legacy_Book,
|
||||||
|
]
|
||||||
|
reload = options["reload"]
|
||||||
for typ in types:
|
for typ in types:
|
||||||
print(typ)
|
print(typ)
|
||||||
LinkModel = model_link[typ]
|
LinkModel = model_link[typ]
|
||||||
if options['clearlink']:
|
if options["clearlink"]:
|
||||||
LinkModel.objects.all().delete()
|
LinkModel.objects.all().delete()
|
||||||
qs = typ.objects.all().order_by('id')
|
qs = typ.objects.all().order_by("id")
|
||||||
if options['id']:
|
if options["id"]:
|
||||||
if options['maxid']:
|
if options["maxid"]:
|
||||||
qs = qs.filter(id__gte=int(options['id']), id__lte=int(options['maxid']))
|
qs = qs.filter(
|
||||||
|
id__gte=int(options["id"]), id__lte=int(options["maxid"])
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
qs = qs.filter(id=int(options['id']))
|
qs = qs.filter(id=int(options["id"]))
|
||||||
|
|
||||||
pg = Paginator(qs, BATCH_SIZE)
|
pg = Paginator(qs, BATCH_SIZE)
|
||||||
for p in tqdm(pg.page_range):
|
for p in tqdm(pg.page_range):
|
||||||
|
@ -185,34 +247,47 @@ class Command(BaseCommand):
|
||||||
site = SiteManager.get_site_by_url(entity.source_url)
|
site = SiteManager.get_site_by_url(entity.source_url)
|
||||||
item = None
|
item = None
|
||||||
if site:
|
if site:
|
||||||
if not site.DEFAULT_MODEL and not content.metadata.get('preferred_model'):
|
if not site.DEFAULT_MODEL and not content.metadata.get(
|
||||||
|
"preferred_model"
|
||||||
|
):
|
||||||
if model_map[typ] == Movie and entity.is_series:
|
if model_map[typ] == Movie and entity.is_series:
|
||||||
content.metadata['preferred_model'] = 'TVSeason' if entity.season else 'TVShow'
|
content.metadata["preferred_model"] = (
|
||||||
|
"TVSeason" if entity.season else "TVShow"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
content.metadata['preferred_model'] = model_map[typ].__name__
|
content.metadata["preferred_model"] = model_map[
|
||||||
item = site.get_resource_ready(preloaded_content=content, ignore_existing_content=reload).item
|
typ
|
||||||
|
].__name__
|
||||||
|
item = site.get_resource_ready(
|
||||||
|
preloaded_content=content,
|
||||||
|
ignore_existing_content=reload,
|
||||||
|
).item
|
||||||
else:
|
else:
|
||||||
# not known site, try save item without external resource
|
# not known site, try save item without external resource
|
||||||
item = None
|
item = None
|
||||||
model = Edition
|
model = model_map[typ]
|
||||||
t, v = None, None
|
t, v = None, None
|
||||||
if content.lookup_ids:
|
if content.lookup_ids:
|
||||||
t, v = Item.get_best_lookup_id(content.lookup_ids)
|
t, v = Item.get_best_lookup_id(content.lookup_ids)
|
||||||
item = model.objects.filter(primary_lookup_id_type=t, primary_lookup_id_value=v).first()
|
item = model.objects.filter(
|
||||||
|
primary_lookup_id_type=t,
|
||||||
|
primary_lookup_id_value=v,
|
||||||
|
).first()
|
||||||
if not item:
|
if not item:
|
||||||
obj = model.copy_metadata(content.metadata)
|
obj = model.copy_metadata(content.metadata)
|
||||||
obj['primary_lookup_id_type'] = t
|
obj["primary_lookup_id_type"] = t
|
||||||
obj['primary_lookup_id_value'] = v
|
obj["primary_lookup_id_value"] = v
|
||||||
item = model.objects.create(**obj)
|
item = model.objects.create(**obj)
|
||||||
item.cover = content.metadata['cover_image_path']
|
item.cover = content.metadata["cover_image_path"]
|
||||||
item.last_editor = entity.last_editor
|
item.last_editor = entity.last_editor
|
||||||
item.save()
|
item.save()
|
||||||
links.append(LinkModel(old_id=entity.id, new_uid=item.uid))
|
links.append(LinkModel(old_id=entity.id, new_uid=item.uid))
|
||||||
# pprint.pp(site.get_item())
|
# pprint.pp(site.get_item())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'Convert failed for {typ} {entity.id}: {e}')
|
print(f"Convert failed for {typ} {entity.id}: {e}")
|
||||||
if options['failstop']:
|
if options["failstop"]:
|
||||||
raise(e)
|
raise (e)
|
||||||
# return
|
# return
|
||||||
LinkModel.objects.bulk_create(links)
|
LinkModel.objects.bulk_create(links)
|
||||||
self.stdout.write(self.style.SUCCESS(f'Done.'))
|
|
||||||
|
self.stdout.write(self.style.SUCCESS(f"Done."))
|
||||||
|
|
|
@ -6,7 +6,7 @@ from games.models import Game as Legacy_Game
|
||||||
from common.models import MarkStatusEnum
|
from common.models import MarkStatusEnum
|
||||||
from books.models import BookMark, BookReview
|
from books.models import BookMark, BookReview
|
||||||
from movies.models import MovieMark, MovieReview
|
from movies.models import MovieMark, MovieReview
|
||||||
from music.models import AlbumMark, AlbumReview
|
from music.models import AlbumMark, AlbumReview, SongMark, SongReview
|
||||||
from games.models import GameMark, GameReview
|
from games.models import GameMark, GameReview
|
||||||
from collection.models import Collection as Legacy_Collection
|
from collection.models import Collection as Legacy_Collection
|
||||||
from collection.models import CollectionMark as Legacy_CollectionMark
|
from collection.models import CollectionMark as Legacy_CollectionMark
|
||||||
|
@ -35,14 +35,17 @@ model_link = {
|
||||||
MovieMark: MovieLink,
|
MovieMark: MovieLink,
|
||||||
AlbumMark: AlbumLink,
|
AlbumMark: AlbumLink,
|
||||||
GameMark: GameLink,
|
GameMark: GameLink,
|
||||||
|
SongMark: SongLink,
|
||||||
BookReview: BookLink,
|
BookReview: BookLink,
|
||||||
MovieReview: MovieLink,
|
MovieReview: MovieLink,
|
||||||
AlbumReview: AlbumLink,
|
AlbumReview: AlbumLink,
|
||||||
GameReview: GameLink,
|
GameReview: GameLink,
|
||||||
|
SongReview: SongLink,
|
||||||
Legacy_Book: BookLink,
|
Legacy_Book: BookLink,
|
||||||
Legacy_Movie: MovieLink,
|
Legacy_Movie: MovieLink,
|
||||||
Legacy_Album: AlbumLink,
|
Legacy_Album: AlbumLink,
|
||||||
Legacy_Game: GameLink,
|
Legacy_Game: GameLink,
|
||||||
|
Legacy_Song: SongLink,
|
||||||
}
|
}
|
||||||
|
|
||||||
shelf_map = {
|
shelf_map = {
|
||||||
|
@ -55,6 +58,7 @@ tag_map = {
|
||||||
BookMark: "bookmark_tags",
|
BookMark: "bookmark_tags",
|
||||||
MovieMark: "moviemark_tags",
|
MovieMark: "moviemark_tags",
|
||||||
AlbumMark: "albummark_tags",
|
AlbumMark: "albummark_tags",
|
||||||
|
SongMark: "songmark_tags",
|
||||||
GameMark: "gamemark_tags",
|
GameMark: "gamemark_tags",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +79,9 @@ class Command(BaseCommand):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--game", dest="types", action="append_const", const=GameMark
|
"--game", dest="types", action="append_const", const=GameMark
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--song", dest="types", action="append_const", const=SongMark
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--mark",
|
"--mark",
|
||||||
help="migrate shelves/tags/ratings, then exit",
|
help="migrate shelves/tags/ratings, then exit",
|
||||||
|
@ -104,7 +111,7 @@ class Command(BaseCommand):
|
||||||
print("Initialize shelves")
|
print("Initialize shelves")
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for user in tqdm(User.objects.filter(is_active=True)):
|
for user in tqdm(User.objects.filter(is_active=True)):
|
||||||
user.shelf_manager.initialize()
|
temp = user.shelf_manager
|
||||||
|
|
||||||
def clear(self, classes):
|
def clear(self, classes):
|
||||||
print("Deleting migrated user pieces")
|
print("Deleting migrated user pieces")
|
||||||
|
@ -196,12 +203,11 @@ class Command(BaseCommand):
|
||||||
raise (e)
|
raise (e)
|
||||||
|
|
||||||
def mark(self, options):
|
def mark(self, options):
|
||||||
types = options["types"] or [GameMark, AlbumMark, MovieMark, BookMark]
|
types = options["types"] or [GameMark, SongMark, AlbumMark, MovieMark, BookMark]
|
||||||
print("Preparing cache")
|
print("Preparing cache")
|
||||||
tag_cache = {f"{t.owner_id}_{t.title}": t.id for t in Tag.objects.all()}
|
tag_cache = {f"{t.owner_id}_{t.title}": t.id for t in Tag.objects.all()}
|
||||||
shelf_cache = {
|
shelf_cache = {
|
||||||
f"{s.owner_id}_{s.item_category}_{shelf_map[s.shelf_type]}": s.id
|
f"{s.owner_id}_{shelf_map[s.shelf_type]}": s.id for s in Shelf.objects.all()
|
||||||
for s in Shelf.objects.all()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for typ in types:
|
for typ in types:
|
||||||
|
@ -255,9 +261,7 @@ class Command(BaseCommand):
|
||||||
text=entity.text,
|
text=entity.text,
|
||||||
visibility=visibility,
|
visibility=visibility,
|
||||||
)
|
)
|
||||||
shelf = shelf_cache[
|
shelf = shelf_cache[f"{user_id}_{entity.status}"]
|
||||||
f"{user_id}_{item.category}_{entity.status}"
|
|
||||||
]
|
|
||||||
ShelfMember.objects.create(
|
ShelfMember.objects.create(
|
||||||
parent_id=shelf,
|
parent_id=shelf,
|
||||||
owner_id=user_id,
|
owner_id=user_id,
|
||||||
|
@ -309,7 +313,7 @@ class Command(BaseCommand):
|
||||||
elif options["mark"]:
|
elif options["mark"]:
|
||||||
if options["clear"]:
|
if options["clear"]:
|
||||||
self.clear(
|
self.clear(
|
||||||
[Comment, Rating, TagMember, Tag, ShelfLogEntry, ShelfMember]
|
[Comment, Rating, TagMember, Tag, ShelfLogEntry, ShelfMember, Shelf]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.mark(options)
|
self.mark(options)
|
||||||
|
|
|
@ -16,6 +16,11 @@ class AlbumLink(models.Model):
|
||||||
new_uid = models.UUIDField()
|
new_uid = models.UUIDField()
|
||||||
|
|
||||||
|
|
||||||
|
class SongLink(models.Model):
|
||||||
|
old_id = models.IntegerField(db_index=True)
|
||||||
|
new_uid = models.UUIDField()
|
||||||
|
|
||||||
|
|
||||||
class GameLink(models.Model):
|
class GameLink(models.Model):
|
||||||
old_id = models.IntegerField(db_index=True)
|
old_id = models.IntegerField(db_index=True)
|
||||||
new_uid = models.UUIDField()
|
new_uid = models.UUIDField()
|
||||||
|
|
|
@ -11,9 +11,7 @@ class SocialTest(TestCase):
|
||||||
self.book2 = Edition.objects.create(title="Andymion")
|
self.book2 = Edition.objects.create(title="Andymion")
|
||||||
self.movie = Edition.objects.create(title="Fight Club")
|
self.movie = Edition.objects.create(title="Fight Club")
|
||||||
self.alice = User.objects.create(mastodon_site="MySpace", username="Alice")
|
self.alice = User.objects.create(mastodon_site="MySpace", username="Alice")
|
||||||
self.alice.shelf_manager.initialize()
|
|
||||||
self.bob = User.objects.create(mastodon_site="KKCity", username="Bob")
|
self.bob = User.objects.create(mastodon_site="KKCity", username="Bob")
|
||||||
self.bob.shelf_manager.initialize()
|
|
||||||
|
|
||||||
def test_timeline(self):
|
def test_timeline(self):
|
||||||
# alice see 0 activity in timeline in the beginning
|
# alice see 0 activity in timeline in the beginning
|
||||||
|
@ -40,8 +38,8 @@ class SocialTest(TestCase):
|
||||||
self.assertEqual(len(timeline2), 0)
|
self.assertEqual(len(timeline2), 0)
|
||||||
|
|
||||||
# bob follows alice, see 2 activities
|
# bob follows alice, see 2 activities
|
||||||
self.bob.mastodon_following = ['Alice@MySpace']
|
self.bob.mastodon_following = ["Alice@MySpace"]
|
||||||
self.alice.mastodon_follower = ['Bob@KKCity']
|
self.alice.mastodon_follower = ["Bob@KKCity"]
|
||||||
self.bob.following = self.bob.get_following_ids()
|
self.bob.following = self.bob.get_following_ids()
|
||||||
timeline2 = self.bob.activity_manager.get_timeline()
|
timeline2 = self.bob.activity_manager.get_timeline()
|
||||||
self.assertEqual(len(timeline2), 2)
|
self.assertEqual(len(timeline2), 2)
|
||||||
|
|
Loading…
Add table
Reference in a new issue