[black] Reformat to use 88 line lengths

This commit is contained in:
2026-03-23 16:17:15 -04:00
parent ab4b5470b7
commit d576467db8
83 changed files with 310 additions and 786 deletions

View File

@ -88,7 +88,7 @@ testpaths = ["tests"]
DJANGO_SETTINGS_MODULE='vrobbler.settings-testing'
[tool.black]
line-length = 79
line-length = 88
target-version = ["py39", "py310"]
include = ".py$"
exclude = "migrations"

View File

@ -93,41 +93,29 @@ class TestRecipeScraperService:
assert result is True
def test_scrape_returns_title(self, scraper):
result = scraper.scrape(
RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe"
)
result = scraper.scrape(RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe")
assert result["title"] == "Test Recipe"
def test_scrape_returns_ingredients(self, scraper):
result = scraper.scrape(
RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe"
)
result = scraper.scrape(RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe")
assert len(result["ingredients"]) == 3
assert "1 cup flour" in result["ingredients"]
def test_scrape_returns_instructions(self, scraper):
result = scraper.scrape(
RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe"
)
result = scraper.scrape(RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe")
assert len(result["instructions"]) > 0
assert "Mix ingredients together" in result["instructions"]
def test_scrape_returns_yields(self, scraper):
result = scraper.scrape(
RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe"
)
result = scraper.scrape(RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe")
assert result["yields"] == "4 servings"
def test_scrape_returns_total_time(self, scraper):
result = scraper.scrape(
RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe"
)
result = scraper.scrape(RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe")
assert result["total_time"] == 30
def test_scrape_returns_url(self, scraper):
result = scraper.scrape(
RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe"
)
result = scraper.scrape(RECIPE_HTML_WITH_SCHEMA, "https://example.com/recipe")
assert result["url"] == "https://example.com/recipe"
def test_scrape_raises_on_invalid_html(self, scraper):

View File

@ -9,9 +9,7 @@ from foods.sources.usda import (
class TestUSDAFoodAPI:
@pytest.fixture
def usda_api(self):
with patch(
"vrobbler.apps.foods.sources.usda.settings"
) as mock_settings:
with patch("vrobbler.apps.foods.sources.usda.settings") as mock_settings:
mock_settings.USDA_API_KEY = "test_api_key"
return USDAFoodAPI(api_key="test_api_key")

View File

@ -1,9 +1,7 @@
import pytest
from vrobbler.apps.podcasts.scrapers import scrape_data_from_google_podcasts
expected_desc_snippet = (
"NPR's Up First is the news you need to start your day. "
)
expected_desc_snippet = "NPR's Up First is the news you need to start your day. "
expected_img_url = "https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcR1F0CfR24RR6sme531yIkCrnK4zzmo97jeualO5drVPKG6oCk"
expected_google_url = "https://podcasts.google.com/feed/aHR0cHM6Ly9mZWVkcy5ucHIub3JnLzUxMDMxOC9wb2RjYXN0LnhtbA"

View File

@ -68,9 +68,7 @@ class MopidyRequest:
"artist": kwargs.get("artist", self.artist),
"album": kwargs.get("album", self.album),
"track_number": int(kwargs.get("track_number", self.track_number)),
"run_time_ticks": int(
kwargs.get("run_time_ticks", self.run_time_ticks)
),
"run_time_ticks": int(kwargs.get("run_time_ticks", self.run_time_ticks)),
"run_time": int(kwargs.get("run_time", self.run_time)),
"playback_time_ticks": int(
kwargs.get("playback_time_ticks", self.playback_time_ticks)
@ -113,9 +111,7 @@ def mopidy_track():
@pytest.fixture
def mopidy_track_diff_album_request_data(**kwargs):
mb_album_id = "0c56c457-afe1-4679-baab-759ba8dd2a58"
return MopidyRequest(
album="Gold", musicbrainz_album_id=mb_album_id
).request_json
return MopidyRequest(album="Gold", musicbrainz_album_id=mb_album_id).request_json
@pytest.fixture

View File

@ -6,7 +6,5 @@ from vrobbler.apps.scrobbles.utils import timestamp_user_tz_to_utc
def test_timestamp_user_tz_to_utc():
timestamp = timestamp_user_tz_to_utc(
1685561082, pytz.timezone("US/Eastern")
)
timestamp = timestamp_user_tz_to_utc(1685561082, pytz.timezone("US/Eastern"))
assert timestamp == datetime(2023, 5, 31, 23, 24, 42, tzinfo=pytz.utc)

View File

@ -498,9 +498,7 @@ def test_scrobble_detail_view_with_notes_as_flat_list(client):
user = get_user_model().objects.create_user(
username="testuser", email="test@example.com", password="testpass"
)
task = Task.objects.create(
title="Test Task", description="Test description"
)
task = Task.objects.create(title="Test Task", description="Test description")
scrobble = Scrobble.objects.create(
task=task,
media_type="Task",
@ -522,9 +520,7 @@ def test_scrobble_detail_view_with_notes_as_dict_timestamps(client):
user = get_user_model().objects.create_user(
username="testuser", email="test@example.com", password="testpass"
)
task = Task.objects.create(
title="Test Task", description="Test description"
)
task = Task.objects.create(title="Test Task", description="Test description")
scrobble = Scrobble.objects.create(
task=task,
media_type="Task",
@ -552,9 +548,7 @@ def test_scrobble_detail_view_with_notes_and_labels(client):
user = get_user_model().objects.create_user(
username="testuser", email="test@example.com", password="testpass"
)
task = Task.objects.create(
title="Test Task", description="Test description"
)
task = Task.objects.create(title="Test Task", description="Test description")
scrobble = Scrobble.objects.create(
task=task,
media_type="Task",
@ -580,9 +574,7 @@ def test_scrobble_detail_view_post_updates_log(client):
user = get_user_model().objects.create_user(
username="testuser", email="test@example.com", password="testpass"
)
task = Task.objects.create(
title="Test Task", description="Test description"
)
task = Task.objects.create(title="Test Task", description="Test description")
scrobble = Scrobble.objects.create(
task=task,
media_type="Task",

View File

@ -14,9 +14,7 @@ def mock_request():
class TestVersionInfo:
def test_returns_version_and_commit(self, mock_request):
with (
patch(
"vrobbler.context_processors.get_version"
) as mock_get_version,
patch("vrobbler.context_processors.get_version") as mock_get_version,
patch(
"vrobbler.context_processors.subprocess.check_output"
) as mock_check_output,
@ -32,9 +30,7 @@ class TestVersionInfo:
def test_uses_env_commit_if_set(self, mock_request):
with (
patch.dict(os.environ, {"VROBBLER_COMMIT": "env_commit_hash"}),
patch(
"vrobbler.context_processors.get_version"
) as mock_get_version,
patch("vrobbler.context_processors.get_version") as mock_get_version,
):
mock_get_version.return_value = "1.0.0"
@ -44,9 +40,7 @@ class TestVersionInfo:
def test_returns_unknown_when_version_fails(self, mock_request):
with (
patch(
"vrobbler.context_processors.get_version"
) as mock_get_version,
patch("vrobbler.context_processors.get_version") as mock_get_version,
patch(
"vrobbler.context_processors.subprocess.check_output"
) as mock_check_output,
@ -62,9 +56,7 @@ class TestVersionInfo:
import subprocess
with (
patch(
"vrobbler.context_processors.get_version"
) as mock_get_version,
patch("vrobbler.context_processors.get_version") as mock_get_version,
patch(
"vrobbler.context_processors.subprocess.check_output"
) as mock_check_output,
@ -80,9 +72,7 @@ class TestVersionInfo:
import subprocess
with (
patch(
"vrobbler.context_processors.get_version"
) as mock_get_version,
patch("vrobbler.context_processors.get_version") as mock_get_version,
patch(
"vrobbler.context_processors.subprocess.check_output"
) as mock_check_output,

View File

@ -48,12 +48,8 @@ class TestVideoAPI:
assert response.status_code == 200
assert response.data["title"] == "Test Video"
def test_filter_videos_by_channel(
self, client, auth_headers, channel, video
):
response = client.get(
f"/api/v1/videos/?channel={channel.id}", **auth_headers
)
def test_filter_videos_by_channel(self, client, auth_headers, channel, video):
response = client.get(f"/api/v1/videos/?channel={channel.id}", **auth_headers)
assert response.status_code == 200
assert len(response.data["results"]) == 1
assert response.data["results"][0]["channel"] == channel.id
@ -68,9 +64,7 @@ class TestChannelAPI:
assert response.data["results"][0]["name"] == "Test Channel"
def test_get_channel(self, client, auth_headers, channel):
response = client.get(
f"/api/v1/channels/{channel.id}/", **auth_headers
)
response = client.get(f"/api/v1/channels/{channel.id}/", **auth_headers)
assert response.status_code == 200
assert response.data["name"] == "Test Channel"

View File

@ -67,9 +67,7 @@ class Beer(ScrobblableMixin):
)
untappd_id = models.CharField(max_length=255, **BNULL)
untappd_rating = models.FloatField(**BNULL)
producer = models.ForeignKey(
BeerProducer, on_delete=models.DO_NOTHING, **BNULL
)
producer = models.ForeignKey(BeerProducer, on_delete=models.DO_NOTHING, **BNULL)
def get_absolute_url(self) -> str:
return reverse("beers:beer_detail", kwargs={"slug": self.uuid})
@ -130,9 +128,7 @@ class Beer(ScrobblableMixin):
)
style_ids.append(style_inst.id)
producer, _created = BeerProducer.objects.get_or_create(
**producer_dict
)
producer, _created = BeerProducer.objects.get_or_create(**producer_dict)
beer_dict["producer_id"] = producer.id
beer = Beer.objects.create(**beer_dict)
for style_id in style_ids:

View File

@ -85,9 +85,7 @@ def get_ibu_from_soup(soup) -> Optional[int]:
def get_rating_from_soup(soup) -> str:
rating = ""
try:
rating = float(
soup.find(class_="num").get_text().strip("(").strip(")")
)
rating = float(soup.find(class_="num").get_text().strip("(").strip(")"))
except AttributeError:
rating = None
except ValueError:
@ -124,9 +122,7 @@ def get_beer_from_untappd_id(untappd_id: str) -> dict:
beer_dict = {"untappd_id": untappd_id}
if response.status_code != 200:
logger.warn(
"Bad response from untappd.com", extra={"response": response}
)
logger.warn("Bad response from untappd.com", extra={"response": response})
return beer_dict
soup = BeautifulSoup(response.text, "html.parser")

View File

@ -14,9 +14,7 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
SEARCH_ID_URL = (
"https://boardgamegeek.com/xmlapi/search?search={query}&exact=1"
)
SEARCH_ID_URL = "https://boardgamegeek.com/xmlapi/search?search={query}&exact=1"
GAME_ID_URL = "https://boardgamegeek.com/xmlapi/boardgame/{id}"
BGG_ACCESS_TOKEN = getattr(settings, "BGG_ACCESS_TOKEN", "")
BASE_HEADERS = {

View File

@ -99,10 +99,7 @@ class BoardGameLogData(BaseLogData, LongPlayLogData):
def player_log(self) -> str:
if self.players:
return ", ".join(
[
BoardGameScoreLogData(**player).__str__()
for player in self.players
]
[BoardGameScoreLogData(**player).__str__() for player in self.players]
)
return ""
@ -134,9 +131,7 @@ class BoardGamePublisher(TimeStampedModel):
return self.name
def get_absolute_url(self):
return reverse(
"boardgames:publisher_detail", kwargs={"slug": self.uuid}
)
return reverse("boardgames:publisher_detail", kwargs={"slug": self.uuid})
class BoardGameDesigner(TimeStampedModel):
@ -149,9 +144,7 @@ class BoardGameDesigner(TimeStampedModel):
return str(self.name)
def get_absolute_url(self):
return reverse(
"boardgames:designer_detail", kwargs={"slug": self.uuid}
)
return reverse("boardgames:designer_detail", kwargs={"slug": self.uuid})
class BoardGameLocation(TimeStampedModel):
@ -159,23 +152,17 @@ class BoardGameLocation(TimeStampedModel):
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
bgstats_id = models.UUIDField(**BNULL)
description = models.TextField(**BNULL)
geo_location = models.ForeignKey(
GeoLocation, **BNULL, on_delete=models.DO_NOTHING
)
geo_location = models.ForeignKey(GeoLocation, **BNULL, on_delete=models.DO_NOTHING)
def __str__(self) -> str:
return str(self.name)
def get_absolute_url(self):
return reverse(
"boardgames:location_detail", kwargs={"slug": self.uuid}
)
return reverse("boardgames:location_detail", kwargs={"slug": self.uuid})
class BoardGame(ScrobblableMixin):
COMPLETION_PERCENT = getattr(
settings, "BOARD_GAME_COMPLETION_PERCENT", 100
)
COMPLETION_PERCENT = getattr(settings, "BOARD_GAME_COMPLETION_PERCENT", 100)
FIELDS_FROM_BGGEEK = [
"igdb_id",
@ -250,9 +237,7 @@ class BoardGame(ScrobblableMixin):
return self.title
def get_absolute_url(self):
return reverse(
"boardgames:boardgame_detail", kwargs={"slug": self.uuid}
)
return reverse("boardgames:boardgame_detail", kwargs={"slug": self.uuid})
@property
def logdata_cls(self):
@ -316,9 +301,7 @@ class BoardGame(ScrobblableMixin):
self.cover.save(fname, ContentFile(r.content), save=True)
@classmethod
def find_or_create(
cls, lookup_id: str, data: dict[str, Any] = {}
) -> "BoardGame":
def find_or_create(cls, lookup_id: str, data: dict[str, Any] = {}) -> "BoardGame":
"""Given a Lookup ID (either BGG or BGA ID), return a board game object"""
game = cls.objects.filter(bggeek_id=lookup_id).first()
if not game:
@ -354,9 +337,7 @@ class BoardGame(ScrobblableMixin):
game.uses_teams = data.get("useTeams", False)
game.bgstats_id = data.get("uuid", None)
if publisher:
publisher, _ = BoardGamePublisher.objects.get_or_create(
name=publisher
)
publisher, _ = BoardGamePublisher.objects.get_or_create(name=publisher)
game.publisher = publisher
game.save()
@ -369,9 +350,7 @@ class BoardGame(ScrobblableMixin):
if publishers:
for name in publishers:
publisher, _ = BoardGamePublisher.objects.get_or_create(
name=name
)
publisher, _ = BoardGamePublisher.objects.get_or_create(name=name)
game.publishers.add(publisher)
return game

View File

@ -11,9 +11,7 @@ User = get_user_model()
def import_chess_games_for_user_id(user_id: int, commit: bool = False) -> dict:
user = User.objects.get(id=user_id)
client = berserk.Client(
session=berserk.TokenSession(settings.LICHESS_API_KEY)
)
client = berserk.Client(session=berserk.TokenSession(settings.LICHESS_API_KEY))
games = client.games.export_by_player(user.profile.lichess_username)
for game_dict in games:
chess, created = BoardGame.objects.get_or_create(title="Chess")
@ -62,9 +60,7 @@ def import_chess_games_for_user_id(user_id: int, commit: bool = False) -> dict:
white_player.get("aiLevel", "")
)
else:
other_player["name_str"] = white_player.get("user", {}).get(
"name", ""
)
other_player["name_str"] = white_player.get("user", {}).get("name", "")
other_player["lichess_username"] = other_player["name_str"]
other_player["color"] = "white"
@ -82,9 +78,7 @@ def import_chess_games_for_user_id(user_id: int, commit: bool = False) -> dict:
black_player.get("aiLevel", "")
)
else:
other_player["name_str"] = black_player.get("user", {}).get(
"name", ""
)
other_player["name_str"] = black_player.get("user", {}).get("name", "")
other_player["lichess_username"] = other_player["name_str"]
other_player["color"] = "black"
if winner == "white":

View File

@ -37,9 +37,7 @@ class BoardGameListView(ScrobbleableListView):
for designer in scrobble.board_game.designers.all():
designers_this_week[designer.id] = {
"designer": designer,
"count": designers_this_week.get(designer.id, {}).get(
"count", 0
)
"count": designers_this_week.get(designer.id, {}).get("count", 0)
+ 1,
}
@ -48,9 +46,7 @@ class BoardGameListView(ScrobbleableListView):
for designer in scrobble.board_game.designers.all():
designers_this_month[designer.id] = {
"designer": designer,
"count": designers_this_month.get(designer.id, {}).get(
"count", 0
)
"count": designers_this_month.get(designer.id, {}).get("count", 0)
+ 1,
}

View File

@ -6,9 +6,7 @@ import logging
logger = logging.getLogger(__name__)
USER_AGENT = (
"Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0"
)
USER_AGENT = "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0"
AMAZON_SEARCH_URL = "https://www.amazon.com/s?k={amazon_id}"
@ -95,32 +93,20 @@ def get_amazon_product_dict(amazon_id: str) -> dict:
soup = BeautifulSoup(response.text, "html.parser")
try:
data_dict["title"] = soup.findAll("span", class_="celwidget")[
1
].text.strip()
data_dict["cover_url"] = soup.find("img", class_="frontImage").get(
"src"
)
data_dict["summary"] = soup.findAll(
"div", class_="a-expander-content"
)[1].text
data_dict["title"] = soup.findAll("span", class_="celwidget")[1].text.strip()
data_dict["cover_url"] = soup.find("img", class_="frontImage").get("src")
data_dict["summary"] = soup.findAll("div", class_="a-expander-content")[1].text
meta = soup.findAll("div", class_="rpi-attribute-value")
data_dict["isbn"] = meta[AmazonAttribute.ISBN_10.value].text.strip()
pages = meta[AmazonAttribute.PAGES.value].text
if "pages" in pages:
data_dict["pages"] = (
meta[AmazonAttribute.PAGES.value]
.text.split("pages")[0]
.strip()
meta[AmazonAttribute.PAGES.value].text.split("pages")[0].strip()
)
except IndexError as e:
logger.error(
f"Amazon lookup is failing for this product {amazon_id}: {e}"
)
logger.error(f"Amazon lookup is failing for this product {amazon_id}: {e}")
except AttributeError as e:
logger.error(
f"Amazon lookup is failing for this product {amazon_id}: {e}"
)
logger.error(f"Amazon lookup is failing for this product {amazon_id}: {e}")
return data_dict

View File

@ -146,9 +146,7 @@ def build_book_map(rows) -> dict:
)
continue
book = Book.objects.filter(
koreader_data_by_hash__icontains=book_row[
KoReaderBookColumn.MD5.value
]
koreader_data_by_hash__icontains=book_row[KoReaderBookColumn.MD5.value]
).first()
if not book:
@ -202,15 +200,11 @@ def build_page_data(page_rows: list, book_map: dict, user_tz=None) -> dict:
"end_ts": start_ts + duration,
}
if book_ids_not_found:
logger.info(
f"Found pages for books not in file: {set(book_ids_not_found)}"
)
logger.info(f"Found pages for books not in file: {set(book_ids_not_found)}")
return book_map
def build_scrobbles_from_book_map(
book_map: dict, user: "User"
) -> list["Scrobble"]:
def build_scrobbles_from_book_map(book_map: dict, user: "User") -> list["Scrobble"]:
Scrobble = apps.get_model("scrobbles", "Scrobble")
scrobbles_to_create = []
@ -240,9 +234,9 @@ def build_scrobbles_from_book_map(
seconds_from_last_page = 0
if prev_page_stats:
seconds_from_last_page = stats.get(
"end_ts"
) - prev_page_stats.get("start_ts")
seconds_from_last_page = stats.get("end_ts") - prev_page_stats.get(
"start_ts"
)
playback_position_seconds = playback_position_seconds + stats.get(
"duration"
@ -251,9 +245,7 @@ def build_scrobbles_from_book_map(
end_of_reading = pages_processed == total_pages_read
big_jump_to_this_page = (cur_page_number - last_page_number) > 10
is_session_gap = seconds_from_last_page > SESSION_GAP_SECONDS
if (
is_session_gap and not big_jump_to_this_page
) or end_of_reading:
if (is_session_gap and not big_jump_to_this_page) or end_of_reading:
should_create_scrobble = True
if should_create_scrobble:
@ -381,9 +373,7 @@ def process_koreader_sqlite_file(file_path, user_id) -> list:
return new_scrobbles
book_map = build_page_data(
cur.execute(
"SELECT * from page_stat_data ORDER BY id_book, start_time"
),
cur.execute("SELECT * from page_stat_data ORDER BY id_book, start_time"),
book_map,
tz,
)

View File

@ -13,9 +13,7 @@ HEADERS = {
}
LOCG_WRTIER_URL = ""
LOCG_WRITER_DETAIL_URL = "https://leagueofcomicgeeks.com/people/{slug}"
LOCG_SEARCH_URL = (
"https://leagueofcomicgeeks.com/search/ajax_issues?query={query}"
)
LOCG_SEARCH_URL = "https://leagueofcomicgeeks.com/search/ajax_issues?query={query}"
LOCG_DETAIL_URL = "https://leagueofcomicgeeks.com/comic/{locg_slug}"
@ -72,26 +70,19 @@ def lookup_comic_by_locg_slug(slug: str) -> dict:
attrs = soup.findAll("div", class_="details-addtl-block")
try:
data_dict["pages"] = (
attrs[1]
.find("div", class_="value")
.text.split("pages")[0]
.strip()
attrs[1].find("div", class_="value").text.split("pages")[0].strip()
)
except IndexError:
logger.warn(f"No ISBN field")
try:
data_dict["isbn"] = (
attrs[3].find("div", class_="value").text.strip()
)
data_dict["isbn"] = attrs[3].find("div", class_="value").text.strip()
except IndexError:
logger.warn(f"No ISBN field")
writer_slug = None
try:
writer_slug = (
soup.findAll("div", class_="name")[5]
.a.get("href")
.split("people/")[1]
soup.findAll("div", class_="name")[5].a.get("href").split("people/")[1]
)
except IndexError:
logger.warn(f"No wrtier found")

View File

@ -37,7 +37,5 @@ class Command(BaseCommand):
def handle(self, *args, **options):
scrobbles_to_create = []
for scrobble in Scrobble.objects.filter(
media_type="Book", source="KOReader"
):
for scrobble in Scrobble.objects.filter(media_type="Book", source="KOReader"):
update_scrobble_from_page_data(scrobble)

View File

@ -52,8 +52,7 @@ class Command(BaseCommand):
seconds_from_last_page = 0
if prev_page:
seconds_from_last_page = (
page.end_time.timestamp()
- prev_page.start_time.timestamp()
page.end_time.timestamp() - prev_page.start_time.timestamp()
)
playback_position_seconds = (
playback_position_seconds + page.duration_seconds
@ -62,9 +61,7 @@ class Command(BaseCommand):
end_of_reading = pages_processed == total_pages
big_jump_to_this_page = False
if prev_page:
big_jump_to_this_page = (
page.number - prev_page.number
) > 10
big_jump_to_this_page = (page.number - prev_page.number) > 10
if (
seconds_from_last_page > SESSION_GAP_SECONDS
and not big_jump_to_this_page
@ -113,9 +110,7 @@ class Command(BaseCommand):
user_id=user.id,
).first()
if scrobble:
logger.info(
f"Found existing scrobble {scrobble}, updating"
)
logger.info(f"Found existing scrobble {scrobble}, updating")
scrobble.book_page_data = scrobble_page_data
scrobble.playback_position_seconds = (
scrobble.calc_reading_duration()

View File

@ -133,9 +133,7 @@ class Author(TimeStampedModel):
class Book(LongPlayScrobblableMixin):
COMPLETION_PERCENT = getattr(settings, "BOOK_COMPLETION_PERCENT", 95)
AVG_PAGE_READING_SECONDS = getattr(
settings, "AVERAGE_PAGE_READING_SECONDS", 60
)
AVG_PAGE_READING_SECONDS = getattr(settings, "AVERAGE_PAGE_READING_SECONDS", 60)
title = models.CharField(max_length=255)
original_title = models.CharField(max_length=255, **BNULL)
@ -260,9 +258,7 @@ class Book(LongPlayScrobblableMixin):
# TODO use either a Google Books id identifier or author name like for tracks
book, created = cls.objects.get_or_create(original_title=title)
if not created:
logger.info(
"Found exact match for book by title", extra={"title": title}
)
logger.info("Found exact match for book by title", extra={"title": title})
if not enrich:
logger.info(
@ -296,9 +292,7 @@ class Book(LongPlayScrobblableMixin):
if authors:
for author_str in authors:
if author_str:
author, a_created = Author.objects.get_or_create(
name=author_str
)
author, a_created = Author.objects.get_or_create(name=author_str)
author_list.append(author)
if a_created:
# TODO enrich author
@ -332,13 +326,9 @@ class Book(LongPlayScrobblableMixin):
if not data:
logger.warn(f"Checking openlibrary for {self.title}")
if self.openlibrary_id and force_update:
data = lookup_book_from_openlibrary(
str(self.openlibrary_id)
)
data = lookup_book_from_openlibrary(str(self.openlibrary_id))
else:
data = lookup_book_from_openlibrary(
str(self.title), author_name
)
data = lookup_book_from_openlibrary(str(self.title), author_name)
if not data:
if self.locg_slug:
@ -385,10 +375,7 @@ class Book(LongPlayScrobblableMixin):
if "pages" in data.keys() and data.get("pages") == None:
data.pop("pages")
if (
not isinstance(data.get("pages"), int)
and "pages" in data.keys()
):
if not isinstance(data.get("pages"), int) and "pages" in data.keys():
logger.info(
f"Pages for {self} from OL expected to be int, but got {data.get('pages')}"
)
@ -420,9 +407,7 @@ class Book(LongPlayScrobblableMixin):
self.save()
def fix_authors_metadata(self, openlibrary_author_id):
author = Author.objects.filter(
openlibrary_id=openlibrary_author_id
).first()
author = Author.objects.filter(openlibrary_id=openlibrary_author_id).first()
if not author:
data = lookup_author_from_openlibrary(openlibrary_author_id)
author_image_url = data.pop("author_headshot_url", None)
@ -433,9 +418,7 @@ class Book(LongPlayScrobblableMixin):
r = requests.get(author_image_url)
if r.status_code == 200:
fname = f"{author.name}_{author.uuid}.jpg"
author.headshot.save(
fname, ContentFile(r.content), save=True
)
author.headshot.save(fname, ContentFile(r.content), save=True)
self.authors.add(author)
def get_author_from_locg(self, locg_slug):
@ -451,9 +434,7 @@ class Book(LongPlayScrobblableMixin):
author.headshot.save(fname, ContentFile(r.content), save=True)
self.authors.add(author)
def page_data_for_user(
self, user_id: int, convert_timestamps: bool = True
) -> dict:
def page_data_for_user(self, user_id: int, convert_timestamps: bool = True) -> dict:
scrobbles = self.scrobble_set.filter(user=user_id)
pages = {}
@ -461,9 +442,7 @@ class Book(LongPlayScrobblableMixin):
if scrobble.logdata.page_data:
for page, data in scrobble.logdata.page_data.items():
if convert_timestamps:
data["start_ts"] = datetime.fromtimestamp(
data["start_ts"]
)
data["start_ts"] = datetime.fromtimestamp(data["start_ts"])
data["end_ts"] = datetime.fromtimestamp(data["end_ts"])
pages[page] = data
sorted_pages = OrderedDict(
@ -502,9 +481,7 @@ class Paper(LongPlayScrobblableMixin):
"""Keeps track of Academic Papers"""
COMPLETION_PERCENT = getattr(settings, "PAPER_COMPLETION_PERCENT", 60)
AVG_PAGE_READING_SECONDS = getattr(
settings, "AVERAGE_PAGE_READING_SECONDS", 60
)
AVG_PAGE_READING_SECONDS = getattr(settings, "AVERAGE_PAGE_READING_SECONDS", 60)
title = models.CharField(max_length=255)
semantic_title = models.CharField(max_length=255, **BNULL)

View File

@ -10,7 +10,9 @@ from thefuzz import fuzz
logger = logging.getLogger(__name__)
ISBN_URL = "https://openlibrary.org/isbn/{isbn}.json"
SEARCH_URL = "https://openlibrary.org/search.json?q={query}&sort=editions&mode=everything"
SEARCH_URL = (
"https://openlibrary.org/search.json?q={query}&sort=editions&mode=everything"
)
AUTHOR_SEARCH_URL = "https://openlibrary.org/search/authors.json?q={query}"
COVER_URL = "https://covers.openlibrary.org/b/olid/{id}-L.jpg"
AUTHOR_URL = "https://openlibrary.org/authors/{id}.json"
@ -80,9 +82,7 @@ def lookup_author_from_openlibrary(olid: str) -> dict:
}
def lookup_book_from_openlibrary(
title: str, author: Optional[str] = None
) -> dict:
def lookup_book_from_openlibrary(title: str, author: Optional[str] = None) -> dict:
title_quoted = urllib.parse.quote(title)
author_quoted = ""
if author:

View File

@ -160,9 +160,7 @@ class ComicVineClient(object):
# or not the request cache is to be used, store the procedure in a
# local function to avoid repetition.
def __httpget():
response = requests.get(
self.API_URL, headers=self.HEADERS, params=params
)
response = requests.get(self.API_URL, headers=self.HEADERS, params=params)
if not response.ok:
self._handle_http_error(response)
@ -214,19 +212,13 @@ def lookup_comic_from_comicvine(title: str) -> dict:
api_key = getattr(settings, "COMICVINE_API_KEY", "")
if not api_key:
logger.warning(
"No ComicVine API key configured, not looking anything up"
)
logger.warning("No ComicVine API key configured, not looking anything up")
return {}
client = ComicVineClient(
api_key=getattr(settings, "COMICVINE_API_KEY", None)
)
client = ComicVineClient(api_key=getattr(settings, "COMICVINE_API_KEY", None))
raw_results = client.search(title).get("results")
results = [
r for r in raw_results if r.get("resource_type") == resource_type
]
results = [r for r in raw_results if r.get("resource_type") == resource_type]
if not results:
logger.warning("No comic found on ComicVine")
return {}

View File

@ -6,9 +6,7 @@ import requests
from django.conf import settings
API_KEY = settings.GOOGLE_API_KEY
GOOGLE_BOOKS_URL = (
'https://www.googleapis.com/books/v1/volumes?q="{title}"&key={key}'
)
GOOGLE_BOOKS_URL = 'https://www.googleapis.com/books/v1/volumes?q="{title}"&key={key}'
logger = logging.getLogger(__name__)
@ -21,14 +19,10 @@ def lookup_book_from_google(title: str) -> dict:
response = requests.get(url, headers=headers)
if response.status_code != 200:
logger.warning(
"Bad response from Google", extra={"response": response}
)
logger.warning("Bad response from Google", extra={"response": response})
return book_dict
google_result = (
json.loads(response.content).get("items", [{}])[0].get("volumeInfo")
)
google_result = json.loads(response.content).get("items", [{}])[0].get("volumeInfo")
if not google_result:
return {}
@ -69,8 +63,8 @@ def lookup_book_from_google(title: str) -> dict:
book_dict["base_run_time_seconds"] = 3600
if book_dict.get("pages"):
book_dict["base_run_time_seconds"] = book_dict.get(
"pages", 10
) * getattr(settings, "AVERAGE_PAGE_READING_SECONDS", 60)
book_dict["base_run_time_seconds"] = book_dict.get("pages", 10) * getattr(
settings, "AVERAGE_PAGE_READING_SECONDS", 60
)
return book_dict

View File

@ -18,9 +18,7 @@ def get_api_result(url):
response = requests.get(url, headers=headers)
if response.status_code != 200:
logger.warning(
"Bad response from Semantic", extra={"response": response}
)
logger.warning("Bad response from Semantic", extra={"response": response})
return None
return response
@ -64,12 +62,10 @@ def lookup_paper_from_semantic(title: str) -> dict:
paper_dict["tldr"] = result.get("bib", {}).get("abstract")
paper_dict["journal"] = result.get("journal", {}).get("name")
paper_dict["journal_volume"] = result.get("journal", {}).get("volume")
paper_dict["openaccess_pdf_url"] = result.get("openAccessPdf", {}).get(
"url"
paper_dict["openaccess_pdf_url"] = result.get("openAccessPdf", {}).get("url")
paper_dict["base_run_time_seconds"] = paper_dict.get("pages", 10) * getattr(
settings, "AVERAGE_PAGE_READING_SECONDS", 60
)
paper_dict["base_run_time_seconds"] = paper_dict.get(
"pages", 10
) * getattr(settings, "AVERAGE_PAGE_READING_SECONDS", 60)
paper_dict["author_dicts"] = result.get("authors")
paper_dict["genres"] = result.get("fieldsOfStudy")

View File

@ -99,7 +99,9 @@ class KoReaderBookRows:
]
)
if end_session:
start_time += 3600 # one second over an hour, marking a new reading session
start_time += (
3600 # one second over an hour, marking a new reading session
)
end_session = False
else:
start_time += AVERAGE_PAGE_READING_SECONDS

View File

@ -32,17 +32,13 @@ def test_load_page_data_to_map(get_mock, koreader_rows, valid_response):
)
assert (
len(book_map[1]["pages"])
== koreader_rows.BOOK_ROWS[0][
KoReaderBookColumn.TOTAL_READ_PAGES.value
]
== koreader_rows.BOOK_ROWS[0][KoReaderBookColumn.TOTAL_READ_PAGES.value]
)
@pytest.mark.django_db
@mock.patch("requests.get")
def test_build_scrobbles_from_pages(
get_mock, koreader_rows, demo_user, valid_response
):
def test_build_scrobbles_from_pages(get_mock, koreader_rows, demo_user, valid_response):
get_mock.return_value = valid_response
book_map = build_book_map(koreader_rows.BOOK_ROWS)
book_map = build_page_data(koreader_rows.PAGE_STATS_ROWS, book_map)

View File

@ -26,9 +26,7 @@ class BrickSet(LongPlayScrobblableMixin):
number = models.CharField(max_length=10, **BNULL)
release_year = models.IntegerField(**BNULL)
piece_count = models.IntegerField(**BNULL)
brickset_rating = models.DecimalField(
max_digits=3, decimal_places=1, **BNULL
)
brickset_rating = models.DecimalField(max_digits=3, decimal_places=1, **BNULL)
lego_item_number = models.CharField(max_length=10, **BNULL)
box_image = models.ImageField(upload_to="brickset/boxes/", **BNULL)
box_image_small = ImageSpecField(

View File

@ -7,14 +7,16 @@ class Command(BaseCommand):
def handle(self, *args, **options):
with connection.cursor() as cursor:
cursor.execute("""
cursor.execute(
"""
INSERT OR IGNORE INTO charts_chartrecord
(created, modified, user_id, rank, count, year, month, week, day,
video_id, series_id, artist_id, track_id, period_start, period_end)
SELECT created, modified, user_id, rank, count, year, month, week, day,
video_id, series_id, artist_id, track_id, period_start, period_end
FROM scrobbles_chartrecord
""")
"""
)
self.stdout.write(
self.style.SUCCESS("Copied ChartRecord data to charts app")
)

View File

@ -87,9 +87,7 @@ def get_ibu_from_soup(soup) -> Optional[int]:
def get_rating_from_soup(soup) -> str:
rating = ""
try:
rating = float(
soup.find(class_="num").get_text().strip("(").strip(")")
)
rating = float(soup.find(class_="num").get_text().strip("(").strip(")"))
except AttributeError:
rating = None
except ValueError:
@ -127,9 +125,7 @@ def get_food_from_allrecipe_id(allrecipe_id: str) -> dict:
food_dict = {"allrecipe_id": allrecipe_id}
if response.status_code != 200:
logger.warn(
"Bad response from allrecipe", extra={"response": response}
)
logger.warn("Bad response from allrecipe", extra={"response": response})
return food_dict
import pdb

View File

@ -63,17 +63,13 @@ class Food(ScrobblableMixin):
cook_time_minutes = models.IntegerField(**BNULL)
total_time_minutes = models.IntegerField(**BNULL)
servings = models.IntegerField(**BNULL)
yield_text = models.CharField(
max_length=255, **BNULL
) # e.g., "8 calzones"
yield_text = models.CharField(max_length=255, **BNULL) # e.g., "8 calzones"
# Nutrition (per serving)
calories = models.IntegerField(**BNULL)
protein = models.DecimalField(max_digits=10, decimal_places=2, **BNULL)
fat = models.DecimalField(max_digits=10, decimal_places=2, **BNULL)
carbohydrates = models.DecimalField(
max_digits=10, decimal_places=2, **BNULL
)
carbohydrates = models.DecimalField(max_digits=10, decimal_places=2, **BNULL)
fiber = models.DecimalField(max_digits=10, decimal_places=2, **BNULL)
sugar = models.DecimalField(max_digits=10, decimal_places=2, **BNULL)
sodium = models.DecimalField(max_digits=10, decimal_places=2, **BNULL)
@ -93,9 +89,7 @@ class Food(ScrobblableMixin):
)
allrecipe_id = models.CharField(max_length=255, **BNULL)
allrecipe_rating = models.FloatField(**BNULL)
category = models.ForeignKey(
FoodCategory, on_delete=models.DO_NOTHING, **BNULL
)
category = models.ForeignKey(FoodCategory, on_delete=models.DO_NOTHING, **BNULL)
class Meta:
indexes = [
@ -136,9 +130,7 @@ class Food(ScrobblableMixin):
return FoodLogData
@classmethod
def find_or_create_from_recipe(
cls, url: str, category=None
) -> Tuple["Food", bool]:
def find_or_create_from_recipe(cls, url: str, category=None) -> Tuple["Food", bool]:
"""
Scrape a recipe URL and create/update Food instance.
Uses django-taggit for tags and existing FoodCategory for category.
@ -164,16 +156,12 @@ class Food(ScrobblableMixin):
nutrition = recipe_data.get("nutrition")
if not nutrition:
calculator = NutritionCalculator()
servings = (
scraper.parse_servings(recipe_data.get("yields", "1")) or 1
)
servings = scraper.parse_servings(recipe_data.get("yields", "1")) or 1
nutrition = calculator.calculate_nutrition(
recipe_data.get("ingredients", []), servings
)
else:
servings = (
scraper.parse_servings(recipe_data.get("yields", "1")) or 1
)
servings = scraper.parse_servings(recipe_data.get("yields", "1")) or 1
# Get or create category (if not provided)
if not category and recipe_data.get("category"):
@ -198,9 +186,7 @@ class Food(ScrobblableMixin):
calories=int(nutrition.get("calories", 0)) if nutrition else None,
protein=nutrition.get("protein") if nutrition else None,
fat=nutrition.get("fat") if nutrition else None,
carbohydrates=(
nutrition.get("carbohydrates") if nutrition else None
),
carbohydrates=(nutrition.get("carbohydrates") if nutrition else None),
fiber=nutrition.get("fiber") if nutrition else None,
category=category,
)
@ -246,9 +232,7 @@ class Food(ScrobblableMixin):
calories=int(nutrition.get("calories", 0)) if nutrition else None,
protein=nutrition.get("protein") if nutrition else None,
fat=nutrition.get("fat") if nutrition else None,
carbohydrates=(
nutrition.get("carbohydrates") if nutrition else None
),
carbohydrates=(nutrition.get("carbohydrates") if nutrition else None),
fiber=nutrition.get("fiber") if nutrition else None,
category=category,
)
@ -263,9 +247,7 @@ class Food(ScrobblableMixin):
try:
ingredients = json.loads(self.ingredients)
calculator = NutritionCalculator()
nutrition = calculator.calculate_nutrition(
ingredients, self.servings or 1
)
nutrition = calculator.calculate_nutrition(ingredients, self.servings or 1)
self.calories = int(nutrition.get("calories", 0))
self.protein = nutrition.get("protein")

View File

@ -87,9 +87,7 @@ class RecipeScraperService:
response = requests.get(
url,
timeout=10,
headers={
"User-Agent": "Mozilla/5.0 (compatible; Vrobbler/0.3)"
},
headers={"User-Agent": "Mozilla/5.0 (compatible; Vrobbler/0.3)"},
)
if response.status_code != 200:
logger.debug("Recipe website returned non 200 response")
@ -181,9 +179,7 @@ class RecipeScraperService:
keywords = recipe_data.get("keywords")
if keywords:
if isinstance(keywords, str):
tags.update(
[k.strip() for k in keywords.split(",") if k.strip()]
)
tags.update([k.strip() for k in keywords.split(",") if k.strip()])
elif isinstance(keywords, list):
tags.update([k.strip() for k in keywords if k.strip()])

View File

@ -110,22 +110,14 @@ class USDAFoodAPI:
if "nutrientNumber" in nutrient:
nutrient_id = str(nutrient.get("nutrientNumber", ""))
value = nutrient.get("value")
elif "nutrient" in nutrient and isinstance(
nutrient["nutrient"], dict
):
nutrient_id = str(
nutrient.get("nutrient", {}).get("id", "")
)
elif "nutrient" in nutrient and isinstance(nutrient["nutrient"], dict):
nutrient_id = str(nutrient.get("nutrient", {}).get("id", ""))
value = nutrient.get("value")
elif "number" in nutrient:
nutrient_id = str(nutrient.get("number", ""))
value = nutrient.get("value")
if (
nutrient_id
and nutrient_id in nutrient_map
and value is not None
):
if nutrient_id and nutrient_id in nutrient_map and value is not None:
key = nutrient_map[nutrient_id]
try:
nutrients[key] = float(value)
@ -404,11 +396,7 @@ class NutritionCalculator:
# Strategy 1: Direct search
ingredient_name,
# Strategy 2: Singular form (remove trailing 's')
(
ingredient_name.rstrip("s")
if ingredient_name.endswith("s")
else None
),
(ingredient_name.rstrip("s") if ingredient_name.endswith("s") else None),
# Strategy 3: Remove common suffixes
re.sub(
r"\s+(fresh|dried|ground|chopped|sliced)$",
@ -417,11 +405,7 @@ class NutritionCalculator:
flags=re.IGNORECASE,
),
# Strategy 4: Just the last word (sometimes works for simple ingredients)
(
ingredient_name.split()[-1]
if len(ingredient_name.split()) > 1
else None
),
(ingredient_name.split()[-1] if len(ingredient_name.split()) > 1 else None),
]
for query in strategies:
@ -431,9 +415,7 @@ class NutritionCalculator:
try:
results = self.usda.search_foods(query, page_size=5)
if results:
logger.debug(
f"Found {query}: {results[0].get('description')}"
)
logger.debug(f"Found {query}: {results[0].get('description')}")
return results[0]
except Exception as e:
logger.warning(f"USDA search failed for '{query}': {e}")
@ -471,9 +453,7 @@ class NutritionCalculator:
}
# Get base weight in grams
base_grams = volume_to_grams.get(
unit, 100
) # Default to 100g if unknown
base_grams = volume_to_grams.get(unit, 100) # Default to 100g if unknown
# Adjust for ingredient type (very simplified)
if any(
@ -481,22 +461,16 @@ class NutritionCalculator:
for word in ["flour", "sugar", "rice", "grain"]
):
base_grams = volume_to_grams.get(unit, 125) # Dry ingredients
elif any(
word in ingredient_name.lower()
for word in ["oil", "butter", "fat"]
):
elif any(word in ingredient_name.lower() for word in ["oil", "butter", "fat"]):
base_grams = volume_to_grams.get(unit, 220) # Dense liquids
elif any(
word in ingredient_name.lower()
for word in ["milk", "water", "broth"]
word in ingredient_name.lower() for word in ["milk", "water", "broth"]
):
base_grams = volume_to_grams.get(unit, 240) # Liquids
return quantity * base_grams
def calculate_nutrition(
self, ingredients: List[str], servings: int = 1
) -> Dict:
def calculate_nutrition(self, ingredients: List[str], servings: int = 1) -> Dict:
"""
Calculate total nutrition for a recipe from ingredient list.
Returns nutrition per serving.
@ -543,12 +517,8 @@ class NutritionCalculator:
# USDA data is typically per 100g, so scale accordingly
multiplier = grams / 100
totals["calories"] += (
nutrients.get("calories", 0) * multiplier
)
totals["protein"] += (
nutrients.get("protein", 0) * multiplier
)
totals["calories"] += nutrients.get("calories", 0) * multiplier
totals["protein"] += nutrients.get("protein", 0) * multiplier
totals["fat"] += nutrients.get("fat", 0) * multiplier
totals["carbohydrates"] += (
nutrients.get("carbohydrates", 0) * multiplier

View File

@ -20,9 +20,7 @@ class LifeEvent(ScrobblableMixin):
return self.title
def get_absolute_url(self):
return reverse(
"life-events:life-event_detail", kwargs={"slug": self.uuid}
)
return reverse("life-events:life-event_detail", kwargs={"slug": self.uuid})
@property
def logdata_cls(self):
@ -38,6 +36,6 @@ class LifeEvent(ScrobblableMixin):
def scrobbles(self, user_id):
Scrobble = apps.get_model("scrobbles", "Scrobble")
return Scrobble.objects.filter(
user_id=user_id, life_event=self
).order_by("-timestamp")
return Scrobble.objects.filter(user_id=user_id, life_event=self).order_by(
"-timestamp"
)

View File

@ -5,9 +5,7 @@ app_name = "lifeevents"
urlpatterns = [
path(
"lifeevents/", views.LifeEventListView.as_view(), name="lifeevent_list"
),
path("lifeevents/", views.LifeEventListView.as_view(), name="lifeevent_list"),
path(
"lifeevent/<slug:slug>/",
views.LifeEventDetailView.as_view(),

View File

@ -42,9 +42,7 @@ class GeoLocation(ScrobblableMixin):
return f"{self.lat} x {self.lon}"
def get_absolute_url(self):
return reverse(
"locations:geolocation_detail", kwargs={"slug": self.uuid}
)
return reverse("locations:geolocation_detail", kwargs={"slug": self.uuid})
@property
def logdata_cls(self):
@ -101,9 +99,7 @@ class GeoLocation(ScrobblableMixin):
@property
def strings(self) -> ScrobblableConstants:
return ScrobblableConstants(
verb="Going", tags="world_map", priority="low"
)
return ScrobblableConstants(verb="Going", tags="world_map", priority="low")
def loc_diff(self, old_lat_lon: tuple) -> tuple:
return (
@ -114,9 +110,7 @@ class GeoLocation(ScrobblableMixin):
def has_moved(self, previous_location: "GeoLocation") -> bool:
has_moved = False
loc_diff = self.loc_diff(
(previous_location.lat, previous_location.lon)
)
loc_diff = self.loc_diff((previous_location.lat, previous_location.lon))
if loc_diff[0] > GEOLOC_PROXIMITY or loc_diff[1] > GEOLOC_PROXIMITY:
has_moved = True
logger.debug(

View File

@ -13,9 +13,7 @@ def test_find_or_create(caplog):
@pytest.mark.django_db
def test_find_or_create_truncation():
loc = GeoLocation.find_or_create(
{"lat": 44.2345, "lon": -68.2345, "alt": 60.356}
)
loc = GeoLocation.find_or_create({"lat": 44.2345, "lon": -68.2345, "alt": 60.356})
assert loc.lat == 44.234
assert loc.lon == -68.234
assert loc.altitude == 60
@ -25,9 +23,7 @@ def test_find_or_create_truncation():
def test_find_or_create_finds_existing():
extant = GeoLocation.objects.create(lat=44.234, lon=-68.234, altitude=50)
loc = GeoLocation.find_or_create(
{"lat": 44.2345, "lon": -68.2345, "alt": 60.356}
)
loc = GeoLocation.find_or_create({"lat": 44.2345, "lon": -68.2345, "alt": 60.356})
assert loc.id == extant.id
@ -35,9 +31,7 @@ def test_find_or_create_finds_existing():
def test_find_or_create_creates_new():
extant = GeoLocation.objects.create(lat=44.234, lon=-69.234, altitude=60)
loc = GeoLocation.find_or_create(
{"lat": 44.2345, "lon": -68.2345, "alt": 60.356}
)
loc = GeoLocation.find_or_create({"lat": 44.2345, "lon": -68.2345, "alt": 60.356})
assert not loc.id == extant.id
@ -47,9 +41,7 @@ def test_found_in_proximity_location():
lon = -69.234
loc = GeoLocation.objects.create(lat=lat, lon=lon, altitude=60)
close = GeoLocation.objects.create(
lat=lat + 0.0001, lon=lon - 0.0001, altitude=60
)
close = GeoLocation.objects.create(lat=lat + 0.0001, lon=lon - 0.0001, altitude=60)
assert close not in loc.in_proximity(named=True)
assert close in loc.in_proximity()
@ -60,9 +52,7 @@ def test_not_found_in_proximity_location():
lon = -69.234
loc = GeoLocation.objects.create(lat=lat, lon=lon, altitude=60)
far = GeoLocation.objects.create(
lat=lat + 0.0002, lon=lon - 0.0001, altitude=60
)
far = GeoLocation.objects.create(lat=lat + 0.0002, lon=lon - 0.0001, altitude=60)
assert far not in loc.in_proximity()

View File

@ -109,9 +109,9 @@ class Artist(TimeStampedModel):
def scrobbles(self):
from scrobbles.models import Scrobble
return Scrobble.objects.filter(
track__in=self.track_set.all()
).order_by("-timestamp")
return Scrobble.objects.filter(track__in=self.track_set.all()).order_by(
"-timestamp"
)
@property
def tracks(self):
@ -130,9 +130,7 @@ class Artist(TimeStampedModel):
if not self.allmusic_id or force:
slug = get_allmusic_slug(self.name)
if not slug:
logger.info(
"No allmusic link found", extra={"track_id": self.id}
)
logger.info("No allmusic link found", extra={"track_id": self.id})
return
self.allmusic_id = slug
self.save(update_fields=["allmusic_id"])
@ -141,9 +139,7 @@ class Artist(TimeStampedModel):
if not self.bandcamp_id or force:
slug = get_bandcamp_slug(self.name)
if not slug:
logger.info(
"No bandcamp link found", extra={"track_id": self.id}
)
logger.info("No bandcamp link found", extra={"track_id": self.id})
return
self.bandcamp_id = slug
self.save(update_fields=["bandcamp_id"])
@ -193,13 +189,9 @@ class Artist(TimeStampedModel):
Thus, when we find or create an artist, we should always provide an optional
album name or track name, but probably not both."""
if album_name:
logger.info(
f"Looking for artist with name {name} and album {album_name}"
)
logger.info(f"Looking for artist with name {name} and album {album_name}")
if track_name:
logger.info(
f"Looking for artist with name {name} and track {track_name}"
)
logger.info(f"Looking for artist with name {name} and track {track_name}")
keys = {}
name = clean_artist_name(name)
@ -310,9 +302,9 @@ class Album(TimeStampedModel):
def scrobbles(self):
from scrobbles.models import Scrobble
return Scrobble.objects.filter(
track__in=self.track_set.all()
).order_by("-timestamp")
return Scrobble.objects.filter(track__in=self.track_set.all()).order_by(
"-timestamp"
)
@property
def primary_image_url(self) -> str:
@ -349,9 +341,7 @@ class Album(TimeStampedModel):
if self.album_artist and (not self.allmusic_id or force):
slug = get_allmusic_slug(self.album_artist.name, self.name)
if not slug:
logger.info(
f"No allmsuic link for {self} by {self.album_artist}"
)
logger.info(f"No allmsuic link for {self} by {self.album_artist}")
return
self.allmusic_id = slug
self.save(update_fields=["allmusic_id"])
@ -380,9 +370,7 @@ class Album(TimeStampedModel):
try:
Album.objects.filter(pk=self.pk).update(**album_data)
except:
logger.info(
f"Could not save info for album {self} with data {album_data}"
)
logger.info(f"Could not save info for album {self} with data {album_data}")
def scrape_bandcamp(self, force=False) -> None:
if not self.bandcamp_id or force:
@ -404,13 +392,13 @@ class Album(TimeStampedModel):
self.musicbrainz_id, includes=["artists", "release-groups"]
)
if not self.musicbrainz_releasegroup_id:
self.musicbrainz_releasegroup_id = mb_data["release"][
"release-group"
]["id"]
self.musicbrainz_releasegroup_id = mb_data["release"]["release-group"][
"id"
]
if not self.musicbrainz_albumartist_id:
self.musicbrainz_albumartist_id = mb_data["release"][
"artist-credit"
][0]["artist"]["id"]
self.musicbrainz_albumartist_id = mb_data["release"]["artist-credit"][
0
]["artist"]["id"]
if not self.year:
try:
self.year = mb_data["release"]["date"][0:4]
@ -435,10 +423,7 @@ class Album(TimeStampedModel):
if not new_artist:
for t in self.track_set.all():
self.artists.add(t.artist)
if (
not self.cover_image
or self.cover_image == "default-image-replace-me"
):
if not self.cover_image or self.cover_image == "default-image-replace-me":
self.fetch_artwork()
self.fix_album_artist()
self.scrape_theaudiodb()
@ -448,20 +433,15 @@ class Album(TimeStampedModel):
if not self.cover_image and not force:
if self.musicbrainz_id:
try:
img_data = musicbrainzngs.get_image_front(
self.musicbrainz_id
)
img_data = musicbrainzngs.get_image_front(self.musicbrainz_id)
name = f"{self.name}_{self.uuid}.jpg"
self.cover_image = ContentFile(img_data, name=name)
logger.info(f"Setting image to {name}")
except musicbrainzngs.ResponseError:
logger.warning(
f"No cover art found for {self.name} by release"
)
logger.warning(f"No cover art found for {self.name} by release")
if (
not self.cover_image
or self.cover_image == "default-image-replace-me"
not self.cover_image or self.cover_image == "default-image-replace-me"
) and self.musicbrainz_releasegroup_id:
try:
img_data = musicbrainzngs.get_release_group_image_front(
@ -522,9 +502,7 @@ class Album(TimeStampedModel):
@classmethod
def find_or_create(cls, name: str, artist_name: str) -> "Album":
logger.info(
f"Looking for album with name {name} and artist_name {artist_name}"
)
logger.info(f"Looking for album with name {name} and artist_name {artist_name}")
artist = Artist.find_or_create(artist_name, album_name=name)
album_dict = get_album_metadata_with_artist(name, artist.name)
@ -572,9 +550,7 @@ class Album(TimeStampedModel):
if found_name and name != found_name:
alt_name = name
album = Album.objects.filter(
musicbrainz_id=album_dict.get("mbid")
).first()
album = Album.objects.filter(musicbrainz_id=album_dict.get("mbid")).first()
if not album:
year = None
@ -583,9 +559,7 @@ class Album(TimeStampedModel):
album = Album.objects.create(
name=found_name,
musicbrainz_id=album_dict.get("mbid"),
musicbrainz_releasegroup_id=album_dict.get(
"release_group_mbid"
),
musicbrainz_releasegroup_id=album_dict.get("release_group_mbid"),
year=year,
album_artist=artist,
alt_names=alt_name,
@ -666,9 +640,7 @@ class Track(ScrobblableMixin):
album = None
if album_name:
logger.info("Looking up album for: {album_name}")
album = Album.find_or_create(
name=album_name, artist_name=artist_name
)
album = Album.find_or_create(name=album_name, artist_name=artist_name)
artist = album.album_artist
else:
artist = Artist.find_or_create(artist_name, track_name=title)
@ -709,15 +681,11 @@ class Track(ScrobblableMixin):
},
)
try:
mbid, length = get_recording_mbid_exact(
title, artist_name, album_name
)
mbid, length = get_recording_mbid_exact(title, artist_name, album_name)
except Exception:
print("No musicbrainz result found, cannot enrich")
return track
track.base_run_time_seconds = run_time_seconds or int(
length / 1000
)
track.base_run_time_seconds = run_time_seconds or int(length / 1000)
track.musicbrainz_id = mbid
if commit:
track.save()

View File

@ -29,9 +29,7 @@ def lookup_album_from_mb(musicbrainz_id: str) -> dict:
"album": {
"name": release_data.get("title"),
"musicbrainz_id": musicbrainz_id,
"musicbrainz_releasegroup_id": release_data.get(
"release-group"
).get("id"),
"musicbrainz_releasegroup_id": release_data.get("release-group").get("id"),
"musicbrainz_albumaritist_id": primary_artist.get("id"),
"year": release_data.get("year")[0:4],
},
@ -56,13 +54,11 @@ def lookup_album_dict_from_mb(release_name: str, artist_name: str) -> dict:
top_result = {}
try:
top_result = musicbrainzngs.search_releases(
release_name, artist=artist_name
)["release-list"][0]
top_result = musicbrainzngs.search_releases(release_name, artist=artist_name)[
"release-list"
][0]
except IndexError:
logger.info(
f"No release found on MB for {artist_name} and {release_name}"
)
logger.info(f"No release found on MB for {artist_name} and {release_name}")
score = int(top_result.get("ext:score", 0))
if score < 85:
@ -86,9 +82,7 @@ def lookup_album_dict_from_mb(release_name: str, artist_name: str) -> dict:
def lookup_artist_from_mb(artist_name: str) -> dict:
try:
top_result = musicbrainzngs.search_artists(artist=artist_name)[
"artist-list"
][0]
top_result = musicbrainzngs.search_artists(artist=artist_name)["artist-list"][0]
except IndexError:
return {}
score = int(top_result.get("ext:score"))
@ -204,9 +198,7 @@ def get_recording_mbid_exact(
for track in tracks:
if track["recording"]["title"].lower() == track_title.lower():
return track["recording"]["id"], int(
track["recording"]["length"]
)
return track["recording"]["id"], int(track["recording"]["length"])
raise Exception("No recording found")
except musicbrainzngs.WebServiceError as e:
@ -224,9 +216,7 @@ def get_artist_metadata_extended(artist_name, strict=True):
"""
try:
# Step 1: Search for artist
search_results = musicbrainzngs.search_artists(
artist=artist_name, limit=5
)
search_results = musicbrainzngs.search_artists(artist=artist_name, limit=5)
for artist in search_results.get("artist-list", []):
if not strict or artist["name"].lower() == artist_name.lower():
mbid = artist["id"]
@ -354,9 +344,7 @@ def get_album_metadata_with_artist(album_name, artist_name, strict=True):
primary_artist = release["artist-credit"][0]["artist"]
all_artists = [
ac["artist"]["name"]
for ac in release["artist-credit"]
if "artist" in ac
ac["artist"]["name"] for ac in release["artist-credit"] if "artist" in ac
]
artist_metadata = get_artist_metadata_brief(primary_artist["id"])
@ -446,9 +434,7 @@ def get_track_metadata_with_artist(track_title, artist_name, strict=True):
for release in recording["release-list"]:
release_date = parse_date(release.get("date"))
if release_date:
valid_candidates.append(
(recording["id"], release, release_date)
)
valid_candidates.append((recording["id"], release, release_date))
if not valid_candidates:
return None

View File

@ -6,9 +6,15 @@ import requests
from django.conf import settings
THEAUDIODB_API_KEY = getattr(settings, "THEAUDIODB_API_KEY")
ARTIST_SEARCH_URL = f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/search.php?s="
ARTIST_FETCH_URL = f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/artist.php?i="
ALBUM_SEARCH_URL = f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/searchalbum.php?s="
ARTIST_SEARCH_URL = (
f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/search.php?s="
)
ARTIST_FETCH_URL = (
f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/artist.php?i="
)
ALBUM_SEARCH_URL = (
f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/searchalbum.php?s="
)
logger = logging.getLogger(__name__)
@ -94,14 +100,10 @@ def lookup_album_from_tadb(name: str, artist: str) -> dict:
album_info["rateyourmusic_id"] = album.get("strRateYourMusicID")
if album.get("intYearReleased"):
album_info["theaudiodb_year_released"] = float(
album.get("intYearReleased")
)
album_info["theaudiodb_year_released"] = float(album.get("intYearReleased"))
if album.get("intScore"):
album_info["theaudiodb_score"] = float(album.get("intScore"))
if album.get("intScoreVotes"):
album_info["theaudiodb_score_votes"] = int(
album.get("intScoreVotes")
)
album_info["theaudiodb_score_votes"] = int(album.get("intScoreVotes"))
return album_info

View File

@ -88,9 +88,7 @@ def condense_albums(commit: bool = False):
track.albums.add(dup_track.album)
# Find out if this track appears more than once
duplicates = Track.objects.filter(
title=track.title, artist=track.artist
)
duplicates = Track.objects.filter(title=track.title, artist=track.artist)
if duplicates.count() > 1:
logger.info(f"Track appears more than once, condensing: {track}")
@ -98,9 +96,7 @@ def condense_albums(commit: bool = False):
# Find all scrobbles
duplicate_ids = duplicates.values_list("id", flat=True)
scrobbles = Scrobble.objects.filter(track_id__in=duplicate_ids)
logger.info(
f"Found {scrobbles.count()} scrobbles to merge onto {track}"
)
logger.info(f"Found {scrobbles.count()} scrobbles to merge onto {track}")
if commit:
scrobbles.update(track=track)
track.albums.add(*list(set(albums_to_add)))

View File

@ -5,9 +5,7 @@ app_name = "people"
urlpatterns = [
path(
"people/", views.PersonCreateUpdateView.as_view(), name="person_form"
),
path("people/", views.PersonCreateUpdateView.as_view(), name="person_form"),
path(
"people/<int:pk>/edit/",
views.PersonUpdateView.as_view(),

View File

@ -39,9 +39,7 @@ class Producer(TimeStampedModel):
class Podcast(TimeStampedModel):
name = models.CharField(max_length=255)
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
producer = models.ForeignKey(
Producer, on_delete=models.DO_NOTHING, **BNULL
)
producer = models.ForeignKey(Producer, on_delete=models.DO_NOTHING, **BNULL)
podcastindex_id = models.CharField(max_length=100, **BNULL)
owner = models.CharField(max_length=150, *BNULL)
description = models.TextField(**BNULL)
@ -123,9 +121,7 @@ class PodcastEpisode(ScrobblableMixin):
mopidy_uri = models.CharField(max_length=255, **BNULL)
def get_absolute_url(self):
return reverse(
"podcasts:podcast_detail", kwargs={"slug": self.podcast.uuid}
)
return reverse("podcasts:podcast_detail", kwargs={"slug": self.podcast.uuid})
def __str__(self):
return f"{self.title}"

View File

@ -14,9 +14,7 @@ PODCASTINDEX_API_SECRET = getattr(settings, "PODCASTINDEX_API_SECRET")
def get_auth_headers():
now = int(time.time())
hash_data = hashlib.sha1(
(PODCASTINDEX_API_KEY + PODCASTINDEX_API_SECRET + str(now)).encode(
"utf-8"
)
(PODCASTINDEX_API_KEY + PODCASTINDEX_API_SECRET + str(now)).encode("utf-8")
).hexdigest()
return {

View File

@ -53,9 +53,7 @@ def fetch_metadata_from_rss(uri: str) -> dict[str, Any]:
if isinstance(feed.feed.itunes_owner, dict)
else feed.feed.itunes_owner
)
podcast_other = feed.feed.get("managingeditor") or feed.feed.get(
"copyright"
)
podcast_other = feed.feed.get("managingeditor") or feed.feed.get("copyright")
except AttributeError:
podcast_owner = None
podcast_other = None
@ -64,9 +62,7 @@ def fetch_metadata_from_rss(uri: str) -> dict[str, Any]:
"podcast_name": getattr(feed.feed, "title", ""),
# "podcast_description": getattr(feed.feed, "description", ""),
# "podcast_link": getattr(feed.feed, "link", ""),
"podcast_producer": podcast_publisher
or podcast_owner
or podcast_other,
"podcast_producer": podcast_publisher or podcast_owner or podcast_other,
}
for entry in feed.entries:
@ -116,8 +112,7 @@ def parse_mopidy_uri(uri: str) -> dict[str, Any]:
# Beacuse we have epsiode numbers on
podcast_data["pub_date"] = parse(
episode_str[
episode_num_pad : len(PODCAST_DATE_FORMAT)
+ episode_num_pad
episode_num_pad : len(PODCAST_DATE_FORMAT) + episode_num_pad
]
)
except ParserError:
@ -129,9 +124,7 @@ def parse_mopidy_uri(uri: str) -> dict[str, Any]:
if podcast_data["episode_num"]:
gap_to_strip += episode_num_pad
podcast_data["title"] = (
episode_str[gap_to_strip:].replace("-", " ").strip()
)
podcast_data["title"] = episode_str[gap_to_strip:].replace("-", " ").strip()
return podcast_data

View File

@ -3,9 +3,7 @@ from datetime import datetime
import pytz
ALL_TIMEZONE_CHOICES = tuple(zip(pytz.all_timezones, pytz.all_timezones))
COMMON_TIMEZONE_CHOICES = tuple(
zip(pytz.common_timezones, pytz.common_timezones)
)
COMMON_TIMEZONE_CHOICES = tuple(zip(pytz.common_timezones, pytz.common_timezones))
PRETTY_TIMEZONE_CHOICES = []
for tz in pytz.common_timezones:

View File

@ -5,7 +5,5 @@ app_name = "profiles"
urlpatterns = [
path(
"settings/", views.ProfileFormView.as_view(), name="profile_settings"
),
path("settings/", views.ProfileFormView.as_view(), name="profile_settings"),
]

View File

@ -82,15 +82,9 @@ def fix_profile_historic_timezones(profile):
profile.timezone_change_log = ""
profile.timezone_change_log += f"{europe_tz} - {pendulum.parse(europe)}\n"
profile.timezone_change_log += (
f"{home_tz} - {pendulum.parse(europe_end)}\n"
)
profile.timezone_change_log += (
f"{washington_tz} - {pendulum.parse(washington)}\n"
)
profile.timezone_change_log += (
f"{home_tz} - {pendulum.parse(washington_end)}\n"
)
profile.timezone_change_log += f"{home_tz} - {pendulum.parse(europe_end)}\n"
profile.timezone_change_log += f"{washington_tz} - {pendulum.parse(washington)}\n"
profile.timezone_change_log += f"{home_tz} - {pendulum.parse(washington_end)}\n"
profile.timezone_change_log += f"{camp_tz} - {pendulum.parse(camp)}\n"
profile.timezone_change_log += f"{home_tz} - {pendulum.parse(camp_end)}\n"
profile.timezone_change_log += f"{summer_tz} - {pendulum.parse(summer)}\n"

View File

@ -110,9 +110,7 @@ class Puzzle(ScrobblableMixin):
(
manufacturer,
_created,
) = PuzzleManufacturer.objects.get_or_create(
name=manufacturer_name
)
) = PuzzleManufacturer.objects.get_or_create(name=manufacturer_name)
puzzle_dict["manufacturer_id"] = manufacturer.id
genres = puzzle_dict.pop("genres", None)
@ -126,9 +124,7 @@ class Puzzle(ScrobblableMixin):
r = requests.get(cover_url)
if r.status_code == 200:
fname = f"{puzzle.title}_{puzzle.uuid}.jpg"
puzzle.ipdb_image.save(
fname, ContentFile(r.content), save=True
)
puzzle.ipdb_image.save(fname, ContentFile(r.content), save=True)
return puzzle

View File

@ -30,9 +30,7 @@ def get_title_from_soup(soup) -> str:
def get_manufacturer_from_soup(soup) -> str:
manufacturer = ""
try:
manufacturer = (
soup.find(class_="infobox").div.contents[0].split("|")[0].strip()
)
manufacturer = soup.find(class_="infobox").div.contents[0].split("|")[0].strip()
except AttributeError:
pass
except ValueError:
@ -46,10 +44,7 @@ def get_pieces_count_from_soup(soup) -> int:
pieces = 0
try:
pieces = int(
soup.find(class_="infobox")
.div.contents[0]
.split("|")[1]
.split(" ")[1]
soup.find(class_="infobox").div.contents[0].split("|")[1].split(" ")[1]
)
except AttributeError:
pass
@ -63,9 +58,7 @@ def get_pieces_count_from_soup(soup) -> int:
def get_publish_year_from_soup(soup) -> int:
year = 1900
try:
year = int(
soup.find(class_="infobox").div.contents[0].split("|")[2].strip()
)
year = int(soup.find(class_="infobox").div.contents[0].split("|")[2].strip())
except AttributeError:
pass
except ValueError:
@ -152,9 +145,7 @@ def get_barcode_from_soup(soup) -> str:
def get_image_url_from_soup(soup) -> str:
url = ""
try:
url = (
soup.find(class_="image-container").contents[0].contents[0]["src"]
)
url = soup.find(class_="image-container").contents[0].contents[0]["src"]
except AttributeError:
pass
except ValueError:
@ -210,9 +201,7 @@ def get_dimensions_from_soup(soup) -> str:
def get_rating_from_soup(soup) -> str:
rating = ""
try:
rating = float(
soup.find(class_="num").get_text().strip("(").strip(")")
)
rating = float(soup.find(class_="num").get_text().strip("(").strip(")"))
except AttributeError:
rating = None
except ValueError:
@ -227,9 +216,7 @@ def get_puzzle_from_ipdb_id(ipdb_id: str) -> dict:
puzzle_dict = {"ipdb_id": ipdb_id}
if response.status_code != 200:
logger.warn(
"Bad response from untappd.com", extra={"response": response}
)
logger.warn("Bad response from untappd.com", extra={"response": response})
return puzzle_dict
soup = BeautifulSoup(response.text, "html.parser")

View File

@ -70,7 +70,10 @@ class KoReaderImportAdmin(ImportBaseAdmin):
@admin.register(RetroarchImport)
class RetroarchImportAdmin(ImportBaseAdmin):
...
class RetroarchImportAdmin(ImportBaseAdmin): ...
class RetroarchImportAdmin(ImportBaseAdmin):
...
@admin.register(Genre)

View File

@ -63,9 +63,7 @@ class KoReaderImportSerializer(serializers.HyperlinkedModelSerializer):
fields = "__all__"
class AudioScrobblerTSVImportSerializer(
serializers.HyperlinkedModelSerializer
):
class AudioScrobblerTSVImportSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = AudioScrobblerTSVImport
fields = "__all__"

View File

@ -13,9 +13,7 @@ def export_scrobbles(start_date=None, end_date=None, format="AS"):
if start_date:
end_query = Q(timestamp__lte=end_date)
scrobble_qs = Scrobble.objects.filter(
start_query, end_query, track__isnull=False
)
scrobble_qs = Scrobble.objects.filter(start_query, end_query, track__isnull=False)
headers = []
extension = "tsv"
delimiter = "\t"

View File

@ -56,9 +56,7 @@ def django_form_field_from_type(field_type, required=True):
if type(None) in args:
required = False
non_none_type = [arg for arg in args if arg is not type(None)][0]
return django_form_field_from_type(
non_none_type, required=required
)
return django_form_field_from_type(non_none_type, required=required)
# Determine actual type
base_type = origin if origin else field_type
@ -84,9 +82,7 @@ def form_from_dataclass(dataclass):
continue
required = f.default is None and f.default_factory is None
form_fields[f.name] = django_form_field_from_type(
f.type, required=required
)
form_fields[f.name] = django_form_field_from_type(f.type, required=required)
if f.name in dataclass._excluded_fields:
form_fields[f.name].disabled = True
@ -101,9 +97,7 @@ def form_from_dataclass(dataclass):
def clean_notes(self):
notes_str = self.cleaned_data.get("notes", "")
return [
line.strip() for line in notes_str.splitlines() if line.strip()
]
return [line.strip() for line in notes_str.splitlines() if line.strip()]
form_cls.clean_notes = clean_notes
return form_cls

View File

@ -43,9 +43,7 @@ def import_scrobbles_from_imap() -> list[Scrobble]:
try:
message = email.message_from_bytes(msg_data[0][1])
logger.info(
"Processing email message", extra={"email_msg": message}
)
logger.info("Processing email message", extra={"email_msg": message})
except IndexError:
logger.info("No email message data found")
return
@ -75,9 +73,7 @@ def import_scrobbles_from_imap() -> list[Scrobble]:
if filename.lower().endswith(".bgsplay"):
# TODO Pull this out into a parse_pgsplay function
try:
parsed_json = json.loads(
file_data.decode("utf-8")
)
parsed_json = json.loads(file_data.decode("utf-8"))
except Exception as e:
logger.error(
"Failed to parse JSON file",

View File

@ -54,9 +54,7 @@ class LastFM:
lfm_scrobble.get("timestamp")
)
timestamp = lfm_scrobble.get("timestamp")
stop_timestamp = timestamp + timedelta(
seconds=track.run_time_seconds
)
stop_timestamp = timestamp + timedelta(seconds=track.run_time_seconds)
new_scrobble = Scrobble(
user=self.vrobbler_user,
timestamp=timestamp,
@ -149,13 +147,11 @@ class LastFM:
# TODO figure out if this will actually work
# timestamp = datetime.fromtimestamp(int(scrobble.timestamp), UTC)
timestamp = datetime.utcfromtimestamp(
int(scrobble.timestamp)
).replace(tzinfo=pytz.utc)
logger.info(
f"Scrobble appended to list for bulk create", extra=log_dict
timestamp = datetime.utcfromtimestamp(int(scrobble.timestamp)).replace(
tzinfo=pytz.utc
)
logger.info(f"Scrobble appended to list for bulk create", extra=log_dict)
scrobbles.append(
{
"artist": artist,

View File

@ -20,9 +20,7 @@ def import_from_webdav_for_all_users(restart=False):
webdav_pass__isnull=False,
webdav_auto_import=True,
).values_list("user_id", flat=True)
logger.info(
f"Start import of {webdav_enabled_user_ids.count()} webdav accounts"
)
logger.info(f"Start import of {webdav_enabled_user_ids.count()} webdav accounts")
koreader_import_count = 0

View File

@ -24,6 +24,4 @@ class Command(BaseCommand):
print(f"Deleted {scrobbles_found} zombie scrobbles")
return
print(
f"Found {scrobbles_found} zombie scrobbles, use --delete to remove them"
)
print(f"Found {scrobbles_found} zombie scrobbles, use --delete to remove them")

View File

@ -41,8 +41,6 @@ class Command(BaseCommand):
if not dry_run:
scrobble.save(update_fields=["scrobble_log"])
else:
print(
f"Scrobble {scrobble} scrobble_log updated to {old_data}"
)
print(f"Scrobble {scrobble} scrobble_log updated to {old_data}")
print(f"Migrated scrobble logs for {updated_scrobble_count} scrobbles")

View File

@ -42,9 +42,7 @@ class ScrobbleNtfyNotification(ScrobbleNotification):
def __init__(self, scrobble, **kwargs):
super().__init__(scrobble)
self.ntfy_str: str = f"{self.scrobble.media_obj}"
self.click_url = self.url_tmpl.format(
path=self.media_obj.get_absolute_url()
)
self.click_url = self.url_tmpl.format(path=self.media_obj.get_absolute_url())
self.title = self.media_obj.strings.verb
self.actions = ""
if kwargs.get("end", False):
@ -89,11 +87,7 @@ class MoodNtfyNotification(BasicNtfyNotification):
self.title = "Mood Check-in!"
def send(self):
if (
self.profile
and self.profile.ntfy_enabled
and self.profile.ntfy_url
):
if self.profile and self.profile.ntfy_enabled and self.profile.ntfy_url:
requests.post(
self.profile.ntfy_url,
data=self.ntfy_str.encode(encoding="utf-8"),

View File

@ -37,9 +37,7 @@ User = get_user_model()
def timestamp_user_tz_to_utc(timestamp: int, user_tz: ZoneInfo) -> datetime:
return user_tz.localize(datetime.utcfromtimestamp(timestamp)).astimezone(
pytz.utc
)
return user_tz.localize(datetime.utcfromtimestamp(timestamp)).astimezone(pytz.utc)
def convert_to_seconds(run_time: str) -> int:
@ -150,9 +148,7 @@ def import_lastfm_for_all_users(restart=False):
)
continue
lfm_client = LastFM(
user=get_user_model().objects.filter(id=user_id).first()
)
lfm_client = LastFM(user=get_user_model().objects.filter(id=user_id).first())
has_scrobbles = lfm_client.get_last_scrobbles(
time_from=last_processed, check=True
@ -237,9 +233,7 @@ def import_from_webdav_for_all_users(restart=False):
webdav_pass__isnull=False,
webdav_auto_import=True,
).values_list("user_id", flat=True)
logger.info(
f"start import of {webdav_enabled_user_ids.count()} webdav accounts"
)
logger.info(f"start import of {webdav_enabled_user_ids.count()} webdav accounts")
koreader_import_count = 0
@ -348,11 +342,7 @@ def send_mood_checkin_reminders() -> int:
def extract_domain(url):
parsed_url = urlparse(url)
domain = (
parsed_url.netloc.split(".")[-2]
+ "."
+ parsed_url.netloc.split(".")[-1]
)
domain = parsed_url.netloc.split(".")[-2] + "." + parsed_url.netloc.split(".")[-1]
return domain
@ -367,20 +357,14 @@ def fix_playback_position_seconds(
)
continue
if (
scrobble.media_type == "Track"
and scrobble.media_obj.run_time_seconds
):
if scrobble.media_type == "Track" and scrobble.media_obj.run_time_seconds:
too_long = (
scrobble.playback_position_seconds
> scrobble.media_obj.run_time_seconds
scrobble.playback_position_seconds > scrobble.media_obj.run_time_seconds
)
zero = scrobble.playback_position_seconds == 0
null = not scrobble.playback_position_seconds
if too_long or zero or null:
scrobble.playback_position_seconds = (
scrobble.media_obj.run_time_seconds
)
scrobble.playback_position_seconds = scrobble.media_obj.run_time_seconds
updated_scrobbles.append(scrobble)
if commit:
scrobble.save(update_fields=["playback_position_seconds"])

View File

@ -43,9 +43,7 @@ class Sport(TheSportsDbMixin):
@property
def default_event_run_time(self):
default_run_time = getattr(
settings, "DEFAULT_EVENT_RUNTIME_SECONDS", 14400
)
default_run_time = getattr(settings, "DEFAULT_EVENT_RUNTIME_SECONDS", 14400)
if self.default_event_run_time_seconds:
default_run_time = self.default_event_run_time_seconds
return default_run_time
@ -124,7 +122,9 @@ class SportEvent(ScrobblableMixin):
)
def __str__(self):
return f"{self.start.date()} - {self.round} - {self.home_team} v {self.away_team}"
return (
f"{self.start.date()} - {self.round} - {self.home_team} v {self.away_team}"
)
def get_absolute_url(self):
return reverse("sports:event_detail", kwargs={"slug": self.uuid})
@ -206,14 +206,10 @@ class SportEvent(ScrobblableMixin):
round.save(update_fields=["name"])
players_list = get_players_from_event(event_name)
player_one = Player.objects.filter(
name__icontains=players_list[0]
).first()
player_one = Player.objects.filter(name__icontains=players_list[0]).first()
if not player_one:
player_one = Player.objects.create(name=players_list[0])
player_two = Player.objects.filter(
name__icontains=players_list[1]
).first()
player_two = Player.objects.filter(name__icontains=players_list[1]).first()
if not player_two:
player_two = Player.objects.create(name=players_list[1])

View File

@ -21,9 +21,7 @@ def lookup_event_from_thesportsdb(event_id: str) -> dict:
if not event or type(event) != dict:
return {}
sport, _created = Sport.objects.get_or_create(
thesportsdb_id=event.get("strSport")
)
sport, _created = Sport.objects.get_or_create(thesportsdb_id=event.get("strSport"))
try:
start = parse(event.get("strTimestamp"))

View File

@ -29,9 +29,7 @@ def get_title_from_labels(
def convert_old_orgmode_log_to_new(commit=False):
scrobbles = Scrobble.objects.filter(
source="Org-mode", log__has_key="drawers"
)
scrobbles = Scrobble.objects.filter(source="Org-mode", log__has_key="drawers")
for scrobble in scrobbles:
scrobble.log["title"] = scrobble.log.pop("description")
scrobble.log["description"] = scrobble.log.pop("details")
@ -49,9 +47,7 @@ def convert_old_orgmode_log_to_new(commit=False):
def convert_old_todoist_log_to_new(commit=False):
scrobbles = Scrobble.objects.filter(
source="Todoist", log__has_key="todoist_type"
)
scrobbles = Scrobble.objects.filter(source="Todoist", log__has_key="todoist_type")
for scrobble in scrobbles:
scrobble.log["title"] = scrobble.log.pop("description")
scrobble.log["description"] = scrobble.log.pop("details")
@ -94,9 +90,7 @@ def convert_notes_to_dict(commit=False):
def convert_old_boardgame_log_to_new(commit=False):
scrobbles = Scrobble.objects.filter(
board_game__isnull=False, log__has_key="notes"
)
scrobbles = Scrobble.objects.filter(board_game__isnull=False, log__has_key="notes")
for scrobble in scrobbles:
if isinstance(scrobble.log.get("notes"), str):
scrobble.log["notes"] = [scrobble.log.pop("notes")]

View File

@ -34,20 +34,14 @@ def todoist_webhook(request):
is_note_type = todoist_tyllll = "note"
new_labels = event_data.get("labels", [])
old_labels = (
post_data.get("event_data_extra", {})
.get("old_item", {})
.get("labels", [])
post_data.get("event_data_extra", {}).get("old_item", {}).get("labels", [])
)
# TODO Don't hard code status strings in here
is_updated = todoist_event in ["updated"]
is_added = todoist_event in ["added"]
task_started = (
"inprogress" in new_labels and "inprogress" not in old_labels
)
task_stopped = (
"inprogress" not in new_labels and "inprogress" in old_labels
)
task_started = "inprogress" in new_labels and "inprogress" not in old_labels
task_stopped = "inprogress" not in new_labels and "inprogress" in old_labels
if is_item_type and is_updated and (task_started or task_stopped):
todoist_task = {
@ -71,9 +65,7 @@ def todoist_webhook(request):
"updated_at": task_data.get("updated_at"),
"details": task_data.get("description"),
"notes": event_data.get("content"),
"is_deleted": (
True if event_data.get("is_deleted") == "true" else False
),
"is_deleted": (True if event_data.get("is_deleted") == "true" else False),
}
if (is_added and not todoist_note) or (is_updated and not todoist_task):
@ -104,9 +96,7 @@ def todoist_webhook(request):
)
if todoist_note:
scrobble = todoist_scrobble_update_task(
todoist_note, user_profile.user_id
)
scrobble = todoist_scrobble_update_task(todoist_note, user_profile.user_id)
if not scrobble:
logger.info(

View File

@ -72,6 +72,6 @@ class Trail(ScrobblableMixin):
def scrobbles(self, user_id):
Scrobble = apps.get_model("scrobbles", "Scrobble")
return Scrobble.objects.filter(
user_id=user_id, life_event=self
).order_by("-timestamp")
return Scrobble.objects.filter(user_id=user_id, life_event=self).order_by(
"-timestamp"
)

View File

@ -37,9 +37,7 @@ def lookup_game_from_hltb(name_or_id: str) -> Optional[dict]:
found_games = []
for g in results:
found_games.append(f"{g.game_name} ({g.game_id})")
logger.info(
f"Found more than one match {found_games}, taking {hltb_game}"
)
logger.info(f"Found more than one match {found_games}, taking {hltb_game}")
game_dict = {
"title": hltb_game.game_name,

View File

@ -29,9 +29,7 @@ User = get_user_model()
def get_igdb_token() -> str:
token_url = REFRESH_TOKEN_URL.format(
id=IGDB_CLIENT_ID, secret=IGDB_CLIENT_SECRET
)
token_url = REFRESH_TOKEN_URL.format(id=IGDB_CLIENT_ID, secret=IGDB_CLIENT_SECRET)
response = requests.post(token_url)
results = json.loads(response.content)
return results.get("access_token")
@ -100,9 +98,9 @@ def lookup_game_from_igdb(name_or_igdb_id: str) -> Dict:
alt_name = game.get("alternative_names")[0].get("name")
screenshot_url = None
if "screenshots" in game.keys():
screenshot_url = "https:" + game.get("screenshots")[0].get(
"url"
).replace("t_thumb", "t_screenshot_big_2x")
screenshot_url = "https:" + game.get("screenshots")[0].get("url").replace(
"t_thumb", "t_screenshot_big_2x"
)
cover_url = None
if "cover" in game.keys():
cover_url = "https:" + game.get("cover").get("url").replace(

View File

@ -58,9 +58,7 @@ class VideoGamePlatform(TimeStampedModel):
return self.name
def get_absolute_url(self):
return reverse(
"videogames:platform_detail", kwargs={"slug": self.uuid}
)
return reverse("videogames:platform_detail", kwargs={"slug": self.uuid})
class VideoGameCollection(TimeStampedModel):
@ -85,9 +83,7 @@ class VideoGameCollection(TimeStampedModel):
return self.name
def get_absolute_url(self):
return reverse(
"videogames:collection_detail", kwargs={"slug": self.uuid}
)
return reverse("videogames:collection_detail", kwargs={"slug": self.uuid})
class VideoGame(LongPlayScrobblableMixin):
@ -188,9 +184,7 @@ class VideoGame(LongPlayScrobblableMixin):
return url
def get_absolute_url(self):
return reverse(
"videogames:videogame_detail", kwargs={"slug": self.uuid}
)
return reverse("videogames:videogame_detail", kwargs={"slug": self.uuid})
def hltb_link(self):
return f"https://howlongtobeat.com/game/{self.hltb_id}"

View File

@ -98,9 +98,7 @@ def import_retroarch_lrtl_files(playlog_path: str, user_id: int) -> List[dict]:
logger.warning(f"User ID {user_id} is not valid, cannot scrobble")
raise UserNotFound
game_logs = load_game_data(
playlog_path, pytz.timezone(user.profile.timezone)
)
game_logs = load_game_data(playlog_path, pytz.timezone(user.profile.timezone))
found_game = None
new_scrobbles = []
@ -130,9 +128,7 @@ def import_retroarch_lrtl_files(playlog_path: str, user_id: int) -> List[dict]:
# Found a game, check if scrobble exists
end_datetime = game_data.get("last_played")
found_scrobble = found_game.scrobble_set.filter(
stop_timestamp=end_datetime
)
found_scrobble = found_game.scrobble_set.filter(stop_timestamp=end_datetime)
if found_scrobble:
logger.info(f"Skipping scrobble for game {found_game.id}")
continue

View File

@ -8,7 +8,9 @@ from vrobbler.apps.videogames.exceptions import GameNotFound
logger = logging.getLogger(__name__)
MAME_LOOKUP_URL = "http://adb.arcadeitalia.net/dettaglio_mame.php?game_name={query}&search_id=2"
MAME_LOOKUP_URL = (
"http://adb.arcadeitalia.net/dettaglio_mame.php?game_name={query}&search_id=2"
)
def _strip_and_clean(text):

View File

@ -32,9 +32,7 @@ def get_or_create_videogame(
platforms = game_dict.get("platforms", [])
if platforms:
for platform in game_dict.get("platforms", []):
p, _created = VideoGamePlatform.objects.get_or_create(
name=platform
)
p, _created = VideoGamePlatform.objects.get_or_create(name=platform)
platform_ids.append(p.id)
game_dict.pop("platforms")
@ -98,9 +96,7 @@ def get_or_create_videogame(
return game
def load_game_data_from_igdb(
game_id: int, igdb_id: str = ""
) -> Optional[VideoGame]:
def load_game_data_from_igdb(game_id: int, igdb_id: str = "") -> Optional[VideoGame]:
"""Look up game, if it doesn't exist, lookup data from igdb"""
game = VideoGame.objects.filter(id=game_id).first()
if not game:

View File

@ -15,9 +15,7 @@ class SeriesSerializer(serializers.HyperlinkedModelSerializer):
class VideoSerializer(serializers.HyperlinkedModelSerializer):
channel = serializers.PrimaryKeyRelatedField(
queryset=Channel.objects.all()
)
channel = serializers.PrimaryKeyRelatedField(queryset=Channel.objects.all())
tv_series = serializers.PrimaryKeyRelatedField(
queryset=Series.objects.all(), required=False, allow_null=True
)

View File

@ -8,22 +8,18 @@ class Command(BaseCommand):
help = "Find or create a Video by ID and output it as JSON"
def add_arguments(self, parser):
parser.add_argument(
"video_id", type=str, help="The video ID to find or create"
)
parser.add_argument("video_id", type=str, help="The video ID to find or create")
def handle(self, *args, **options):
instance = Video.find_or_create(
options.get("video_id", ""), overwrite=True
)
instance = Video.find_or_create(options.get("video_id", ""), overwrite=True)
data = json.loads(serializers.serialize("json", [instance]))[0]
# --- Enrich with series model ---
if instance.tv_series_id:
series_instance = instance.tv_series
series_json = json.loads(
serializers.serialize("json", [series_instance])
)[0]
series_json = json.loads(serializers.serialize("json", [series_instance]))[
0
]
data["series"] = series_json # new nested field
if instance.channel_id:

View File

@ -7,9 +7,7 @@ import logging
logger = logging.getLogger(__name__)
USER_AGENT = (
"Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0"
)
USER_AGENT = "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0"
SKATEVIDEOSITE_URL = "https://www.skatevideosite.com"
SKATEVIDEOSITE_SEARCH_URL = SKATEVIDEOSITE_URL + "/search/?q={title}"

View File

@ -18,9 +18,7 @@ TMDB_IMAGE_URL = "https://image.tmdb.org/t/p/original"
logger = logging.getLogger(__name__)
def lookup_video_from_tmdb(
name_or_id: str, kind: str = "movie"
) -> VideoMetadata:
def lookup_video_from_tmdb(name_or_id: str, kind: str = "movie") -> VideoMetadata:
from videos.models import Series
imdb_id = name_or_id

View File

@ -26,14 +26,10 @@ def lookup_video_from_youtube(youtube_id: str) -> VideoMetadata:
response = requests.get(url, headers=headers)
if response.status_code != 200:
logger.warning(
"Bad response from Google", extra={"response": response}
)
logger.warning("Bad response from Google", extra={"response": response})
return video_metadata
yt_metadata = (
json.loads(response.content).get("items", [None])[0].get("snippet")
)
yt_metadata = json.loads(response.content).get("items", [None])[0].get("snippet")
duration_iso8601 = (
json.loads(response.content)
.get("items", [None])[0]

View File

@ -19,9 +19,7 @@ def clean_up_videos():
try:
video.save(update_fields=["imdb_id"])
except IntegrityError:
new_video = Video.objects.filter(
imdb_id="tt" + video.imdb_id
).first()
new_video = Video.objects.filter(imdb_id="tt" + video.imdb_id).first()
video.scrobble_set.all().update(video=new_video)
video.delete()

View File

@ -32,9 +32,7 @@ class SeriesDetailView(LoginRequiredMixin, ChartContextMixin, generic.DetailView
context_data = super().get_context_data(**kwargs)
context_data["scrobbles"] = self.object.scrobbles_for_user(user_id)
next_episode_id = (
self.object.last_scrobbled_episode(user_id).next_imdb_id or ""
)
next_episode_id = self.object.last_scrobbled_episode(user_id).next_imdb_id or ""
if self.object.is_episode_playing(user_id):
next_episode_id = ""
if next_episode_id:
@ -96,10 +94,7 @@ class VideoListView(ScrobbleableListView):
if channel:
channels_this_week[channel.id] = {
"channel": channel,
"count": channels_this_week.get(channel.id, {}).get(
"count", 0
)
+ 1,
"count": channels_this_week.get(channel.id, {}).get("count", 0) + 1,
}
channels_this_month = {}
@ -108,9 +103,7 @@ class VideoListView(ScrobbleableListView):
if channel:
channels_this_month[channel.id] = {
"channel": channel,
"count": channels_this_month.get(channel.id, {}).get(
"count", 0
)
"count": channels_this_month.get(channel.id, {}).get("count", 0)
+ 1,
}

View File

@ -39,9 +39,9 @@ class Domain(TimeStampedModel):
def scrobbles_for_user(self, user_id):
from scrobbles.models import Scrobble
return Scrobble.objects.filter(
web_page__domain=self, user_id=user_id
).order_by("-timestamp")
return Scrobble.objects.filter(web_page__domain=self, user_id=user_id).order_by(
"-timestamp"
)
class WebPage(ScrobblableMixin):
@ -136,9 +136,7 @@ class WebPage(ScrobblableMixin):
def scrobbles(self, user):
Scrobble = apps.get_model("scrobbles", "Scrobble")
return Scrobble.objects.filter(user=user, web_page=self).order_by(
"-timestamp"
)
return Scrobble.objects.filter(user=user, web_page=self).order_by("-timestamp")
def clean_title(self, title: str, save=True):
if len(title.split("|")) > 1:
@ -213,9 +211,7 @@ class WebPage(ScrobblableMixin):
return
if response.status_code == 200:
logger.info(
"Website already exists in archive", extra={"url": self.url}
)
logger.info("Website already exists in archive", extra={"url": self.url})
else:
raise Exception(
f"Failed to push URL to archivebox (Response {response.status_code})"

View File

@ -40,10 +40,7 @@ class WebPageListView(ScrobbleableListView):
if domain:
domains_this_week[domain.id] = {
"domain": domain,
"count": domains_this_week.get(domain.id, {}).get(
"count", 0
)
+ 1,
"count": domains_this_week.get(domain.id, {}).get("count", 0) + 1,
}
domains_this_month = {}
@ -52,10 +49,7 @@ class WebPageListView(ScrobbleableListView):
if domain:
domains_this_month[domain.id] = {
"domain": domain,
"count": domains_this_month.get(domain.id, {}).get(
"count", 0
)
+ 1,
"count": domains_this_month.get(domain.id, {}).get("count", 0) + 1,
}
context_data["domains_this_week"] = sorted(
@ -82,9 +76,7 @@ class WebPageDetailView(ScrobbleableDetailView):
return context
class WebPageReadView(
LoginRequiredMixin, generic.edit.FormView, generic.DetailView
):
class WebPageReadView(LoginRequiredMixin, generic.edit.FormView, generic.DetailView):
model = WebPage
slug_field = "uuid"
template_name = "webpages/webpage_read.html"
@ -95,11 +87,7 @@ class WebPageReadView(
webpage = WebPage.objects.get(uuid=kwargs.get("slug"))
latest_scrobble = webpage.scrobbles(user).last()
if latest_scrobble.played_to_completion:
redirect(
reverse(
"webpages:webpage_detail", kwargs={"slug": webpage.uuid}
)
)
redirect(reverse("webpages:webpage_detail", kwargs={"slug": webpage.uuid}))
return super().get(*args, **kwargs)
def form_valid(self, *args):

View File

@ -19,9 +19,7 @@ class HealthCheckMiddleware:
is_db_connected = False
else:
is_db_connected = True
logger.info(
"[health-check]", extra={"is_db_connected": is_db_connected}
)
logger.info("[health-check]", extra={"is_db_connected": is_db_connected})
if is_db_connected:
return HttpResponse("ok")
return self.get_response(request)

View File

@ -37,9 +37,7 @@ TESTING = len(sys.argv) > 1 and sys.argv[1] == "test"
TAGGIT_CASE_INSENSITIVE = True
KEEP_DETAILED_SCROBBLE_LOGS = os.getenv(
"VROBBLER_KEEP_DETAILED_SCROBBLE_LOGS", False
)
KEEP_DETAILED_SCROBBLE_LOGS = os.getenv("VROBBLER_KEEP_DETAILED_SCROBBLE_LOGS", False)
# Key must be 16, 24 or 32 bytes long and will be converted to a byte stream
ENCRYPTED_FIELD_KEY = os.getenv(
@ -54,9 +52,7 @@ DELETE_STALE_SCROBBLES = (
)
# Used to dump data coming from srobbling sources, helpful for building new inputs
DUMP_REQUEST_DATA = (
os.getenv("VROBBLER_DUMP_REQUEST_DATA", "false").lower() in TRUTHY
)
DUMP_REQUEST_DATA = os.getenv("VROBBLER_DUMP_REQUEST_DATA", "false").lower() in TRUTHY
USDA_API_KEY = os.getenv("VROBBLER_USDA_API_KEY")
THESPORTSDB_API_KEY = os.getenv("VROBBLER_THESPORTSDB_API_KEY", "2")
@ -72,9 +68,7 @@ COMICVINE_API_KEY = os.getenv("VROBBLER_COMICVINE_API_KEY")
BGG_ACCESS_TOKEN = os.getenv("VROBBLER_BGG_ACCESS_TOKEN", "")
GEOLOC_ACCURACY = os.getenv("VROBBLER_GEOLOC_ACCURACY", 3)
GEOLOC_PROXIMITY = os.getenv("VROBBLER_GEOLOC_PROXIMITY", "0.0001")
POINTS_FOR_MOVEMENT_HISTORY = os.getenv(
"VROBBLER_POINTS_FOR_MOVEMENT_HISTORY", 3
)
POINTS_FOR_MOVEMENT_HISTORY = os.getenv("VROBBLER_POINTS_FOR_MOVEMENT_HISTORY", 3)
TODOIST_CLIENT_ID = os.getenv("VROBBLER_TODOIST_CLIENT_ID", "")
TODOIST_CLIENT_SECRET = os.getenv("VROBBLER_TODOIST_CLIENT_SECRET", "")
@ -97,9 +91,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
TIME_ZONE = os.getenv("VROBBLER_TIME_ZONE", "America/New_York")
ALLOWED_HOSTS = ["*"]
CSRF_TRUSTED_ORIGINS = [
os.getenv("VROBBLER_TRUSTED_ORIGINS", "http://localhost:8000")
]
CSRF_TRUSTED_ORIGINS = [os.getenv("VROBBLER_TRUSTED_ORIGINS", "http://localhost:8000")]
X_FRAME_OPTIONS = "SAMEORIGIN"
REDIS_URL = os.getenv("VROBBLER_REDIS_URL", None)
@ -108,9 +100,7 @@ if REDIS_URL:
else:
print("Eagerly running all tasks")
CELERY_TASK_ALWAYS_EAGER = (
os.getenv("VROBBLER_SKIP_CELERY", "false").lower() in TRUTHY
)
CELERY_TASK_ALWAYS_EAGER = os.getenv("VROBBLER_SKIP_CELERY", "false").lower() in TRUTHY
CELERY_BROKER_URL = REDIS_URL if REDIS_URL else "memory://localhost/"
CELERY_RESULT_BACKEND = "django-db"
CELERY_TIMEZONE = os.getenv("VROBBLER_TIME_ZONE", "America/New_York")
@ -231,9 +221,7 @@ DATABASES = {
}
if TESTING:
DATABASES = {
"default": dj_database_url.config(default="sqlite:///testdb.sqlite3")
}
DATABASES = {"default": dj_database_url.config(default="sqlite:///testdb.sqlite3")}
db_str = ""
if "sqlite" in DATABASES["default"]["ENGINE"]:
@ -269,9 +257,7 @@ REST_FRAMEWORK = {
"rest_framework.authentication.SessionAuthentication",
],
"DEFAULT_CONTENT_NEGOTIATION_CLASS": "vrobbler.negotiation.IgnoreClientContentNegotiation",
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend"
],
"DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 200,
}
@ -331,9 +317,7 @@ else:
STATIC_ROOT = os.getenv(
"VROBBLER_STATIC_ROOT", os.path.join(PROJECT_ROOT, "static")
)
MEDIA_ROOT = os.getenv(
"VROBBLER_MEDIA_ROOT", os.path.join(PROJECT_ROOT, "media")
)
MEDIA_ROOT = os.getenv("VROBBLER_MEDIA_ROOT", os.path.join(PROJECT_ROOT, "media"))
STATIC_URL = os.getenv("VROBBLER_STATIC_URL", "/static/")
MEDIA_URL = os.getenv("VROBBLER_MEDIA_URL", "/media/")
@ -417,9 +401,7 @@ LOGGING = {
},
}
LOG_TO_CONSOLE = (
os.getenv("VROBBLER_LOG_TO_CONSOLE", "false").lower() in TRUTHY
)
LOG_TO_CONSOLE = os.getenv("VROBBLER_LOG_TO_CONSOLE", "false").lower() in TRUTHY
if LOG_TO_CONSOLE:
LOGGING["loggers"]["django"]["handlers"] = ["console"]
LOGGING["loggers"]["vrobbler"]["handlers"] = ["console"]