update lint: replace black and isort with ruff

This commit is contained in:
mein Name 2025-01-11 17:20:02 -05:00 committed by Henri Dickson
parent a93d9668f7
commit 2a048a3719
151 changed files with 282 additions and 394 deletions

View file

@ -25,21 +25,21 @@ repos:
- id: mixed-line-ending - id: mixed-line-ending
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4 rev: v0.9.1
hooks: hooks:
- id: ruff - id: ruff
# - id: ruff-format - id: ruff-format
- repo: https://github.com/pycqa/isort # - repo: https://github.com/pycqa/isort
rev: 5.13.2 # rev: 5.13.2
hooks: # hooks:
- id: isort # - id: isort
args: ["--profile=black"] # args: ["--profile=black"]
- repo: https://github.com/psf/black # - repo: https://github.com/psf/black
rev: 24.4.2 # rev: 24.4.2
hooks: # hooks:
- id: black # - id: black
- repo: https://github.com/Riverside-Healthcare/djLint - repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.36.4 rev: v1.36.4

View file

@ -75,4 +75,4 @@ Please see [docs/install.md](docs/install.md)
- To translate NeoDB to more languages, please join [our project on Weblate](https://hosted.weblate.org/projects/neodb/neodb/) - To translate NeoDB to more languages, please join [our project on Weblate](https://hosted.weblate.org/projects/neodb/neodb/)
## Sponsor ## Sponsor
If you like this project, please consider donating to [NeoDB on ko-fi](https://ko-fi.com/neodb), or our friends at [NiceDB](https://patreon.com/tertius) without whom this project won't be possible. If you like this project, please consider donating to [NeoDB.social on ko-fi](https://ko-fi.com/neodb), or [Takahē](https://www.patreon.com/takahe) and [NiceDB](https://patreon.com/tertius) without whom this project won't be possible.

View file

@ -1,3 +1 @@
from django.contrib import admin
# Register your models here. # Register your models here.

View file

@ -1,5 +1,5 @@
from enum import Enum from enum import Enum
from typing import Any, Callable, List, Optional, Tuple, Type from typing import List
from django.core.cache import cache from django.core.cache import cache
from django.http import HttpResponse from django.http import HttpResponse

View file

@ -7,12 +7,9 @@ class CatalogConfig(AppConfig):
def ready(self): def ready(self):
# load key modules in proper order, make sure class inject and signal works as expected # load key modules in proper order, make sure class inject and signal works as expected
from catalog import api, models, sites
from catalog.models import init_catalog_audit_log, init_catalog_search_models from catalog.models import init_catalog_audit_log, init_catalog_search_models
from journal import models as journal_models
# register cron jobs # register cron jobs
from catalog.jobs import DiscoverGenerator, PodcastUpdater # isort:skip
init_catalog_search_models() init_catalog_search_models()
init_catalog_audit_log() init_catalog_audit_log()

View file

@ -33,8 +33,6 @@ from catalog.common import (
Item, Item,
ItemCategory, ItemCategory,
ItemInSchema, ItemInSchema,
ItemSchema,
ItemType,
PrimaryLookupIdDescriptor, PrimaryLookupIdDescriptor,
jsondata, jsondata,
) )
@ -286,7 +284,7 @@ class Edition(Item):
logger.warning(f"Unable to find work for {work_res}") logger.warning(f"Unable to find work for {work_res}")
else: else:
logger.warning( logger.warning(
f'Unable to find resource for {w["id_type"]}:{w["id_value"]}' f"Unable to find resource for {w['id_type']}:{w['id_value']}"
) )
work = Work.objects.filter( work = Work.objects.filter(
primary_lookup_id_type=w["id_type"], primary_lookup_id_type=w["id_type"],
@ -431,7 +429,7 @@ class Work(Item):
logger.warning(f"Unable to find edition for {edition_res}") logger.warning(f"Unable to find edition for {edition_res}")
else: else:
logger.warning( logger.warning(
f'Unable to find resource for {e["id_type"]}:{e["id_value"]}' f"Unable to find resource for {e['id_type']}:{e['id_value']}"
) )
edition = Edition.objects.filter( edition = Edition.objects.filter(
primary_lookup_id_type=e["id_type"], primary_lookup_id_type=e["id_type"],

View file

@ -1,5 +1,4 @@
import json import json
import logging
import re import re
import time import time
from io import BytesIO, StringIO from io import BytesIO, StringIO

View file

@ -1,20 +1,16 @@
# pyright: reportIncompatibleMethodOverride=false # pyright: reportIncompatibleMethodOverride=false
import copy import copy
from base64 import b64decode, b64encode from base64 import b64encode
from datetime import date, datetime from datetime import date, datetime
from functools import partialmethod from functools import partialmethod
from hashlib import sha256 from hashlib import sha256
from importlib import import_module
import django
import loguru
from cryptography.fernet import Fernet, MultiFernet from cryptography.fernet import Fernet, MultiFernet
from django.conf import settings from django.conf import settings
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db.models import DEFERRED, fields # type:ignore from django.db.models import DEFERRED, fields # type:ignore
from django.utils import dateparse, timezone from django.utils import dateparse, timezone
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.translation import gettext_lazy as _
from django_jsonform.forms.fields import JSONFormField as DJANGO_JSONFormField from django_jsonform.forms.fields import JSONFormField as DJANGO_JSONFormField
# from django.db.models import JSONField as DJANGO_JSONField # from django.db.models import JSONField as DJANGO_JSONField
@ -22,7 +18,6 @@ from django_jsonform.forms.fields import JSONFormField as DJANGO_JSONFormField
# from django.contrib.postgres.fields import ArrayField as DJANGO_ArrayField # from django.contrib.postgres.fields import ArrayField as DJANGO_ArrayField
from django_jsonform.models.fields import ArrayField as DJANGO_ArrayField from django_jsonform.models.fields import ArrayField as DJANGO_ArrayField
from django_jsonform.models.fields import JSONField as DJANGO_JSONField from django_jsonform.models.fields import JSONField as DJANGO_JSONField
from loguru import logger
class Patched_DJANGO_JSONField(DJANGO_JSONField): class Patched_DJANGO_JSONField(DJANGO_JSONField):

View file

@ -1,19 +1,17 @@
import re import re
import uuid import uuid
from functools import cached_property from functools import cached_property
from typing import TYPE_CHECKING, Any, Iterable, Self, Sequence, Type, cast from typing import TYPE_CHECKING, Any, Self
from auditlog.context import disable_auditlog from auditlog.context import disable_auditlog
from auditlog.models import AuditlogHistoryField, LogEntry from auditlog.models import LogEntry
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.signing import b62_decode, b62_encode from django.core.signing import b62_decode, b62_encode
from django.db import connection, models from django.db import connection, models
from django.db.models import QuerySet, Value from django.db.models import QuerySet
from django.template.defaultfilters import default
from django.utils import timezone from django.utils import timezone
from django.utils.translation import get_language
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from loguru import logger from loguru import logger
from ninja import Field, Schema from ninja import Field, Schema
@ -72,36 +70,24 @@ class IdType(models.TextChoices):
IMDB = "imdb", _("IMDb") # type:ignore[reportCallIssue] IMDB = "imdb", _("IMDb") # type:ignore[reportCallIssue]
TMDB_TV = "tmdb_tv", _("TMDB TV Serie") # type:ignore[reportCallIssue] TMDB_TV = "tmdb_tv", _("TMDB TV Serie") # type:ignore[reportCallIssue]
TMDB_TVSeason = "tmdb_tvseason", _("TMDB TV Season") # type:ignore[reportCallIssue] TMDB_TVSeason = "tmdb_tvseason", _("TMDB TV Season") # type:ignore[reportCallIssue]
TMDB_TVEpisode = "tmdb_tvepisode", _( TMDB_TVEpisode = "tmdb_tvepisode", _("TMDB TV Episode") # type:ignore[reportCallIssue]
"TMDB TV Episode"
) # type:ignore[reportCallIssue]
TMDB_Movie = "tmdb_movie", _("TMDB Movie") # type:ignore[reportCallIssue] TMDB_Movie = "tmdb_movie", _("TMDB Movie") # type:ignore[reportCallIssue]
Goodreads = "goodreads", _("Goodreads") # type:ignore[reportCallIssue] Goodreads = "goodreads", _("Goodreads") # type:ignore[reportCallIssue]
Goodreads_Work = "goodreads_work", _( Goodreads_Work = "goodreads_work", _("Goodreads Work") # type:ignore[reportCallIssue]
"Goodreads Work"
) # type:ignore[reportCallIssue]
GoogleBooks = "googlebooks", _("Google Books") # type:ignore[reportCallIssue] GoogleBooks = "googlebooks", _("Google Books") # type:ignore[reportCallIssue]
DoubanBook = "doubanbook", _("Douban Book") # type:ignore[reportCallIssue] DoubanBook = "doubanbook", _("Douban Book") # type:ignore[reportCallIssue]
DoubanBook_Work = "doubanbook_work", _( DoubanBook_Work = "doubanbook_work", _("Douban Book Work") # type:ignore[reportCallIssue]
"Douban Book Work"
) # type:ignore[reportCallIssue]
DoubanMovie = "doubanmovie", _("Douban Movie") # type:ignore[reportCallIssue] DoubanMovie = "doubanmovie", _("Douban Movie") # type:ignore[reportCallIssue]
DoubanMusic = "doubanmusic", _("Douban Music") # type:ignore[reportCallIssue] DoubanMusic = "doubanmusic", _("Douban Music") # type:ignore[reportCallIssue]
DoubanGame = "doubangame", _("Douban Game") # type:ignore[reportCallIssue] DoubanGame = "doubangame", _("Douban Game") # type:ignore[reportCallIssue]
DoubanDrama = "doubandrama", _("Douban Drama") # type:ignore[reportCallIssue] DoubanDrama = "doubandrama", _("Douban Drama") # type:ignore[reportCallIssue]
DoubanDramaVersion = "doubandrama_version", _( DoubanDramaVersion = "doubandrama_version", _("Douban Drama Version") # type:ignore[reportCallIssue]
"Douban Drama Version"
) # type:ignore[reportCallIssue]
BooksTW = "bookstw", _("BooksTW Book") # type:ignore[reportCallIssue] BooksTW = "bookstw", _("BooksTW Book") # type:ignore[reportCallIssue]
Bandcamp = "bandcamp", _("Bandcamp") # type:ignore[reportCallIssue] Bandcamp = "bandcamp", _("Bandcamp") # type:ignore[reportCallIssue]
Spotify_Album = "spotify_album", _("Spotify Album") # type:ignore[reportCallIssue] Spotify_Album = "spotify_album", _("Spotify Album") # type:ignore[reportCallIssue]
Spotify_Show = "spotify_show", _("Spotify Podcast") # type:ignore[reportCallIssue] Spotify_Show = "spotify_show", _("Spotify Podcast") # type:ignore[reportCallIssue]
Discogs_Release = "discogs_release", _( Discogs_Release = "discogs_release", _("Discogs Release") # type:ignore[reportCallIssue]
"Discogs Release" Discogs_Master = "discogs_master", _("Discogs Master") # type:ignore[reportCallIssue]
) # type:ignore[reportCallIssue]
Discogs_Master = "discogs_master", _(
"Discogs Master"
) # type:ignore[reportCallIssue]
MusicBrainz = "musicbrainz", _("MusicBrainz ID") # type:ignore[reportCallIssue] MusicBrainz = "musicbrainz", _("MusicBrainz ID") # type:ignore[reportCallIssue]
# DoubanBook_Author = "doubanbook_author", _("Douban Book Author") # type:ignore[reportCallIssue] # DoubanBook_Author = "doubanbook_author", _("Douban Book Author") # type:ignore[reportCallIssue]
# DoubanCelebrity = "doubanmovie_celebrity", _("Douban Movie Celebrity") # type:ignore[reportCallIssue] # DoubanCelebrity = "doubanmovie_celebrity", _("Douban Movie Celebrity") # type:ignore[reportCallIssue]
@ -142,13 +128,9 @@ class ItemType(models.TextChoices):
Album = "music", _("Album") # type:ignore[reportCallIssue] Album = "music", _("Album") # type:ignore[reportCallIssue]
Game = "game", _("Game") # type:ignore[reportCallIssue] Game = "game", _("Game") # type:ignore[reportCallIssue]
Podcast = "podcast", _("Podcast Program") # type:ignore[reportCallIssue] Podcast = "podcast", _("Podcast Program") # type:ignore[reportCallIssue]
PodcastEpisode = "podcastepisode", _( PodcastEpisode = "podcastepisode", _("Podcast Episode") # type:ignore[reportCallIssue]
"Podcast Episode"
) # type:ignore[reportCallIssue]
Performance = "performance", _("Performance") # type:ignore[reportCallIssue] Performance = "performance", _("Performance") # type:ignore[reportCallIssue]
PerformanceProduction = "production", _( PerformanceProduction = "production", _("Production") # type:ignore[reportCallIssue]
"Production"
) # type:ignore[reportCallIssue]
FanFic = "fanfic", _("Fanfic") # type:ignore[reportCallIssue] FanFic = "fanfic", _("Fanfic") # type:ignore[reportCallIssue]
Exhibition = "exhibition", _("Exhibition") # type:ignore[reportCallIssue] Exhibition = "exhibition", _("Exhibition") # type:ignore[reportCallIssue]
Collection = "collection", _("Collection") # type:ignore[reportCallIssue] Collection = "collection", _("Collection") # type:ignore[reportCallIssue]

View file

@ -10,7 +10,7 @@ ResourceContent persists as an ExternalResource which may link to an Item
import json import json
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Callable, Type, TypeVar from typing import Type, TypeVar
import django_rq import django_rq
import requests import requests

View file

@ -1,10 +1,6 @@
from django.test import TestCase from django.test import TestCase
from common.models import ( from common.models import (
LANGUAGE_CHOICES,
LOCALE_CHOICES,
SCRIPT_CHOICES,
SITE_DEFAULT_LANGUAGE,
SITE_PREFERRED_LANGUAGES, SITE_PREFERRED_LANGUAGES,
SITE_PREFERRED_LOCALES, SITE_PREFERRED_LOCALES,
detect_language, detect_language,

View file

@ -1,17 +1,14 @@
from datetime import date from datetime import date
from django.db import models from django.db import models
from django.template.defaultfilters import default
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from catalog.common import ( from catalog.common import (
BaseSchema, BaseSchema,
ExternalResource,
IdType, IdType,
Item, Item,
ItemCategory, ItemCategory,
ItemInSchema, ItemInSchema,
ItemSchema,
ItemType, ItemType,
PrimaryLookupIdDescriptor, PrimaryLookupIdDescriptor,
jsondata, jsondata,

View file

@ -1,2 +1,7 @@
from .discover import DiscoverGenerator from .discover import DiscoverGenerator
from .podcast import PodcastUpdater from .podcast import PodcastUpdater
__all__ = [
"DiscoverGenerator",
"PodcastUpdater",
]

View file

@ -7,7 +7,6 @@ from django.db.models import Count, F, Q
from django.utils import timezone from django.utils import timezone
from loguru import logger from loguru import logger
from boofilsic.settings import MIN_MARKS_FOR_DISCOVER
from catalog.models import * from catalog.models import *
from common.models import SITE_PREFERRED_LOCALES, BaseJob, JobManager from common.models import SITE_PREFERRED_LOCALES, BaseJob, JobManager
from journal.models import ( from journal.models import (
@ -19,7 +18,6 @@ from journal.models import (
q_item_in_category, q_item_in_category,
) )
from takahe.utils import Takahe from takahe.utils import Takahe
from users.models import APIdentity
MAX_ITEMS_PER_PERIOD = 12 MAX_ITEMS_PER_PERIOD = 12
MIN_MARKS = settings.MIN_MARKS_FOR_DISCOVER MIN_MARKS = settings.MIN_MARKS_FOR_DISCOVER

View file

@ -1,6 +1,4 @@
import pprint
from datetime import timedelta from datetime import timedelta
from time import sleep
from loguru import logger from loguru import logger
@ -30,6 +28,6 @@ class PodcastUpdater(BaseJob):
site = RSS(p.feed_url) site = RSS(p.feed_url)
site.scrape_additional_data() site.scrape_additional_data()
c2 = p.episodes.count() c2 = p.episodes.count()
logger.info(f"updated {p}, {c2-c} new episodes.") logger.info(f"updated {p}, {c2 - c} new episodes.")
count += c2 - c count += c2 - c
logger.info(f"Podcasts update finished, {count} new episodes total.") logger.info(f"Podcasts update finished, {count} new episodes total.")

View file

@ -46,4 +46,4 @@ class Command(BaseCommand):
resource = site.scrape() resource = site.scrape()
pprint.pp(resource.metadata) pprint.pp(resource.metadata)
pprint.pp(resource.lookup_ids) pprint.pp(resource.lookup_ids)
self.stdout.write(self.style.SUCCESS(f"Done.")) self.stdout.write(self.style.SUCCESS("Done."))

View file

@ -1,6 +1,3 @@
import pprint
import re
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db.models import Count, F from django.db.models import Count, F
@ -9,7 +6,6 @@ from tqdm import tqdm
from catalog.book.tests import uniq from catalog.book.tests import uniq
from catalog.models import * from catalog.models import *
from common.models.lang import detect_language from common.models.lang import detect_language
from journal.models import update_journal_for_merged_item
class Command(BaseCommand): class Command(BaseCommand):
@ -49,7 +45,7 @@ class Command(BaseCommand):
self.integrity() self.integrity()
if options["localize"]: if options["localize"]:
self.localize() self.localize()
self.stdout.write(self.style.SUCCESS(f"Done.")) self.stdout.write(self.style.SUCCESS("Done."))
def localize(self): def localize(self):
c = Item.objects.all().count() c = Item.objects.all().count()
@ -94,14 +90,14 @@ class Command(BaseCommand):
cls.objects.filter(is_deleted=True).delete() cls.objects.filter(is_deleted=True).delete()
def integrity(self): def integrity(self):
self.stdout.write(f"Checking circulated merge...") self.stdout.write("Checking circulated merge...")
for i in Item.objects.filter(merged_to_item=F("id")): for i in Item.objects.filter(merged_to_item=F("id")):
self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1") self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1")
if self.fix: if self.fix:
i.merged_to_item = None i.merged_to_item = None
i.save() i.save()
self.stdout.write(f"Checking chained merge...") self.stdout.write("Checking chained merge...")
for i in ( for i in (
Item.objects.filter(merged_to_item__isnull=False) Item.objects.filter(merged_to_item__isnull=False)
.annotate(n=Count("merged_from_items")) .annotate(n=Count("merged_from_items"))
@ -113,14 +109,14 @@ class Command(BaseCommand):
j.merged_to_item = i.merged_to_item j.merged_to_item = i.merged_to_item
j.save() j.save()
self.stdout.write(f"Checking deleted merge...") self.stdout.write("Checking deleted merge...")
for i in Item.objects.filter(merged_to_item__isnull=False, is_deleted=True): for i in Item.objects.filter(merged_to_item__isnull=False, is_deleted=True):
self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1") self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1")
if self.fix: if self.fix:
i.is_deleted = False i.is_deleted = False
i.save() i.save()
self.stdout.write(f"Checking deleted item with external resources...") self.stdout.write("Checking deleted item with external resources...")
for i in ( for i in (
Item.objects.filter(is_deleted=True) Item.objects.filter(is_deleted=True)
.annotate(n=Count("external_resources")) .annotate(n=Count("external_resources"))
@ -132,7 +128,7 @@ class Command(BaseCommand):
r.item = None r.item = None
r.save() r.save()
self.stdout.write(f"Checking merged item with external resources...") self.stdout.write("Checking merged item with external resources...")
for i in ( for i in (
Item.objects.filter(merged_to_item__isnull=False) Item.objects.filter(merged_to_item__isnull=False)
.annotate(n=Count("external_resources")) .annotate(n=Count("external_resources"))
@ -145,7 +141,7 @@ class Command(BaseCommand):
r.save() r.save()
tvshow_ct_id = ContentType.objects.get_for_model(TVShow).id tvshow_ct_id = ContentType.objects.get_for_model(TVShow).id
self.stdout.write(f"Checking TVShow merged to other class...") self.stdout.write("Checking TVShow merged to other class...")
for i in ( for i in (
TVShow.objects.filter(merged_to_item__isnull=False) TVShow.objects.filter(merged_to_item__isnull=False)
.filter(merged_to_item__isnull=False) .filter(merged_to_item__isnull=False)
@ -161,7 +157,7 @@ class Command(BaseCommand):
if self.fix: if self.fix:
i.recast_to(i.merged_to_item.__class__) # type:ignore i.recast_to(i.merged_to_item.__class__) # type:ignore
self.stdout.write(f"Checking TVSeason is child of other class...") self.stdout.write("Checking TVSeason is child of other class...")
for i in TVSeason.objects.filter(show__isnull=False).exclude( for i in TVSeason.objects.filter(show__isnull=False).exclude(
show__polymorphic_ctype_id=tvshow_ct_id show__polymorphic_ctype_id=tvshow_ct_id
): ):
@ -172,7 +168,7 @@ class Command(BaseCommand):
i.show = None i.show = None
i.save() i.save()
self.stdout.write(f"Checking deleted item with child TV Season...") self.stdout.write("Checking deleted item with child TV Season...")
for i in TVSeason.objects.filter(show__is_deleted=True): for i in TVSeason.objects.filter(show__is_deleted=True):
if not i.show: if not i.show:
continue continue
@ -181,7 +177,7 @@ class Command(BaseCommand):
i.show.is_deleted = False i.show.is_deleted = False
i.show.save() i.show.save()
self.stdout.write(f"Checking merged item with child TV Season...") self.stdout.write("Checking merged item with child TV Season...")
for i in TVSeason.objects.filter(show__merged_to_item__isnull=False): for i in TVSeason.objects.filter(show__merged_to_item__isnull=False):
if not i.show: if not i.show:
continue continue

View file

@ -1,12 +1,8 @@
import pprint import pprint
from datetime import timedelta
from time import sleep from time import sleep
from typing import TYPE_CHECKING
from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils import timezone
from tqdm import tqdm from tqdm import tqdm
from catalog.models import Item from catalog.models import Item

View file

@ -3,7 +3,6 @@
import uuid import uuid
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import catalog.common.utils import catalog.common.utils

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("catalog", "0010_alter_item_polymorphic_ctype"), ("catalog", "0010_alter_item_polymorphic_ctype"),
] ]

View file

@ -6,7 +6,6 @@ import catalog.common.utils
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("catalog", "0011_alter_externalresource_id_type_and_more"), ("catalog", "0011_alter_externalresource_id_type_and_more"),
] ]

View file

@ -38,7 +38,7 @@ from .tv.models import (
TVShowSchema, TVShowSchema,
) )
from .search.models import Indexer, ExternalSearchResultItem # isort:skip from .search.models import Indexer # isort:skip
# class Exhibition(Item): # class Exhibition(Item):

View file

@ -3,12 +3,10 @@ from django.utils.translation import gettext_lazy as _
from catalog.common import ( from catalog.common import (
BaseSchema, BaseSchema,
ExternalResource,
IdType, IdType,
Item, Item,
ItemCategory, ItemCategory,
ItemInSchema, ItemInSchema,
ItemSchema,
ItemType, ItemType,
PrimaryLookupIdDescriptor, PrimaryLookupIdDescriptor,
jsondata, jsondata,

View file

@ -6,12 +6,10 @@ from django.utils.translation import pgettext_lazy
from catalog.common import ( from catalog.common import (
BaseSchema, BaseSchema,
ExternalResource,
IdType, IdType,
Item, Item,
ItemCategory, ItemCategory,
ItemInSchema, ItemInSchema,
ItemSchema,
ItemType, ItemType,
PrimaryLookupIdDescriptor, PrimaryLookupIdDescriptor,
jsondata, jsondata,

View file

@ -391,7 +391,9 @@ class PerformanceProduction(Item):
return ( return (
self.cover.url # type:ignore self.cover.url # type:ignore
if self.cover and self.cover != settings.DEFAULT_ITEM_COVER if self.cover and self.cover != settings.DEFAULT_ITEM_COVER
else self.show.cover_image_url if self.show else None else self.show.cover_image_url
if self.show
else None
) )
def update_linked_items_from_external_resource(self, resource: ExternalResource): def update_linked_items_from_external_resource(self, resource: ExternalResource):

View file

@ -6,14 +6,10 @@ from ninja import Field
from catalog.common import ( from catalog.common import (
BaseSchema, BaseSchema,
ExternalResource,
IdType, IdType,
Item, Item,
ItemCategory, ItemCategory,
ItemInSchema, ItemInSchema,
ItemSchema,
ItemType,
PrimaryLookupIdDescriptor,
jsondata, jsondata,
) )
from catalog.common.models import LIST_OF_ONE_PLUS_STR_SCHEMA, LanguageListField from catalog.common.models import LIST_OF_ONE_PLUS_STR_SCHEMA, LanguageListField

View file

@ -62,7 +62,7 @@ class PodcastRSSFeedTestCase(TestCase):
site = SiteManager.get_site_by_url(t_url) site = SiteManager.get_site_by_url(t_url)
site.get_resource_ready() site.get_resource_ready()
self.assertEqual(site.ready, True) self.assertEqual(site.ready, True)
metadata = site.resource.metadata # metadata = site.resource.metadata
self.assertIsNotNone(site.get_item().cover.url) self.assertIsNotNone(site.get_item().cover.url)
self.assertIsNotNone(site.get_item().recent_episodes[0].title) self.assertIsNotNone(site.get_item().recent_episodes[0].title)
self.assertIsNotNone(site.get_item().recent_episodes[0].link) self.assertIsNotNone(site.get_item().recent_episodes[0].link)
@ -74,7 +74,7 @@ class PodcastRSSFeedTestCase(TestCase):
site = SiteManager.get_site_by_url(t_url) site = SiteManager.get_site_by_url(t_url)
site.get_resource_ready() site.get_resource_ready()
self.assertEqual(site.ready, True) self.assertEqual(site.ready, True)
metadata = site.resource.metadata # metadata = site.resource.metadata
self.assertIsNotNone(site.get_item().recent_episodes[0].title) self.assertIsNotNone(site.get_item().recent_episodes[0].title)
self.assertIsNotNone(site.get_item().recent_episodes[0].link) self.assertIsNotNone(site.get_item().recent_episodes[0].link)
self.assertIsNotNone(site.get_item().recent_episodes[0].media_url) self.assertIsNotNone(site.get_item().recent_episodes[0].media_url)

View file

@ -1,6 +1,5 @@
import asyncio import asyncio
import logging import logging
import time
from urllib.parse import quote_plus, urlparse from urllib.parse import quote_plus, urlparse
import httpx import httpx
@ -90,7 +89,7 @@ class GoogleBooks:
@classmethod @classmethod
def search(cls, q, page=1): def search(cls, q, page=1):
results = [] results = []
api_url = f"https://www.googleapis.com/books/v1/volumes?country=us&q={quote_plus(q)}&startIndex={SEARCH_PAGE_SIZE*(page-1)}&maxResults={SEARCH_PAGE_SIZE}&maxAllowedMaturityRating=MATURE" api_url = f"https://www.googleapis.com/books/v1/volumes?country=us&q={quote_plus(q)}&startIndex={SEARCH_PAGE_SIZE * (page - 1)}&maxResults={SEARCH_PAGE_SIZE}&maxAllowedMaturityRating=MATURE"
try: try:
j = requests.get(api_url, timeout=2).json() j = requests.get(api_url, timeout=2).json()
if "items" in j: if "items" in j:
@ -183,7 +182,7 @@ class Spotify:
@classmethod @classmethod
def search(cls, q, page=1): def search(cls, q, page=1):
results = [] results = []
api_url = f"https://api.spotify.com/v1/search?q={q}&type=album&limit={SEARCH_PAGE_SIZE}&offset={page*SEARCH_PAGE_SIZE}" api_url = f"https://api.spotify.com/v1/search?q={q}&type=album&limit={SEARCH_PAGE_SIZE}&offset={page * SEARCH_PAGE_SIZE}"
try: try:
headers = {"Authorization": f"Bearer {get_spotify_token()}"} headers = {"Authorization": f"Bearer {get_spotify_token()}"}
j = requests.get(api_url, headers=headers, timeout=2).json() j = requests.get(api_url, headers=headers, timeout=2).json()
@ -255,7 +254,7 @@ class ApplePodcast:
@classmethod @classmethod
def search(cls, q, page=1): def search(cls, q, page=1):
results = [] results = []
search_url = f"https://itunes.apple.com/search?entity=podcast&limit={page*SEARCH_PAGE_SIZE}&term={quote_plus(q)}" search_url = f"https://itunes.apple.com/search?entity=podcast&limit={page * SEARCH_PAGE_SIZE}&term={quote_plus(q)}"
try: try:
r = requests.get(search_url, timeout=2).json() r = requests.get(search_url, timeout=2).json()
for p in r["results"][(page - 1) * SEARCH_PAGE_SIZE :]: for p in r["results"][(page - 1) * SEARCH_PAGE_SIZE :]:
@ -291,7 +290,7 @@ class IGDB:
class Fediverse: class Fediverse:
@staticmethod @staticmethod
async def search_task(host, q, category=None): async def search_task(host, q, category=None):
api_url = f"https://{host}/api/catalog/search?query={quote_plus(q)}{'&category='+category if category else ''}" api_url = f"https://{host}/api/catalog/search?query={quote_plus(q)}{'&category=' + category if category else ''}"
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
results = [] results = []
try: try:

View file

@ -6,7 +6,6 @@ import django_rq
from auditlog.context import set_actor from auditlog.context import set_actor
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.utils.translation import gettext_lazy as _
from loguru import logger from loguru import logger
from rq.job import Job from rq.job import Job

View file

@ -1,6 +1,5 @@
import types import types
from datetime import timedelta from datetime import timedelta
from pprint import pprint
from time import sleep from time import sleep
import django_rq import django_rq

View file

@ -1,5 +1,4 @@
import re import re
from urllib.parse import quote
import django_rq import django_rq
from django.conf import settings from django.conf import settings
@ -7,7 +6,6 @@ from django.contrib.auth.decorators import login_required
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import BadRequest from django.core.exceptions import BadRequest
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from rq.job import Job from rq.job import Job
@ -162,7 +160,7 @@ def external_search(request):
keywords = request.GET.get("q", default="").strip() keywords = request.GET.get("q", default="").strip()
page_number = int_(request.GET.get("page"), 1) page_number = int_(request.GET.get("page"), 1)
items = ExternalSources.search(category, keywords, page_number) if keywords else [] items = ExternalSources.search(category, keywords, page_number) if keywords else []
cache_key = f"search_{category if category!='movietv' else 'movie,tv'}_{keywords}" cache_key = f"search_{category if category != 'movietv' else 'movie,tv'}_{keywords}"
dedupe_urls = cache.get(cache_key, []) dedupe_urls = cache.get(cache_key, [])
items = [i for i in items if i.source_url not in dedupe_urls] items = [i for i in items if i.source_url not in dedupe_urls]

View file

@ -25,3 +25,33 @@ from .tmdb import TMDB_Movie
from .ypshuo import Ypshuo from .ypshuo import Ypshuo
# from .apple_podcast import ApplePodcast # from .apple_podcast import ApplePodcast
__all__ = [
"SiteManager",
"ArchiveOfOurOwn",
"AppleMusic",
"Bandcamp",
"Bangumi",
"BoardGameGeek",
"BooksTW",
"DiscogsMaster",
"DiscogsRelease",
"DoubanBook",
"DoubanDrama",
"DoubanGame",
"DoubanMovie",
"DoubanMusic",
"FediverseInstance",
"Goodreads",
"GoogleBooks",
"IGDB",
"IMDB",
"JJWXC",
"Qidian",
"RSS",
"Spotify",
"Steam",
"TMDB_Movie",
"Ypshuo",
# "ApplePodcast",
]

View file

@ -1,5 +1,3 @@
import logging
from catalog.book.models import * from catalog.book.models import *
from catalog.common import * from catalog.common import *

View file

@ -10,7 +10,6 @@ Scraping the website directly.
import json import json
import logging import logging
from threading import local
import dateparser import dateparser

View file

@ -6,8 +6,6 @@ ref: https://boardgamegeek.com/wiki/page/BGG_XML_API2
import html import html
from langdetect import detect
from loguru import logger
from catalog.common import * from catalog.common import *
from catalog.game.models import GameReleaseType from catalog.game.models import GameReleaseType

View file

@ -3,7 +3,6 @@ import logging
from catalog.book.models import * from catalog.book.models import *
from catalog.book.utils import * from catalog.book.utils import *
from catalog.common import * from catalog.common import *
from common.models.lang import detect_language
from .douban import * from .douban import *

View file

@ -2,10 +2,8 @@
Discogs. Discogs.
""" """
import json
import logging import logging
import requests
from django.conf import settings from django.conf import settings
from catalog.common import * from catalog.common import *

View file

@ -37,12 +37,10 @@ class DoubanDownloader(ProxiedDownloader):
class DoubanSearcher: class DoubanSearcher:
@classmethod @classmethod
def search(cls, cat: ItemCategory, c: str, q: str, p: int = 1): def search(cls, cat: ItemCategory, c: str, q: str, p: int = 1):
url = f"https://search.douban.com/{c}/subject_search?search_text={q}&start={15*(p-1)}" url = f"https://search.douban.com/{c}/subject_search?search_text={q}&start={15 * (p - 1)}"
content = DoubanDownloader(url).download().html() content = DoubanDownloader(url).download().html()
j = json.loads( j = json.loads(
content.xpath( content.xpath("//script[text()[contains(.,'window.__DATA__')]]/text()")[ # type:ignore
"//script[text()[contains(.,'window.__DATA__')]]/text()"
)[ # type:ignore
0 0
] ]
.split("window.__DATA__ = ")[1] # type:ignore .split("window.__DATA__ = ")[1] # type:ignore

View file

@ -7,7 +7,7 @@ from catalog.common import *
from catalog.models import * from catalog.models import *
from common.models.lang import detect_language from common.models.lang import detect_language
from .douban import DoubanDownloader, DoubanSearcher from .douban import DoubanDownloader
def _cache_key(url): def _cache_key(url):

View file

@ -1,5 +1,3 @@
import logging
import dateparser import dateparser
from catalog.common import * from catalog.common import *
@ -7,7 +5,7 @@ from catalog.models import *
from common.models.lang import detect_language from common.models.lang import detect_language
from common.models.misc import uniq from common.models.misc import uniq
from .douban import DoubanDownloader, DoubanSearcher from .douban import DoubanDownloader
@SiteManager.register @SiteManager.register

View file

@ -1,5 +1,5 @@
import json import json
import logging import re
from loguru import logger from loguru import logger
@ -10,7 +10,7 @@ from common.models.lang import detect_language
from common.models.misc import int_ from common.models.misc import int_
from .douban import DoubanDownloader, DoubanSearcher from .douban import DoubanDownloader, DoubanSearcher
from .tmdb import TMDB_TV, TMDB_TVSeason, query_tmdb_tv_episode, search_tmdb_by_imdb_id from .tmdb import TMDB_TV, search_tmdb_by_imdb_id
@SiteManager.register @SiteManager.register

View file

@ -1,5 +1,3 @@
import logging
import dateparser import dateparser
from catalog.common import * from catalog.common import *

View file

@ -1,5 +1,3 @@
import re
from django.conf import settings from django.conf import settings
from django.core.validators import URLValidator from django.core.validators import URLValidator
from loguru import logger from loguru import logger

View file

@ -1,6 +1,5 @@
import json import json
import logging import logging
import re
from datetime import datetime from datetime import datetime
from django.utils.timezone import make_aware from django.utils.timezone import make_aware

View file

@ -1,6 +1,6 @@
import json import json
import logging import logging
import re
from catalog.common import * from catalog.common import *
from catalog.movie.models import * from catalog.movie.models import *
from catalog.tv.models import * from catalog.tv.models import *

View file

@ -1,5 +1,3 @@
import logging
from catalog.common import * from catalog.common import *
from catalog.models import * from catalog.models import *

View file

@ -7,7 +7,6 @@ import bleach
import podcastparser import podcastparser
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator from django.core.validators import URLValidator
from django.utils.timezone import make_aware from django.utils.timezone import make_aware

View file

@ -2,7 +2,6 @@
Spotify Spotify
""" """
import datetime
import logging import logging
import time import time

View file

@ -1,7 +1,5 @@
import logging import logging
import re
import dateparser
from django.conf import settings from django.conf import settings
from catalog.common import * from catalog.common import *

View file

@ -242,9 +242,9 @@ class TMDB_TV(AbstractSite):
lambda s: { lambda s: {
"model": "TVSeason", "model": "TVSeason",
"id_type": IdType.TMDB_TVSeason, "id_type": IdType.TMDB_TVSeason,
"id_value": f'{self.id_value}-{s["season_number"]}', "id_value": f"{self.id_value}-{s['season_number']}",
"title": s["name"], "title": s["name"],
"url": f'{self.url}/season/{s["season_number"]}', "url": f"{self.url}/season/{s['season_number']}",
}, },
res_data["seasons"], res_data["seasons"],
) )
@ -359,7 +359,7 @@ class TMDB_TVSeason(AbstractSite):
pd.metadata["title"] = ( pd.metadata["title"] = (
pd.metadata["title"] pd.metadata["title"]
if pd.metadata.get("title") if pd.metadata.get("title")
else f'Season {d["season_number"]}' else f"Season {d['season_number']}"
) )
pd.metadata["episode_number_list"] = list( pd.metadata["episode_number_list"] = list(
map(lambda ep: ep["episode_number"], d["episodes"]) map(lambda ep: ep["episode_number"], d["episodes"])
@ -460,7 +460,7 @@ class TMDB_TVEpisode(AbstractSite):
pd.metadata["title"] = ( pd.metadata["title"] = (
pd.metadata["title"] pd.metadata["title"]
if pd.metadata["title"] if pd.metadata["title"]
else f'S{d["season_number"]} E{d["episode_number"]}' else f"S{d['season_number']} E{d['episode_number']}"
) )
if pd.lookup_ids.get(IdType.IMDB): if pd.lookup_ids.get(IdType.IMDB):

View file

@ -25,15 +25,12 @@ For now, we follow Douban convention, but keep an eye on it in case it breaks it
""" """
import re
from functools import cached_property from functools import cached_property
from typing import TYPE_CHECKING, overload from typing import TYPE_CHECKING
from auditlog.diff import ForeignKey
from auditlog.models import QuerySet from auditlog.models import QuerySet
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from typing_extensions import override
from catalog.common import ( from catalog.common import (
BaseSchema, BaseSchema,

View file

@ -128,7 +128,7 @@ class DoubanMovieTVTestCase(TestCase):
url = "https://movie.douban.com/subject/35597581/" url = "https://movie.douban.com/subject/35597581/"
item = SiteManager.get_site_by_url(url).get_resource_ready().item item = SiteManager.get_site_by_url(url).get_resource_ready().item
# disable this test to make douban data less disrupted # disable this test to make douban data less disrupted
# self.assertEqual(item.imdb, "tt21599650") self.assertEqual(item.imdb, "tt21599650")
class MultiTVSitesTestCase(TestCase): class MultiTVSitesTestCase(TestCase):
@ -186,7 +186,7 @@ class MovieTVModelRecastTestCase(TestCase):
@use_local_response @use_local_response
def test_recast(self): def test_recast(self):
from catalog.models import Movie, TVShow from catalog.models import Movie
url2 = "https://www.imdb.com/title/tt0436992/" url2 = "https://www.imdb.com/title/tt0436992/"
p2 = SiteManager.get_site_by_url(url2).get_resource_ready() p2 = SiteManager.get_site_by_url(url2).get_resource_ready()
@ -232,10 +232,10 @@ class IMDBTestCase(TestCase):
@use_local_response @use_local_response
def test_get_episode_list(self): def test_get_episode_list(self):
l = IMDB.get_episode_list("tt0436992", 4) episodes = IMDB.get_episode_list("tt0436992", 4)
self.assertEqual(len(l), 14) self.assertEqual(len(episodes), 14)
l = IMDB.get_episode_list("tt1205438", 4) episodes = IMDB.get_episode_list("tt1205438", 4)
self.assertEqual(len(l), 14) self.assertEqual(len(episodes), 14)
@use_local_response @use_local_response
def test_tvshow(self): def test_tvshow(self):

View file

@ -22,12 +22,10 @@ from journal.models import (
Review, Review,
ShelfManager, ShelfManager,
ShelfMember, ShelfMember,
ShelfType,
q_piece_in_home_feed_of_user, q_piece_in_home_feed_of_user,
q_piece_visible_to_user, q_piece_visible_to_user,
) )
from takahe.utils import Takahe from takahe.utils import Takahe
from users.views import announcements
from .forms import * from .forms import *
from .models import * from .models import *

View file

@ -1,4 +1,4 @@
from typing import Any, Callable, List, Optional, Tuple, Type from typing import Any, List
from django.conf import settings from django.conf import settings
from django.db.models import QuerySet from django.db.models import QuerySet
@ -91,7 +91,7 @@ class PageNumberPagination(NinjaPageNumberPagination):
api = NinjaAPI( api = NinjaAPI(
auth=OAuthAccessTokenAuth(), auth=OAuthAccessTokenAuth(),
title=f'{settings.SITE_INFO["site_name"]} API', title=f"{settings.SITE_INFO['site_name']} API",
version="1.0.0", version="1.0.0",
description=f"{settings.SITE_INFO['site_name']} API <hr/><a href='{settings.SITE_INFO['site_url']}'>Learn more</a>", description=f"{settings.SITE_INFO['site_name']} API <hr/><a href='{settings.SITE_INFO['site_url']}'>Learn more</a>",
) )

View file

@ -1,12 +1,5 @@
import json
import django.contrib.postgres.forms as postgres
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.forms import ModelForm from django.forms import ModelForm
from django.utils import formats
from django.utils.translation import gettext_lazy as _
from markdownx.fields import MarkdownxFormField
class NeoModelForm(ModelForm): class NeoModelForm(ModelForm):

View file

@ -1,10 +1,7 @@
import random
from time import sleep
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from loguru import logger from loguru import logger
from common.models import BaseJob, JobManager from common.models import JobManager
# @JobManager.register # @JobManager.register
# class DummyJob(BaseJob): # class DummyJob(BaseJob):

View file

@ -1,9 +1,6 @@
import pprint
import django_rq import django_rq
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from redis import Redis
from rq import Queue from rq import Queue
from rq.job import Job from rq.job import Job

View file

@ -1,12 +1,11 @@
import re import re
from functools import cached_property from functools import cached_property
from time import sleep from time import sleep
from typing import Iterable, Self, TypeVar from typing import Iterable, Self
import typesense import typesense
from django.conf import settings from django.conf import settings
from loguru import logger from loguru import logger
from ninja import Query
from typesense.collection import Collection from typesense.collection import Collection
from typesense.exceptions import ObjectNotFound from typesense.exceptions import ObjectNotFound

View file

@ -1,24 +1,24 @@
""" """
Language support utilities Language support utilities
get site wide preferences: get site wide preferences:
SITE_DEFAULT_LANGUAGE SITE_DEFAULT_LANGUAGE
SITE_PREFERRED_LANGUAGES SITE_PREFERRED_LANGUAGES
SITE_PREFERRED_LOCALES SITE_PREFERRED_LOCALES
get available choices based on site wide preferences: get available choices based on site wide preferences:
LANGUAGE_CHOICES LANGUAGE_CHOICES
LOCALE_CHOICES LOCALE_CHOICES
SCRIPT_CHOICES SCRIPT_CHOICES
based on user preferences: based on user preferences:
get_current_locales() get_current_locales()
detect language based on text: detect language based on text:
detect_language() detect_language()
refereneces: refereneces:
https://en.wikipedia.org/wiki/IETF_language_tag https://en.wikipedia.org/wiki/IETF_language_tag
""" """
import re import re

View file

@ -9,9 +9,7 @@ from takahe.models import Config as TakaheConfig
from takahe.models import Domain as TakaheDomain from takahe.models import Domain as TakaheDomain
from takahe.models import Identity as TakaheIdentity from takahe.models import Identity as TakaheIdentity
from takahe.models import Relay as TakaheRelay from takahe.models import Relay as TakaheRelay
from takahe.models import User as TakaheUser
from takahe.utils import Takahe from takahe.utils import Takahe
from users.models import User
class Setup: class Setup:

View file

@ -24,19 +24,26 @@
{% if site_color == 'azure' %} {% if site_color == 'azure' %}
<link rel="stylesheet" <link rel="stylesheet"
href="{{ cdn_url }}/npm/@picocss/pico@2/css/pico.min.css" /> href="{{ cdn_url }}/npm/@picocss/pico@2/css/pico.min.css" />
<style>
{% else %} {% else %}
<link rel="stylesheet" href="{{ cdn_url }}/npm/@picocss/pico@2/css/pico.{{site_color}}.min.css" /> <link rel="stylesheet"
<style> href="{{ cdn_url }}/npm/@picocss/pico@2/css/pico.{{ site_color }}.min.css" />
{% endif %} {% endif %}
<style>
:where(nav li)::before { :where(nav li)::before {
float: none; float: none;
content: " "; content: " ";
} }
</style> </style>
<link href="{% sass_src 'scss/neodb.scss' %}" rel="stylesheet" type="text/css" /> <link href="{% sass_src 'scss/neodb.scss' %}"
<link href="{{ cdn_url }}/npm/@fortawesome/fontawesome-free@6.5.2/css/all.min.css" rel="stylesheet" type="text/css"> rel="stylesheet"
<link rel="search" type="application/opensearchdescription+xml" title="{{ site_name }}" href="{% static 'opensearch.xml' %}"> type="text/css" />
<link href="{{ cdn_url }}/npm/@fortawesome/fontawesome-free@6.5.2/css/all.min.css"
rel="stylesheet"
type="text/css">
<link rel="search"
type="application/opensearchdescription+xml"
title="{{ site_name }}"
href="{% static 'opensearch.xml' %}">
<script defer> <script defer>
(function(){ (function(){
const s = localStorage.getItem("user_style"); const s = localStorage.getItem("user_style");
@ -59,8 +66,8 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="{{ site_name }}"> <meta name="apple-mobile-web-app-title" content="{{ site_name }}">
{% if request.META.HTTP_HOST == site_domain %} {% if request.META.HTTP_HOST == site_domain %}
<style type="text/css">.hide_unless_alter_domain{display:none;}</style> <style type="text/css">.hide_unless_alter_domain{display:none;}</style>
{% else %} {% else %}
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
{% endif %} {% endif %}
{{ site_head|safe }} {{ site_head|safe }}

View file

@ -1,4 +1,4 @@
from datetime import date, datetime, timedelta, timezone from datetime import datetime
from django import template from django import template
from django.conf import settings from django.conf import settings
@ -48,16 +48,16 @@ def naturaldelta(v: datetime | None):
if d < 60: if d < 60:
return _("just now") return _("just now")
if d < 3600: if d < 3600:
return f"{d//60}m" return f"{d // 60}m"
if d < 86400: if d < 86400:
return f"{d//3600}h" return f"{d // 3600}h"
if d < 86400 * 14: if d < 86400 * 14:
return f"{d//86400}d" return f"{d // 86400}d"
if d < 86400 * 56: if d < 86400 * 56:
return f"{d//86400//7}w" return f"{d // 86400 // 7}w"
if d < 86400 * 30 * 18: if d < 86400 * 30 * 18:
return f"{d//86400//30}mo" return f"{d // 86400 // 30}mo"
return f"{d//86400//365}yr" return f"{d // 86400 // 365}yr"
@register.filter(is_safe=True) @register.filter(is_safe=True)

View file

@ -1,5 +1,3 @@
import re
from django import template from django import template
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -31,7 +29,7 @@ def highlight(text, search):
m = None m = None
for w in words: for w in words:
if otext[i : i + len(w)] == w: if otext[i : i + len(w)] == w:
m = f"<mark>{text[i:i+len(w)]}</mark>" m = f"<mark>{text[i : i + len(w)]}</mark>"
i += len(w) i += len(w)
break break
if not m: if not m:

View file

@ -5,10 +5,9 @@ from typing import TYPE_CHECKING
import django_rq import django_rq
from discord import SyncWebhook from discord import SyncWebhook
from django.conf import settings from django.conf import settings
from django.conf.locale import LANG_INFO
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.core.signing import b62_decode, b62_encode from django.core.signing import b62_decode
from django.http import Http404, HttpRequest, HttpResponseRedirect, QueryDict from django.http import Http404, HttpRequest, HttpResponseRedirect, QueryDict
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _

View file

@ -1,3 +1 @@
from django.contrib import admin
# Register your models here. # Register your models here.

View file

@ -14,7 +14,6 @@ from .models import (
Review, Review,
ShelfType, ShelfType,
Tag, Tag,
TagManager,
q_item_in_category, q_item_in_category,
) )

View file

@ -9,7 +9,6 @@ class JournalConfig(AppConfig):
# load key modules in proper order, make sure class inject and signal works as expected # load key modules in proper order, make sure class inject and signal works as expected
from catalog.models import Indexer from catalog.models import Indexer
from . import api
from .models import Rating, Tag from .models import Rating, Tag
Indexer.register_list_model(Tag) Indexer.register_list_model(Tag)

View file

@ -35,7 +35,7 @@ def _fetch_remote_image(url):
# logger.info(f'remote image saved as {local_url}') # logger.info(f'remote image saved as {local_url}')
return local_url return local_url
except Exception as e: except Exception as e:
logger.error(f"unable to fetch image", extra={"url": url, "exception": e}) logger.error("unable to fetch image", extra={"url": url, "exception": e})
return url return url
@ -140,7 +140,7 @@ class DoubanImporter(Task):
timestamp timestamp
- ( - (
datetime.strptime(c[1], "%Y-%m-%d %H:%M:%S") datetime.strptime(c[1], "%Y-%m-%d %H:%M:%S")
if type(c[1]) == str if isinstance(c[1], str)
else c[1] else c[1]
).replace(tzinfo=_tz_sh) ).replace(tzinfo=_tz_sh)
) )
@ -161,7 +161,7 @@ class DoubanImporter(Task):
self.import_review_sheet(self.review_data[name], name) self.import_review_sheet(self.review_data[name], name)
self.message = f"豆瓣标记和评论导入完成,共处理{self.metadata['total']}篇,已存在{self.metadata['skipped']}篇,新增{self.metadata['imported']}篇。" self.message = f"豆瓣标记和评论导入完成,共处理{self.metadata['total']}篇,已存在{self.metadata['skipped']}篇,新增{self.metadata['imported']}篇。"
if len(self.metadata["failed_urls"]) > 0: if len(self.metadata["failed_urls"]) > 0:
self.message += f'导入时未能处理{len(self.metadata["failed_urls"])}个网址。' self.message += f"导入时未能处理{len(self.metadata['failed_urls'])}个网址。"
self.save() self.save()
def import_mark_sheet(self, worksheet, shelf_type, sheet_name): def import_mark_sheet(self, worksheet, shelf_type, sheet_name):
@ -188,7 +188,7 @@ class DoubanImporter(Task):
comment = cells[7] if len(cells) >= 8 else None comment = cells[7] if len(cells) >= 8 else None
self.metadata["processed"] += 1 self.metadata["processed"] += 1
try: try:
if type(time) == str: if isinstance(time, str):
time = datetime.strptime(time, "%Y-%m-%d %H:%M:%S") time = datetime.strptime(time, "%Y-%m-%d %H:%M:%S")
time = time.replace(tzinfo=_tz_sh) time = time.replace(tzinfo=_tz_sh)
except Exception: except Exception:
@ -248,7 +248,7 @@ class DoubanImporter(Task):
content = cells[6] content = cells[6]
self.metadata["processed"] += 1 self.metadata["processed"] += 1
if time: if time:
if type(time) == str: if isinstance(time, str):
time = datetime.strptime(time, "%Y-%m-%d %H:%M:%S") time = datetime.strptime(time, "%Y-%m-%d %H:%M:%S")
time = time.replace(tzinfo=_tz_sh) time = time.replace(tzinfo=_tz_sh)
else: else:
@ -271,7 +271,7 @@ class DoubanImporter(Task):
def get_item_by_url(self, url): def get_item_by_url(self, url):
item = None item = None
if not url: if not url:
logger.warning(f"URL empty") logger.warning("URL empty")
return None return None
try: try:
site = SiteManager.get_site_by_url(url) site = SiteManager.get_site_by_url(url)

View file

@ -75,7 +75,7 @@ class GoodreadsImporter(Task):
collection.append_item(book["book"], note=book["review"]) collection.append_item(book["book"], note=book["review"])
total += 1 total += 1
collection.save() collection.save()
self.message = f'Imported {total} books from Goodreads as a Collection {shelf["title"]}.' self.message = f"Imported {total} books from Goodreads as a Collection {shelf['title']}."
elif match_profile: elif match_profile:
uid = match_profile[1] uid = match_profile[1]
shelves = { shelves = {
@ -103,7 +103,7 @@ class GoodreadsImporter(Task):
) )
): ):
print( print(
f'Skip {shelf_type}/{book["book"]} bc it was marked {mark.shelf_type}' f"Skip {shelf_type}/{book['book']} bc it was marked {mark.shelf_type}"
) )
else: else:
mark.update( mark.update(

View file

@ -28,13 +28,13 @@ class Command(BaseCommand):
if options["export"]: if options["export"]:
self.process_export(options["export"]) self.process_export(options["export"])
self.stderr.write(self.style.SUCCESS(f"Done.")) self.stderr.write(self.style.SUCCESS("Done."))
def process_export(self, collection_uuid): def process_export(self, collection_uuid):
try: try:
collection = Collection.objects.get(uid=get_uuid_or_404(collection_uuid)) collection = Collection.objects.get(uid=get_uuid_or_404(collection_uuid))
except Collection.DoesNotExist: except Collection.DoesNotExist:
self.stderr.write(self.style.ERROR(f"Collection not found.")) self.stderr.write(self.style.ERROR("Collection not found."))
return return
self.stderr.write(self.style.SUCCESS(f"Exporting {collection}")) self.stderr.write(self.style.SUCCESS(f"Exporting {collection}"))
data = { data = {

View file

@ -99,12 +99,12 @@ class Command(BaseCommand):
) )
def integrity(self): def integrity(self):
self.stdout.write(f"Checking deleted items with remaining journals...") self.stdout.write("Checking deleted items with remaining journals...")
for i in Item.objects.filter(is_deleted=True): for i in Item.objects.filter(is_deleted=True):
if i.journal_exists(): if i.journal_exists():
self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1") self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1")
self.stdout.write(f"Checking merged items with remaining journals...") self.stdout.write("Checking merged items with remaining journals...")
for i in Item.objects.filter(merged_to_item__isnull=False): for i in Item.objects.filter(merged_to_item__isnull=False):
if i.journal_exists(): if i.journal_exists():
self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1") self.stdout.write(f"! {i} : {i.absolute_url}?skipcheck=1")
@ -143,14 +143,14 @@ class Command(BaseCommand):
match action: match action:
case "integrity": case "integrity":
self.integrity() self.integrity()
self.stdout.write(self.style.SUCCESS(f"Done.")) self.stdout.write(self.style.SUCCESS("Done."))
case "purge": case "purge":
for pcls in [Content, ListMember]: for pcls in [Content, ListMember]:
for cls in pcls.__subclasses__(): for cls in pcls.__subclasses__():
self.stdout.write(f"Cleaning up {cls}...") self.stdout.write(f"Cleaning up {cls}...")
cls.objects.filter(visibility=99).delete() cls.objects.filter(visibility=99).delete()
self.stdout.write(self.style.SUCCESS(f"Done.")) self.stdout.write(self.style.SUCCESS("Done."))
case "idx-destroy": case "idx-destroy":
if yes or input(_CONFIRM).upper().startswith("Y"): if yes or input(_CONFIRM).upper().startswith("Y"):

View file

@ -1,6 +1,6 @@
from auditlog.context import set_actor from auditlog.context import set_actor
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db.models import Count, F, Q from django.db.models import Count
from django.utils.translation import gettext_lazy as t from django.utils.translation import gettext_lazy as t
from catalog.models import * from catalog.models import *

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("takahe", "0001_initial"), ("takahe", "0001_initial"),
("journal", "0015_use_identity_support_remote_piece"), ("journal", "0015_use_identity_support_remote_piece"),

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("journal", "0016_piecepost_piece_posts_piecepost_unique_piece_post"), ("journal", "0016_piecepost_piece_posts_piecepost_unique_piece_post"),
] ]

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("takahe", "0001_initial"), ("takahe", "0001_initial"),
("journal", "0017_alter_piece_options_and_more"), ("journal", "0017_alter_piece_options_and_more"),

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("journal", "0018_shelflogentrypost_shelflogentry_posts_and_more"), ("journal", "0018_shelflogentrypost_shelflogentry_posts_and_more"),
] ]

View file

@ -12,7 +12,6 @@ WHERE a.ctid < b.ctid
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("journal", "0019_alter_collection_edited_time_and_more"), ("journal", "0019_alter_collection_edited_time_and_more"),
] ]

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("users", "0019_task"), ("users", "0019_task"),
("journal", "0021_pieceinteraction_pieceinteraction_unique_interaction"), ("journal", "0021_pieceinteraction_pieceinteraction_unique_interaction"),

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("journal", "0023_debris"), ("journal", "0023_debris"),
] ]

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("journal", "0025_pin_tags"), ("journal", "0025_pin_tags"),
] ]

View file

@ -33,6 +33,7 @@ from .utils import (
__all__ = [ __all__ = [
"Collection", "Collection",
"CollectionMember", "CollectionMember",
"Content",
"FeaturedCollection", "FeaturedCollection",
"Comment", "Comment",
"JournalIndex", "JournalIndex",

View file

@ -105,8 +105,6 @@ class Collection(List):
) )
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
from takahe.utils import Takahe
if getattr(self, "catalog_item", None) is None: if getattr(self, "catalog_item", None) is None:
self.catalog_item = CatalogCollection() self.catalog_item = CatalogCollection()
if ( if (

View file

@ -1,5 +1,5 @@
from functools import cached_property from functools import cached_property
from typing import TYPE_CHECKING, Self from typing import TYPE_CHECKING
import django.dispatch import django.dispatch
from django.db import models from django.db import models

View file

@ -1,7 +1,6 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import connection, models from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from users.models import APIdentity from users.models import APIdentity

View file

@ -2,13 +2,10 @@ import re
from functools import cached_property from functools import cached_property
from typing import Any, override from typing import Any, override
from deepmerge import always_merger
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from loguru import logger
from catalog.models import Item from catalog.models import Item
from takahe.utils import Takahe
from .common import Content from .common import Content
from .renderers import render_text from .renderers import render_text

View file

@ -3,8 +3,7 @@ from typing import Any
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Avg, Count, Q from django.db.models import Avg, Count
from django.utils.translation import gettext_lazy as _
from catalog.models import Item from catalog.models import Item
from takahe.utils import Takahe from takahe.utils import Takahe

View file

@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
from functools import cached_property from functools import cached_property
from typing import TYPE_CHECKING, Any, override from typing import TYPE_CHECKING, Any
from django.conf import settings from django.conf import settings
from django.db import connection, models from django.db import connection, models

View file

@ -1,15 +1,12 @@
import re import re
from datetime import timedelta from datetime import timedelta
from functools import cached_property
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator from django.core.validators import RegexValidator
from django.db import connection, models from django.db import models
from django.db.models import Avg, Count, F, Q from django.db.models import Count, F
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from catalog.collection.models import Collection as CatalogCollection
from catalog.models import Item from catalog.models import Item
from users.models import APIdentity from users.models import APIdentity

View file

@ -1,7 +1,6 @@
from auditlog.context import set_actor from auditlog.context import set_actor
from django.db import transaction from django.db import transaction
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from django.utils.translation import gettext_lazy as _
from loguru import logger from loguru import logger
from catalog.models import Item from catalog.models import Item

View file

@ -1,5 +1,4 @@
from django import template from django import template
from django.template.defaultfilters import stringfilter
from django.utils.translation import ngettext from django.utils.translation import ngettext
from journal.models import Collection from journal.models import Collection

View file

@ -1,8 +1,6 @@
from django import template from django import template
from django.urls import reverse
from journal.models.mark import Mark from journal.models.mark import Mark
from takahe.utils import Takahe
register = template.Library() register = template.Library()

View file

@ -32,3 +32,48 @@ from .review import ReviewFeed, review_edit, review_retrieve, user_review_list
from .search import search from .search import search
from .tag import user_tag_edit, user_tag_list, user_tag_member_list from .tag import user_tag_edit, user_tag_list, user_tag_member_list
from .wrapped import WrappedShareView, WrappedView from .wrapped import WrappedShareView, WrappedView
__all__ = [
"add_to_collection",
"collection_add_featured",
"collection_append_item",
"collection_edit",
"collection_move_item",
"collection_remove_featured",
"collection_remove_item",
"collection_retrieve",
"collection_retrieve_items",
"collection_retrieve_redirect",
"collection_share",
"collection_update_item_note",
"collection_update_member_order",
"user_collection_list",
"user_liked_collection_list",
"piece_delete",
"comment",
"mark",
"mark_log",
"user_mark_list",
"wish",
"note_edit",
"piece_replies",
"post_boost",
"post_delete",
"post_like",
"post_replies",
"post_reply",
"post_unlike",
"profile",
"user_calendar_data",
"ReviewFeed",
"review_edit",
"review_retrieve",
"user_review_list",
"search",
"user_tag_edit",
"user_tag_list",
"user_tag_member_list",
"WrappedShareView",
"WrappedView",
]

View file

@ -2,7 +2,6 @@ import datetime
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest, PermissionDenied from django.core.exceptions import BadRequest, PermissionDenied
from django.core.paginator import Paginator
from django.db.models import F, Min, OuterRef, Subquery from django.db.models import F, Min, OuterRef, Subquery
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse

View file

@ -1,11 +1,10 @@
from datetime import datetime from datetime import datetime
from django import forms
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied from django.core.exceptions import BadRequest, PermissionDenied
from django.http import Http404, HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, render
from django.utils import timezone from django.utils import timezone
from django.utils.dateparse import parse_datetime from django.utils.dateparse import parse_datetime
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -15,8 +14,8 @@ from loguru import logger
from catalog.models import * from catalog.models import *
from common.utils import AuthedHttpRequest, get_uuid_or_404 from common.utils import AuthedHttpRequest, get_uuid_or_404
from ..models import Comment, Mark, ShelfManager, ShelfType, TagManager from ..models import Comment, Mark, ShelfManager, ShelfType
from .common import render_list, render_relogin, target_identity_required from .common import render_list, render_relogin
PAGE_SIZE = 10 PAGE_SIZE = 10

View file

@ -1,8 +1,8 @@
from django import forms from django import forms
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied from django.core.exceptions import BadRequest
from django.http import Http404, HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, render
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods

View file

@ -1,12 +1,12 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied from django.core.exceptions import BadRequest, PermissionDenied
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from common.utils import AuthedHttpRequest, get_uuid_or_404, target_identity_required from common.utils import AuthedHttpRequest, get_uuid_or_404
from takahe.utils import Takahe from takahe.utils import Takahe
from ..forms import * from ..forms import *

View file

@ -1,6 +1,6 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods

View file

@ -1,3 +1 @@
from django.contrib import admin
# Register your models here. # Register your models here.

View file

@ -1,5 +1,3 @@
from os import link
from django.db import models from django.db import models

View file

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here. # Create your tests here.

Some files were not shown because too many files have changed in this diff Show more