improve logging

This commit is contained in:
Your Name 2024-05-25 23:38:11 -04:00 committed by Henri Dickson
parent ce68abb9cc
commit c670b69fdd
25 changed files with 207 additions and 223 deletions

View file

@ -574,7 +574,10 @@ if SENTRY_DSN:
sentry_sdk.init(
dsn=SENTRY_DSN,
environment=sentry_env or "unknown",
integrations=[LoguruIntegration(), DjangoIntegration()],
integrations=[
DjangoIntegration(),
LoguruIntegration(event_format="{name}:{function}:{line} - {message}"),
],
release=NEODB_VERSION,
send_default_pii=True,
traces_sample_rate=env("NEODB_SENTRY_SAMPLE_RATE"),

View file

@ -22,7 +22,7 @@ from os.path import exists
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from loguru import logger as _logger
from loguru import logger
from catalog.common import (
BaseSchema,
@ -186,9 +186,9 @@ class Edition(Item):
if work_res:
work = work_res.item
if not work:
_logger.warning(f"Unable to find work for {work_res}")
logger.warning(f"Unable to find work for {work_res}")
else:
_logger.warning(
logger.warning(
f'Unable to find resource for {w["id_type"]}:{w["id_value"]}'
)
work = Work.objects.filter(

View file

@ -11,14 +11,12 @@ import filetype
import requests
from django.conf import settings
from django.core.cache import cache
from loguru import logger
from lxml import etree, html
from PIL import Image
from requests import Response
from requests.exceptions import RequestException
_logger = logging.getLogger(__name__)
RESPONSE_OK = 0 # response is ready for pasring
RESPONSE_INVALID_CONTENT = -1 # content not valid but no need to retry
RESPONSE_NETWORK_ERROR = -2 # network error, retry next proxied url
@ -67,11 +65,11 @@ class MockResponse:
try:
self.content = Path(fn).read_bytes()
self.status_code = 200
_logger.debug(f"use local response for {url} from {fn}")
logger.debug(f"use local response for {url} from {fn}")
except Exception:
self.content = b"Error: response file not found"
self.status_code = 404
_logger.debug(f"local response not found for {url} at {fn}")
logger.debug(f"local response not found for {url} at {fn}")
@property
def text(self):
@ -173,7 +171,7 @@ class BasicDownloader:
) as fp:
fp.write(resp.text)
except Exception:
_logger.warn("Save downloaded data failed.")
logger.warning("Save downloaded data failed.")
else:
resp = MockResponse(self.url)
response_type = self.validate_response(resp)
@ -249,7 +247,7 @@ class RetryDownloader(BasicDownloader):
elif self.response_type != RESPONSE_NETWORK_ERROR and retries == 0:
raise DownloadError(self)
elif retries > 0:
_logger.debug("Retry " + self.url)
logger.debug("Retry " + self.url)
time.sleep((settings.DOWNLOADER_RETRIES - retries) * 0.5)
raise DownloadError(self, "max out of retries")

View file

@ -1,4 +1,3 @@
import logging
import re
import uuid
from functools import cached_property
@ -13,6 +12,7 @@ from django.db import connection, models
from django.utils import timezone
from django.utils.baseconv import base62
from django.utils.translation import gettext_lazy as _
from loguru import logger
from ninja import Field, Schema
from polymorphic.models import PolymorphicModel
@ -24,8 +24,6 @@ from .utils import DEFAULT_ITEM_COVER, item_cover_path, resource_cover_path
if TYPE_CHECKING:
from users.models import User
_logger = logging.getLogger(__name__)
class SiteName(models.TextChoices):
Unknown = "unknown", _("Unknown")
@ -406,7 +404,7 @@ class Item(SoftDeleteMixin, PolymorphicModel):
res.save()
def recast_to(self, model):
_logger.warn(f"recast item {self} to {model}")
logger.warning(f"recast item {self} to {model}")
if self.__class__ == model:
return self
if model not in Item.__subclasses__():
@ -631,7 +629,7 @@ class ExternalResource(models.Model):
site = self.get_site()
return site.SITE_NAME if site else SiteName.Unknown
except Exception:
_logger.warning(f"Unknown site for {self}")
logger.warning(f"Unknown site for {self}")
return SiteName.Unknown
@property

View file

@ -7,19 +7,17 @@ a Site may scrape a url and store result in ResourceContent
ResourceContent persists as an ExternalResource which may link to an Item
"""
import json
import logging
import re
from dataclasses import dataclass, field
from typing import Any, Callable, Type, TypeVar
import django_rq
import requests
from loguru import logger
from validators import url as url_validate
from .models import ExternalResource, IdealIdTypes, IdType, Item, SiteName
_logger = logging.getLogger(__name__)
@dataclass
class ResourceContent:
@ -238,7 +236,7 @@ class AbstractSite:
if resource_content:
p.update_content(resource_content)
if not p.ready:
_logger.error(f"unable to get resource {self.url} ready")
logger.error(f"unable to get resource {self.url} ready")
return None
if auto_create: # and (p.item is None or p.item.is_deleted):
self.get_item()
@ -259,7 +257,7 @@ class AbstractSite:
preloaded_content=linked_resource.get("content"),
)
else:
_logger.error(f"unable to get site for {linked_url}")
logger.error(f"unable to get site for {linked_url}")
if p.related_resources or p.prematched_resources:
django_rq.get_queue("crawl").enqueue(crawl_related_resources_task, p.pk)
if p.item:
@ -338,7 +336,7 @@ class SiteManager:
def crawl_related_resources_task(resource_pk):
resource = ExternalResource.objects.filter(pk=resource_pk).first()
if not resource:
_logger.warn(f"crawl resource not found {resource_pk}")
logger.warning(f"crawl resource not found {resource_pk}")
return
links = (resource.related_resources or []) + (resource.prematched_resources or []) # type: ignore
for w in links: # type: ignore
@ -353,8 +351,8 @@ def crawl_related_resources_task(resource_pk):
site.get_resource_ready(ignore_existing_content=False, auto_link=True)
item = site.get_item()
if item:
_logger.info(f"crawled {w} {item}")
logger.info(f"crawled {w} {item}")
else:
_logger.warn(f"crawl {w} failed")
logger.warning(f"crawl {w} failed")
except Exception as e:
_logger.warn(f"crawl {w} error {e}")
logger.warning(f"crawl {w} error {e}")

View file

@ -1,11 +1,7 @@
import logging
import uuid
from django.utils import timezone
_logger = logging.getLogger(__name__)
DEFAULT_ITEM_COVER = "item/default.svg"

View file

@ -1,8 +1,7 @@
import logging
from auditlog.registry import auditlog
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from loguru import logger
from .book.models import Edition, EditionInSchema, EditionSchema, Series, Work
from .collection.models import Collection as CatalogCollection
@ -42,8 +41,6 @@ from .tv.models import (
from .search.models import Indexer # isort:skip
_logger = logging.getLogger(__name__)
# class Exhibition(Item):
@ -59,7 +56,7 @@ _logger = logging.getLogger(__name__)
def init_catalog_search_models():
if settings.DISABLE_MODEL_SIGNAL:
_logger.warn(
logger.warning(
"Catalog models are not being indexed with DISABLE_MODEL_SIGNAL configuration"
)
return
@ -100,4 +97,4 @@ def init_catalog_audit_log():
ExternalResource, include_fields=["item", "id_type", "id_value", "url"]
)
# _logger.debug(f"Catalog audit log initialized for {item_content_types().values()}")
# logger.debug(f"Catalog audit log initialized for {item_content_types().values()}")

View file

@ -53,7 +53,7 @@ class SearchResultItem:
class Goodreads:
@classmethod
def search(cls, q, page=1):
def search(cls, q: str, page=1):
results = []
search_url = f"https://www.goodreads.com/search?page={page}&q={quote_plus(q)}"
try:
@ -115,7 +115,7 @@ class Goodreads:
except requests.exceptions.RequestException as e:
logger.warning(f"Search {search_url} error: {e}")
except Exception as e:
logger.error(f"Goodreads search '{q}' error: {e}")
logger.error("Goodreads search error", extra={"query": q, "exception": e})
return results
@ -164,7 +164,7 @@ class GoogleBooks:
except requests.exceptions.RequestException as e:
logger.warning(f"Search {api_url} error: {e}")
except Exception as e:
logger.error(f"GoogleBooks search '{q}' error: {e}")
logger.error("GoogleBooks search error", extra={"query": q, "exception": e})
return results
@ -206,7 +206,7 @@ class TheMovieDatabase:
except requests.exceptions.RequestException as e:
logger.warning(f"Search {api_url} error: {e}")
except Exception as e:
logger.error(f"TMDb search '{q}' error: {e}")
logger.error("TMDb search error", extra={"query": q, "exception": e})
return results
@ -242,7 +242,7 @@ class Spotify:
except requests.exceptions.RequestException as e:
logger.warning(f"Search {api_url} error: {e}")
except Exception as e:
logger.error(f"Spotify search '{q}' error: {e}")
logger.error("Spotify search error", extra={"query": q, "exception": e})
return results
@ -278,7 +278,7 @@ class Bandcamp:
except requests.exceptions.RequestException as e:
logger.warning(f"Search {search_url} error: {e}")
except Exception as e:
logger.error(f"Goodreads search '{q}' error: {e}")
logger.error("Bandcamp search error", extra={"query": q, "exception": e})
return results
@ -305,7 +305,9 @@ class ApplePodcast:
except requests.exceptions.RequestException as e:
logger.warning(f"Search {search_url} error: {e}")
except Exception as e:
logger.error(f"ApplePodcast search '{q}' error: {e}")
logger.error(
"ApplePodcast search error", extra={"query": q, "exception": e}
)
return results
@ -322,7 +324,10 @@ class Fediverse:
)
r = response.json()
except Exception as e:
logger.warning(f"Search {api_url} error: {e}")
logger.error(
f"Fediverse search {host} error",
extra={"url": api_url, "query": q, "exception": e},
)
return []
if "data" in r:
for item in r["data"]:

View file

@ -1,12 +1,12 @@
# pyright: reportFunctionMemberAccess=false
import hashlib
import logging
import django_rq
from auditlog.context import set_actor
from django.conf import settings
from django.core.cache import cache
from django.utils.translation import gettext_lazy as _
from loguru import logger
from rq.job import Job
from catalog.common.sites import SiteManager
@ -16,8 +16,6 @@ from .typesense import Indexer as TypeSenseIndexer
# from .meilisearch import Indexer as MeiliSearchIndexer
_logger = logging.getLogger(__name__)
class DbIndexer:
@classmethod
@ -154,10 +152,10 @@ def _fetch_task(url, is_refetch, user):
site.get_resource_ready(ignore_existing_content=is_refetch)
item = site.get_item()
if item:
_logger.info(f"fetched {url} {item.url} {item}")
logger.info(f"fetched {url} {item.url} {item}")
item_url = item.url
else:
_logger.error(f"fetch {url} failed")
logger.error(f"fetch {url} failed")
except Exception as e:
_logger.error(f"fetch {url} error {e}")
logger.error(f"fetch {url} error", extra={"exception": e})
return item_url

View file

@ -1,4 +1,3 @@
import logging
import types
from datetime import timedelta
from pprint import pprint

View file

@ -6,17 +6,16 @@ use (e.g. "portal-2") as id, which is different from real id in IGDB API
import datetime
import json
import logging
import requests
from django.conf import settings
from django.core.cache import cache
from igdb.wrapper import IGDBWrapper
from loguru import logger
from catalog.common import *
from catalog.models import *
_logger = logging.getLogger(__name__)
_cache_key = "igdb_access_token"
@ -32,8 +31,8 @@ def _igdb_access_token():
token = j["access_token"]
ttl = j["expires_in"] - 60
cache.set(_cache_key, token, ttl)
except Exception:
_logger.error("unable to obtain IGDB token")
except Exception as e:
logger.error("unable to obtain IGDB token", extra={"exception": e})
token = "<invalid>"
return token
@ -156,7 +155,7 @@ class IGDB(AbstractSite):
pd.cover_image = imgdl.download().content
pd.cover_image_extention = imgdl.extention
except Exception:
_logger.debug(
logger.debug(
f'failed to download cover for {self.url} from {pd.metadata["cover_image_url"]}'
)
return pd

View file

@ -1,5 +1,3 @@
import logging
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.core.paginator import Paginator
@ -29,9 +27,6 @@ from .models import *
from .search.views import *
from .views_edit import *
_logger = logging.getLogger(__name__)
NUM_REVIEWS_ON_ITEM_PAGE = 5
NUM_REVIEWS_ON_LIST_PAGE = 20

View file

@ -1,5 +1,3 @@
import logging
from auditlog.context import set_actor
from django.contrib import messages
from django.contrib.auth.decorators import login_required
@ -9,6 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from loguru import logger
from common.utils import discord_send, get_uuid_or_404
from journal.models import update_journal_for_merged_item
@ -19,8 +18,6 @@ from .models import *
from .search.views import *
from .sites.imdb import IMDB as IMDB
_logger = logging.getLogger(__name__)
def _add_error_map_detail(e):
e.additonal_detail = []
@ -171,7 +168,7 @@ def recast(request, item_path, item_uuid):
raise BadRequest("Invalid target type")
if isinstance(item, model):
raise BadRequest("Same target type")
_logger.warn(f"{request.user} recasting {item} to {model}")
logger.warning(f"{request.user} recasting {item} to {model}")
discord_send(
"audit",
f"{item.absolute_url}\n{item.__class__.__name__}{model.__name__}\nby [@{request.user.username}]({request.user.absolute_url})",
@ -180,7 +177,7 @@ def recast(request, item_path, item_uuid):
)
if isinstance(item, TVShow):
for season in item.seasons.all():
_logger.warn(f"{request.user} recast orphaning season {season}")
logger.warning(f"{request.user} recast orphaning season {season}")
season.show = None
season.save(update_fields=["show"])
new_item = item.recast_to(model)
@ -218,7 +215,7 @@ def assign_parent(request, item_path, item_uuid):
raise BadRequest("Incompatible child item type")
# if not request.user.is_staff and item.parent_item:
# raise BadRequest("Already assigned to a parent item")
_logger.warn(f"{request.user} assign {item} to {parent_item}")
logger.warning(f"{request.user} assign {item} to {parent_item}")
item.set_parent_item(parent_item)
item.save()
return redirect(item.url)
@ -290,7 +287,7 @@ def merge(request, item_path, item_uuid):
_("Cannot merge items in different categories")
+ f" ({item.class_name} to {new_item.class_name})"
)
_logger.warn(f"{request.user} merges {item} to {new_item}")
logger.warning(f"{request.user} merges {item} to {new_item}")
item.merge_to(new_item)
update_journal_for_merged_item(item_uuid)
discord_send(
@ -302,7 +299,7 @@ def merge(request, item_path, item_uuid):
return redirect(new_item.url)
else:
if item.merged_to_item:
_logger.warn(f"{request.user} cancels merge for {item}")
logger.warning(f"{request.user} cancels merge for {item}")
item.merge_to(None)
discord_send(
"audit",
@ -334,7 +331,7 @@ def link_edition(request, item_path, item_uuid):
"catalog_merge.html",
{"item": item, "new_item": new_item, "mode": "link"},
)
_logger.warn(f"{request.user} merges {item} to {new_item}")
logger.warning(f"{request.user} merges {item} to {new_item}")
item.link_to_related_book(new_item)
discord_send(
"audit",

View file

@ -1,8 +1,8 @@
import logging
from typing import Any, Callable, List, Optional, Tuple, Type
from django.conf import settings
from django.db.models import QuerySet
from loguru import logger
from ninja import NinjaAPI, Schema
from ninja.pagination import PageNumberPagination as NinjaPageNumberPagination
from ninja.security import HttpBearer
@ -10,9 +10,6 @@ from oauth2_provider.oauth2_backends import OAuthLibCore
from oauth2_provider.oauth2_validators import OAuth2Validator
from oauthlib.oauth2 import Server
_logger = logging.getLogger(__name__)
PERMITTED_WRITE_METHODS = ["PUT", "POST", "DELETE", "PATCH"]
PERMITTED_READ_METHODS = ["GET", "HEAD", "OPTIONS"]
@ -20,7 +17,7 @@ PERMITTED_READ_METHODS = ["GET", "HEAD", "OPTIONS"]
class OAuthAccessTokenAuth(HttpBearer):
def authenticate(self, request, token) -> bool:
if not token or not request.user.is_authenticated:
_logger.debug("API auth: no access token or user not authenticated")
logger.debug("API auth: no access token or user not authenticated")
return False
request_scopes = []
request_method = request.method
@ -34,7 +31,7 @@ class OAuthAccessTokenAuth(HttpBearer):
core = OAuthLibCore(Server(validator))
valid, oauthlib_req = core.verify_request(request, scopes=request_scopes)
if not valid:
_logger.debug(f"API auth: request scope {request_scopes} not verified")
logger.debug(f"API auth: request scope {request_scopes} not verified")
return valid

View file

@ -49,7 +49,7 @@ class Command(BaseCommand):
for job_id in options["runonce"]:
run = JobManager.run(job_id)
if not run:
logger.error(f"Job not found: {job_id}")
logger.error(f"job not found: {job_id}")
if options["list"]:
all_jobs = [j.__name__ for j in JobManager.registry]
logger.info(f"{len(all_jobs)} available jobs: {' '.join(all_jobs)}")

View file

@ -1,4 +1,3 @@
import logging
import os
import re
from datetime import datetime
@ -36,8 +35,8 @@ def _fetch_remote_image(url):
binary_file.write(raw_img)
# logger.info(f'remote image saved as {local_url}')
return local_url
except Exception:
logger.error(f"unable to fetch remote image {url}")
except Exception as e:
logger.error(f"unable to fetch image", extra={"url": url, "exception": e})
return url
@ -107,7 +106,9 @@ class DoubanImporter:
self.import_from_file_task, job_id=jid
)
except Exception as e:
logger.error(e)
logger.error(
f"unable to enqueue import {uploaded_file}", extra={"exception": e}
)
return False
return True
@ -327,7 +328,7 @@ class DoubanImporter:
# logger.info(f"matched {url}")
print(".", end="", flush=True)
except Exception as e:
logger.error(f"fetching exception: {url} {e}")
logger.error(f"fetching error: {url}", extra={"exception": e})
if item is None:
self.failed.append(str(url))
return item

View file

@ -30,9 +30,9 @@ def render_relogin(request):
"common/error.html",
{
"url": reverse("users:connect") + "?domain=" + request.user.mastodon_site,
"msg": _("Data saved but unable to repost to Fediverse."),
"msg": _("Data saved but unable to repost to Fediverse instance."),
"secondary_msg": _(
"Redirecting to your Mastodon instance now to re-authenticate."
"Redirecting to your Fediverse instance now to re-authenticate."
),
},
)

View file

@ -1,4 +1,3 @@
import logging
from datetime import datetime
from django.conf import settings
@ -10,6 +9,7 @@ from django.utils import timezone
from django.utils.dateparse import parse_datetime
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from loguru import logger
from catalog.models import *
from common.utils import AuthedHttpRequest, get_uuid_or_404
@ -19,7 +19,6 @@ from takahe.utils import Takahe
from ..models import Comment, Mark, ShelfManager, ShelfType, TagManager
from .common import render_list, render_relogin, target_identity_required
_logger = logging.getLogger(__name__)
PAGE_SIZE = 10
_checkmark = "✔️".encode("utf-8")
@ -101,12 +100,12 @@ def mark(request: AuthedHttpRequest, item_uuid):
created_time=mark_date,
)
except PermissionDenied:
_logger.warn(f"post to mastodon error 401 {request.user}")
logger.warning(f"post to mastodon error 401 {request.user}")
return render_relogin(request)
except ValueError as e:
_logger.warn(f"post to mastodon error {e} {request.user}")
logger.warning(f"post to mastodon error {e} {request.user}")
err = (
_("Content too long for your Mastodon instance.")
_("Content too long for your Fediverse instance.")
if str(e) == "422"
else str(e)
)
@ -114,7 +113,9 @@ def mark(request: AuthedHttpRequest, item_uuid):
request,
"common/error.html",
{
"msg": _("Data saved but unable to repost to Fediverse."),
"msg": _(
"Data saved but unable to repost to Fediverse instance."
),
"secondary_msg": err,
},
)

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-23 11:27-0400\n"
"POT-Creation-Date: 2024-05-25 23:36-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -92,315 +92,315 @@ msgstr "出品方"
msgid "other title"
msgstr "其它标题"
#: catalog/common/models.py:31
#: catalog/common/models.py:29
msgid "Unknown"
msgstr "未知"
#: catalog/common/models.py:32
#: catalog/common/models.py:30
msgid "Douban"
msgstr "豆瓣"
#: catalog/common/models.py:33 catalog/common/models.py:66
#: catalog/common/models.py:31 catalog/common/models.py:64
msgid "Goodreads"
msgstr "Goodreads"
#: catalog/common/models.py:34 catalog/common/models.py:68
#: catalog/common/models.py:32 catalog/common/models.py:66
msgid "Google Books"
msgstr "谷歌图书"
#: catalog/common/models.py:35
#: catalog/common/models.py:33
msgid "BooksTW"
msgstr "博客來"
#: catalog/common/models.py:36 catalog/common/models.py:61
#: catalog/common/models.py:34 catalog/common/models.py:59
#: catalog/templates/movie.html:51 catalog/templates/tvseason.html:68
#: catalog/templates/tvshow.html:63
msgid "IMDb"
msgstr "IMDb"
#: catalog/common/models.py:37
#: catalog/common/models.py:35
msgid "TMDB"
msgstr "TMDB"
#: catalog/common/models.py:38 catalog/common/models.py:77
#: catalog/common/models.py:36 catalog/common/models.py:75
msgid "Bandcamp"
msgstr "Bandcamp"
#: catalog/common/models.py:39
#: catalog/common/models.py:37
msgid "Spotify"
msgstr "Spotify"
#: catalog/common/models.py:40
#: catalog/common/models.py:38
msgid "IGDB"
msgstr "IGDB"
#: catalog/common/models.py:41
#: catalog/common/models.py:39
msgid "Steam"
msgstr "Steam"
#: catalog/common/models.py:42 catalog/common/models.py:91
#: catalog/common/models.py:40 catalog/common/models.py:89
msgid "Bangumi"
msgstr "Bangumi"
#: catalog/common/models.py:43
#: catalog/common/models.py:41
msgid "BGG"
msgstr "BGG"
#: catalog/common/models.py:45
#: catalog/common/models.py:43
msgid "RSS"
msgstr "RSS"
#: catalog/common/models.py:46
#: catalog/common/models.py:44
msgid "Discogs"
msgstr "Discogs"
#: catalog/common/models.py:47 catalog/common/models.py:93
#: catalog/common/models.py:45 catalog/common/models.py:91
msgid "Apple Music"
msgstr "苹果音乐"
#: catalog/common/models.py:48 catalog/common/models.py:94
#: catalog/common/models.py:46 catalog/common/models.py:92
msgid "Fediverse"
msgstr "联邦宇宙"
#: catalog/common/models.py:52
#: catalog/common/models.py:50
msgid "WikiData"
msgstr "维基数据"
#: catalog/common/models.py:53
#: catalog/common/models.py:51
msgid "ISBN10"
msgstr "ISBN10"
#: catalog/common/models.py:54 catalog/templates/edition.html:19
#: catalog/common/models.py:52 catalog/templates/edition.html:19
msgid "ISBN"
msgstr "ISBN"
#: catalog/common/models.py:55
#: catalog/common/models.py:53
msgid "ASIN"
msgstr "ASIN"
#: catalog/common/models.py:56
#: catalog/common/models.py:54
msgid "ISSN"
msgstr "ISSN"
#: catalog/common/models.py:57
#: catalog/common/models.py:55
msgid "CUBN"
msgstr "统一书号"
#: catalog/common/models.py:58
#: catalog/common/models.py:56
msgid "ISRC"
msgstr "ISRC"
#: catalog/common/models.py:59
#: catalog/common/models.py:57
msgid "GTIN UPC EAN"
msgstr "条形码"
#: catalog/common/models.py:60
#: catalog/common/models.py:58
msgid "RSS Feed URL"
msgstr "RSS网址"
#: catalog/common/models.py:62
#: catalog/common/models.py:60
msgid "TMDB TV Serie"
msgstr "TMDB电视剧集"
#: catalog/common/models.py:63
#: catalog/common/models.py:61
msgid "TMDB TV Season"
msgstr "TMDB电视分季"
#: catalog/common/models.py:64
#: catalog/common/models.py:62
msgid "TMDB TV Episode"
msgstr "TMDB电视单集"
#: catalog/common/models.py:65
#: catalog/common/models.py:63
msgid "TMDB Movie"
msgstr "TMDB电影"
#: catalog/common/models.py:67
#: catalog/common/models.py:65
msgid "Goodreads Work"
msgstr "Goodreads著作"
#: catalog/common/models.py:69
#: catalog/common/models.py:67
msgid "Douban Book"
msgstr "豆瓣图书"
#: catalog/common/models.py:70
#: catalog/common/models.py:68
msgid "Douban Book Work"
msgstr "豆瓣图书著作"
#: catalog/common/models.py:71
#: catalog/common/models.py:69
msgid "Douban Movie"
msgstr "豆瓣电影"
#: catalog/common/models.py:72
#: catalog/common/models.py:70
msgid "Douban Music"
msgstr "豆瓣音乐"
#: catalog/common/models.py:73
#: catalog/common/models.py:71
msgid "Douban Game"
msgstr "豆瓣游戏"
#: catalog/common/models.py:74
#: catalog/common/models.py:72
msgid "Douban Drama"
msgstr "豆瓣舞台剧"
#: catalog/common/models.py:75
#: catalog/common/models.py:73
msgid "Douban Drama Version"
msgstr "豆瓣舞台剧版本"
#: catalog/common/models.py:76
#: catalog/common/models.py:74
msgid "BooksTW Book"
msgstr "博客来图书"
#: catalog/common/models.py:78
#: catalog/common/models.py:76
msgid "Spotify Album"
msgstr "Spotify专辑"
#: catalog/common/models.py:79
#: catalog/common/models.py:77
msgid "Spotify Podcast"
msgstr "Spotify播客"
#: catalog/common/models.py:88
#: catalog/common/models.py:86
msgid "IGDB Game"
msgstr "IGDB游戏"
#: catalog/common/models.py:89
#: catalog/common/models.py:87
msgid "BGG Boardgame"
msgstr "BGG桌游"
#: catalog/common/models.py:90
#: catalog/common/models.py:88
msgid "Steam Game"
msgstr "Steam游戏"
#: catalog/common/models.py:92
#: catalog/common/models.py:90
msgid "Apple Podcast"
msgstr "苹果播客"
#: catalog/common/models.py:110 catalog/common/models.py:127
#: catalog/common/models.py:140 catalog/jobs/discover.py:91
#: catalog/common/models.py:108 catalog/common/models.py:125
#: catalog/common/models.py:138 catalog/jobs/discover.py:91
#: common/templates/_header.html:25
msgid "Book"
msgstr "书"
#: catalog/common/models.py:111
#: catalog/common/models.py:109
msgid "TV Serie"
msgstr "电视剧集"
#: catalog/common/models.py:112 catalog/templates/_sidebar_edit.html:140
#: catalog/common/models.py:110 catalog/templates/_sidebar_edit.html:140
msgid "TV Season"
msgstr "电视分季"
#: catalog/common/models.py:113
#: catalog/common/models.py:111
msgid "TV Episode"
msgstr "电视单集"
#: catalog/common/models.py:114 catalog/common/models.py:128
#: catalog/common/models.py:141 catalog/templates/_sidebar_edit.html:133
#: catalog/common/models.py:112 catalog/common/models.py:126
#: catalog/common/models.py:139 catalog/templates/_sidebar_edit.html:133
msgid "Movie"
msgstr "电影"
#: catalog/common/models.py:115
#: catalog/common/models.py:113
msgid "Album"
msgstr "专辑"
#: catalog/common/models.py:116 catalog/common/models.py:131
#: catalog/common/models.py:144 common/templates/_header.html:41
#: catalog/common/models.py:114 catalog/common/models.py:129
#: catalog/common/models.py:142 common/templates/_header.html:41
msgid "Game"
msgstr "游戏"
#: catalog/common/models.py:117
#: catalog/common/models.py:115
msgid "Podcast Program"
msgstr "播客节目"
#: catalog/common/models.py:118
#: catalog/common/models.py:116
msgid "Podcast Episode"
msgstr "播客单集"
#: catalog/common/models.py:119 catalog/common/models.py:133
#: catalog/common/models.py:146 common/templates/_header.html:45
#: catalog/common/models.py:117 catalog/common/models.py:131
#: catalog/common/models.py:144 common/templates/_header.html:45
msgid "Performance"
msgstr "演出"
#: catalog/common/models.py:120
#: catalog/common/models.py:118
msgid "Production"
msgstr "上演"
#: catalog/common/models.py:121
#: catalog/common/models.py:119
msgid "Fanfic"
msgstr "网文"
#: catalog/common/models.py:122 catalog/common/models.py:135
#: catalog/common/models.py:120 catalog/common/models.py:133
msgid "Exhibition"
msgstr "展览"
#: catalog/common/models.py:123 catalog/common/models.py:136
#: catalog/common/models.py:121 catalog/common/models.py:134
#: journal/templates/collection.html:15 journal/templates/collection.html:22
#: journal/templates/collection_edit.html:9
#: journal/templates/collection_share.html:12
msgid "Collection"
msgstr "收藏单"
#: catalog/common/models.py:129 catalog/common/models.py:142
#: catalog/common/models.py:127 catalog/common/models.py:140
msgid "TV"
msgstr "剧集"
#: catalog/common/models.py:130 catalog/common/models.py:143
#: catalog/common/models.py:128 catalog/common/models.py:141
#: common/templates/_header.html:37
msgid "Music"
msgstr "音乐"
#: catalog/common/models.py:132 catalog/common/models.py:145
#: catalog/common/models.py:130 catalog/common/models.py:143
#: catalog/templates/_sidebar_edit.html:152 common/templates/_header.html:33
msgid "Podcast"
msgstr "播客"
#: catalog/common/models.py:134
#: catalog/common/models.py:132
msgid "FanFic"
msgstr "网文"
#: catalog/common/models.py:257 journal/models/collection.py:48
#: catalog/common/models.py:255 journal/models/collection.py:48
msgid "title"
msgstr "标题"
#: catalog/common/models.py:258 journal/models/collection.py:49
#: catalog/common/models.py:256 journal/models/collection.py:49
msgid "description"
msgstr "描述"
#: catalog/common/models.py:260 catalog/forms.py:26
#: catalog/common/models.py:258 catalog/forms.py:26
msgid "Primary ID Type"
msgstr "主要标识类型"
#: catalog/common/models.py:263 catalog/forms.py:31
#: catalog/common/models.py:261 catalog/forms.py:31
msgid "Primary ID Value"
msgstr "主要标识数据"
#: catalog/common/models.py:269
#: catalog/common/models.py:267
msgid "metadata"
msgstr "元数据"
#: catalog/common/models.py:271
#: catalog/common/models.py:269
msgid "cover"
msgstr "封面"
#: catalog/common/models.py:565
#: catalog/common/models.py:563
msgid "source site"
msgstr "来源站点"
#: catalog/common/models.py:567
#: catalog/common/models.py:565
msgid "ID on source site"
msgstr "来源站点标识"
#: catalog/common/models.py:569
#: catalog/common/models.py:567
msgid "source url"
msgstr "来源站点网址"
#: catalog/common/models.py:581
#: catalog/common/models.py:579
msgid "IdType of the source site"
msgstr "来源站点的主要标识类型"
#: catalog/common/models.py:587
#: catalog/common/models.py:585
msgid "Primary Id on the source site"
msgstr "来源站点的主要标识数据"
#: catalog/common/models.py:590
#: catalog/common/models.py:588
msgid "url to the resource"
msgstr "指向外部资源的网址"
@ -1361,17 +1361,17 @@ msgstr "{show_title} 第{season_number}季"
msgid "{season_title} E{episode_number}"
msgstr "{season_title} 第{episode_number}集"
#: catalog/views.py:53 catalog/views.py:76
#: catalog/views.py:49 catalog/views.py:72
msgid "Item not found"
msgstr "条目不存在"
#: catalog/views.py:57 catalog/views.py:84
#: catalog/views.py:53 catalog/views.py:80
msgid "Item no longer exists"
msgstr "条目已不存在"
#: catalog/views_edit.py:125 catalog/views_edit.py:148
#: catalog/views_edit.py:200 catalog/views_edit.py:276
#: catalog/views_edit.py:353 journal/views/collection.py:52
#: catalog/views_edit.py:122 catalog/views_edit.py:145
#: catalog/views_edit.py:197 catalog/views_edit.py:273
#: catalog/views_edit.py:350 journal/views/collection.py:52
#: journal/views/collection.py:102 journal/views/collection.py:114
#: journal/views/collection.py:130 journal/views/collection.py:161
#: journal/views/collection.py:180 journal/views/collection.py:200
@ -1383,34 +1383,34 @@ msgstr "条目已不存在"
msgid "Insufficient permission"
msgstr "权限不足"
#: catalog/views_edit.py:203 journal/views/collection.py:229
#: catalog/views_edit.py:200 journal/views/collection.py:229
#: journal/views/collection.py:296 journal/views/common.py:81
#: journal/views/mark.py:139 journal/views/post.py:41 journal/views/post.py:55
#: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169
msgid "Invalid parameter"
msgstr "无效参数"
#: catalog/views_edit.py:252
#: catalog/views_edit.py:249
msgid "Must be a TV Season with IMDB id and season id"
msgstr ""
#: catalog/views_edit.py:257
#: catalog/views_edit.py:254
msgid "Updating episodes"
msgstr ""
#: catalog/views_edit.py:287
#: catalog/views_edit.py:284
msgid "Cannot be merged to an item already deleted or merged"
msgstr ""
#: catalog/views_edit.py:290
#: catalog/views_edit.py:287
msgid "Cannot merge items in different categories"
msgstr ""
#: catalog/views_edit.py:327
#: catalog/views_edit.py:324
msgid "Cannot be linked to an item already deleted or merged"
msgstr ""
#: catalog/views_edit.py:329
#: catalog/views_edit.py:326
msgid "Cannot link items other than editions"
msgstr ""
@ -2238,7 +2238,7 @@ msgstr "日历"
msgid "annual summary"
msgstr "年度小结"
#: journal/templates/profile.html:131 mastodon/api.py:676
#: journal/templates/profile.html:131 mastodon/api.py:677
msgid "collection"
msgstr "收藏单"
@ -2386,21 +2386,21 @@ msgstr "找不到条目,请使用本站条目网址。"
msgid "Login required"
msgstr "登录后访问"
#: journal/views/common.py:33 journal/views/mark.py:117
msgid "Data saved but unable to repost to Fediverse."
msgstr "数据已保存但未能转发到联邦宇宙。"
#: journal/views/common.py:33 journal/views/mark.py:116
msgid "Data saved but unable to repost to Fediverse instance."
msgstr "数据已保存但未能转发到联邦实例。"
#: journal/views/common.py:35
msgid "Redirecting to your Mastodon instance now to re-authenticate."
msgstr "正在重定向到你的Mastodon实例以重新认证。"
msgid "Redirecting to your Fediverse instance now to re-authenticate."
msgstr "正在重定向到你的联邦实例以重新认证。"
#: journal/views/common.py:42
msgid "List not found."
msgstr "列表未找到"
#: journal/views/mark.py:109
msgid "Content too long for your Mastodon instance."
msgstr "内容过长,超出了你的Mastodon实例的限制。"
#: journal/views/mark.py:107
msgid "Content too long for your Fediverse instance."
msgstr "内容过长,超出了你的联邦实例的限制。"
#: journal/views/mark.py:161 journal/views/review.py:30
msgid "Content not found"
@ -2444,16 +2444,16 @@ msgstr "标签已更新"
msgid "Summary posted to timeline."
msgstr "总结已发布到时间轴"
#: mastodon/api.py:519 takahe/utils.py:514
#: mastodon/api.py:520 takahe/utils.py:514
#, python-brace-format
msgid "regarding {item_title}, may contain spoiler or triggering content"
msgstr "关于 {item_title},可能包含剧透或敏感内容"
#: mastodon/api.py:681
#: mastodon/api.py:682
msgid "shared my collection"
msgstr "分享我的收藏单"
#: mastodon/api.py:684
#: mastodon/api.py:685
#, python-brace-format
msgid "shared {username}'s collection"
msgstr "分享 {username} 的收藏单"
@ -2801,8 +2801,8 @@ msgid "Authentication failed"
msgstr "认证失败"
#: users/account.py:159
msgid "Invalid response from Mastodon instance."
msgstr "Mastodon实例返回信息无效"
msgid "Invalid response from Fediverse instance."
msgstr "联邦实例返回信息无效"
#: users/account.py:169
msgid "Invalid cookie data."
@ -2813,12 +2813,12 @@ msgid "Invalid instance domain"
msgstr "实例域名无效"
#: users/account.py:182
msgid "Invalid token from Mastodon instance."
msgstr "Mastodon实例返回了无效的认证令牌。"
msgid "Invalid token from Fediverse instance."
msgstr "联邦实例返回了无效的认证令牌。"
#: users/account.py:205 users/account.py:559
msgid "Invalid account data from Mastodon instance."
msgstr "Mastodon实例返回了无效的账号数据。"
msgid "Invalid account data from Fediverse instance."
msgstr "联邦实例返回了无效的账号数据。"
#: users/account.py:230
msgid "Registration is for invitation only"
@ -2953,39 +2953,39 @@ msgstr "登录信息已更新"
msgid "Account mismatch."
msgstr "账号信息不匹配。"
#: users/data.py:123
#: users/data.py:124
msgid "Generating exports."
msgstr "正在生成导出文件。"
#: users/data.py:135
#: users/data.py:136
msgid "Export file expired. Please export again."
msgstr "导出文件已失效,请重新导出"
#: users/data.py:146
#: users/data.py:147
msgid "Sync in progress."
msgstr "正在同步。"
#: users/data.py:160
#: users/data.py:161
msgid "Settings saved."
msgstr "设置已保存"
#: users/data.py:171
#: users/data.py:172
msgid "Reset completed."
msgstr "重置已完成。"
#: users/data.py:180
#: users/data.py:181
msgid "Import in progress."
msgstr "正在导出"
#: users/data.py:182
#: users/data.py:183
msgid "Invalid URL."
msgstr "无效网址。"
#: users/data.py:196 users/data.py:221 users/data.py:236
#: users/data.py:197 users/data.py:222 users/data.py:237
msgid "File is uploaded and will be imported soon."
msgstr "文件已上传,等待后台导入。"
#: users/data.py:199 users/data.py:239
#: users/data.py:200 users/data.py:240
msgid "Invalid file."
msgstr "无效文件。"

View file

@ -4,6 +4,7 @@ reportIncompatibleVariableOverride = false
reportUnusedImport = false
reportUnknownVariableType = false
reportConstantRedefinition = false
reportUnusedCallResult = false
[tool.djlint]
ignore="T002,T003,H005,H006,H019,H020,H021,H023,H030,H031,D018"

View file

@ -6,7 +6,6 @@ ActivityManager generates chronological view for user and, in future, ActivitySt
"""
import logging
from functools import cached_property
from typing import Type
@ -15,6 +14,7 @@ from django.db import models
from django.db.models import Q
from django.db.models.signals import post_delete, post_save, pre_delete
from django.utils import timezone
from loguru import logger
from catalog.common.models import Item
from journal.models import (
@ -29,8 +29,6 @@ from journal.models import (
)
from users.models import APIdentity
_logger = logging.getLogger(__name__)
class ActivityTemplate(models.TextChoices):
MarkItem = "mark_item"
@ -105,7 +103,7 @@ class DataSignalManager:
@staticmethod
def add_handler_for_model(model):
if settings.DISABLE_MODEL_SIGNAL:
_logger.warn(
logger.warning(
f"{model.__name__} are not being indexed with DISABLE_MODEL_SIGNAL configuration"
)
return

View file

@ -1,5 +1,3 @@
import logging
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.urls import reverse

View file

@ -156,7 +156,7 @@ def connect_redirect_back(request):
"common/error.html",
{
"msg": _("Authentication failed"),
"secondary_msg": _("Invalid response from Mastodon instance."),
"secondary_msg": _("Invalid response from Fediverse instance."),
},
)
site = request.session.get("mastodon_domain")
@ -179,7 +179,7 @@ def connect_redirect_back(request):
"common/error.html",
{
"msg": _("Authentication failed"),
"secondary_msg": _("Invalid token from Mastodon instance."),
"secondary_msg": _("Invalid token from Fediverse instance."),
},
)
@ -202,7 +202,7 @@ def connect_redirect_back(request):
"common/error.html",
{
"msg": _("Authentication failed"),
"secondary_msg": _("Invalid account data from Mastodon instance."),
"secondary_msg": _("Invalid account data from Fediverse instance."),
},
)
return register_new_user(
@ -348,7 +348,7 @@ def send_verification_link(user_id, action, email, code=""):
fail_silently=False,
)
except Exception as e:
logger.error(e)
logger.error(f"send email {email} failed", extra={"exception": e})
@require_http_methods(["POST"])
@ -418,7 +418,7 @@ def verify_email(request):
else:
return register_new_user(request, username=None, email=email)
except Exception as e:
logger.error(e)
logger.error("verify email error", extra={"exception": e, "s": s})
error = _("Unable to verify")
return render(
request, "users/verify_email.html", {"success": False, "error": error}
@ -556,7 +556,7 @@ def swap_login(request, token, site, refresh_token):
)
else:
messages.add_message(
request, messages.ERROR, _("Invalid account data from Mastodon instance.")
request, messages.ERROR, _("Invalid account data from Fediverse instance.")
)
return redirect(reverse("users:data"))

View file

@ -66,7 +66,9 @@ class Task(models.Model):
task.state = cls.States.complete
task.save(update_fields=["state"])
except Exception as e:
logger.error(f"Task {task} Exception {e}")
logger.error(
f"error running {cls.__name__}", extra={"exception": e, "task": task_id}
)
task.message = "Error occured."
task.state = cls.States.failed
task.save(update_fields=["state", "message"])

View file

@ -310,7 +310,10 @@ class User(AbstractUser):
f = ContentFile(r.content, name=identity.icon_uri.split("/")[-1])
identity.icon.save(f.name, f, save=False)
except Exception as e:
logger.error(f"Get icon failed: {identity} {identity.icon_uri} {e}")
logger.error(
f"fetch icon failed: {identity} {identity.icon_uri}",
extra={"exception": e},
)
identity.save()
def refresh_mastodon_data(self, skip_detail=False):