This commit is contained in:
@ -88,7 +88,7 @@ fetching and simple saving.
|
|||||||
*** Metadata sources
|
*** Metadata sources
|
||||||
**** Scraper
|
**** Scraper
|
||||||
|
|
||||||
* Backlog [0/22] :vrobbler:project:personal:
|
* Backlog [1/23] :vrobbler:project:personal:
|
||||||
** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles:
|
** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles:
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:ID: 702462cf-d54b-48c6-8a7c-78b8de751deb
|
:ID: 702462cf-d54b-48c6-8a7c-78b8de751deb
|
||||||
@ -604,6 +604,10 @@ independent of the email flow it was originally creatdd for
|
|||||||
|
|
||||||
** TODO [#B] Is there way to create unique slugs for media instances :media_types:
|
** TODO [#B] Is there way to create unique slugs for media instances :media_types:
|
||||||
|
|
||||||
|
** DONE [#A] Add tests to discgolf app :discgolf:tests:
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 28e8344e-c3cf-19af-ce1c-cb821d4fcb5f
|
||||||
|
:END:
|
||||||
* Version 56.0 [1/1]
|
* Version 56.0 [1/1]
|
||||||
** DONE [#B] Add DiscGolf as a scrobbleable media :discgolf:
|
** DONE [#B] Add DiscGolf as a scrobbleable media :discgolf:
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
|
|||||||
0
tests/discgolf_tests/__init__.py
Normal file
0
tests/discgolf_tests/__init__.py
Normal file
63
tests/discgolf_tests/conftest.py
Normal file
63
tests/discgolf_tests/conftest.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import tempfile
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def user(db):
|
||||||
|
return User.objects.create(email="golfer@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def udisc_singles_csv_content():
|
||||||
|
return """PlayerName,CourseName,LayoutName,StartDate,Hole1,Hole2,Hole3,Total
|
||||||
|
Par,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",3,3,3,9
|
||||||
|
Alice,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",4,2,3,9
|
||||||
|
Bob,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",3,4,5,12
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def udisc_singles_csv_file(udisc_singles_csv_content):
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".csv", delete=False, encoding="utf-8-sig"
|
||||||
|
) as f:
|
||||||
|
f.write(udisc_singles_csv_content)
|
||||||
|
return f.name
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def udisc_teams_csv_content():
|
||||||
|
return """PlayerName,CourseName,LayoutName,StartDate,Hole1,Hole2,Hole3,Total
|
||||||
|
Par,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",3,3,3,9
|
||||||
|
Alice+Bob,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",4,2,3,9
|
||||||
|
Charlie+Diana,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",3,4,5,12
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def udisc_teams_csv_file(udisc_teams_csv_content):
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".csv", delete=False, encoding="utf-8-sig"
|
||||||
|
) as f:
|
||||||
|
f.write(udisc_teams_csv_content)
|
||||||
|
return f.name
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def udisc_csv_no_par_content():
|
||||||
|
return """PlayerName,CourseName,LayoutName,StartDate,Hole1,Hole2,Hole3,Total
|
||||||
|
Alice,Maple Hill,Mountains,"Jun 15, 2026 10:00 AM",4,2,3,9
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def udisc_csv_no_par_file(udisc_csv_no_par_content):
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".csv", delete=False, encoding="utf-8-sig"
|
||||||
|
) as f:
|
||||||
|
f.write(udisc_csv_no_par_content)
|
||||||
|
return f.name
|
||||||
102
tests/discgolf_tests/test_models.py
Normal file
102
tests/discgolf_tests/test_models.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
from discgolf.models import DiscGolfCourse, DiscGolfLogData
|
||||||
|
from scrobbles.dataclasses import BaseLogData
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiscGolfCourseModel:
|
||||||
|
def test_create_course(self, db):
|
||||||
|
course = DiscGolfCourse.objects.create(
|
||||||
|
title="Maple Hill",
|
||||||
|
layout_name="Mountains",
|
||||||
|
number_of_holes=18,
|
||||||
|
par_total=54,
|
||||||
|
par_per_hole={"hole_1": 3, "hole_2": 3},
|
||||||
|
)
|
||||||
|
assert course.uuid is not None
|
||||||
|
assert str(course) == "Maple Hill (Mountains)"
|
||||||
|
assert course.subtitle == "Mountains"
|
||||||
|
|
||||||
|
def test_subtitle_fallback(self, db):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
assert course.subtitle == ""
|
||||||
|
|
||||||
|
def test_logdata_cls(self, db):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
assert course.logdata_cls is DiscGolfLogData
|
||||||
|
assert issubclass(course.logdata_cls, BaseLogData)
|
||||||
|
|
||||||
|
def test_strings(self, db):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
assert course.strings.verb == "Playing"
|
||||||
|
assert course.strings.tags == "golf"
|
||||||
|
|
||||||
|
def test_primary_image_url(self, db):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
assert course.primary_image_url == ""
|
||||||
|
|
||||||
|
def test_get_absolute_url(self, db):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
url = course.get_absolute_url()
|
||||||
|
assert str(course.uuid) in url
|
||||||
|
assert url.startswith("/disc-golf/")
|
||||||
|
|
||||||
|
def test_find_or_create_new(self, db):
|
||||||
|
course = DiscGolfCourse.find_or_create(
|
||||||
|
"New Course", layout_name="Default"
|
||||||
|
)
|
||||||
|
assert course.title == "New Course"
|
||||||
|
assert course.layout_name == "Default"
|
||||||
|
|
||||||
|
def test_find_or_create_existing(self, db):
|
||||||
|
created = DiscGolfCourse.objects.create(
|
||||||
|
title="Existing", layout_name="Alpha"
|
||||||
|
)
|
||||||
|
found = DiscGolfCourse.find_or_create("Existing", layout_name="Beta")
|
||||||
|
assert found.id == created.id
|
||||||
|
assert found.layout_name == "Alpha"
|
||||||
|
|
||||||
|
def test_scrobbles_method(self, db, user):
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
dt1 = datetime(2026, 6, 15, 14, 0, 0, tzinfo=pytz.UTC)
|
||||||
|
dt2 = datetime(2026, 6, 14, 14, 0, 0, tzinfo=pytz.UTC)
|
||||||
|
s1 = Scrobble.objects.create(
|
||||||
|
user=user,
|
||||||
|
disc_golf_course=course,
|
||||||
|
media_type=Scrobble.MediaType.DISC_GOLF,
|
||||||
|
timestamp=dt1,
|
||||||
|
)
|
||||||
|
s2 = Scrobble.objects.create(
|
||||||
|
user=user,
|
||||||
|
disc_golf_course=course,
|
||||||
|
media_type=Scrobble.MediaType.DISC_GOLF,
|
||||||
|
timestamp=dt2,
|
||||||
|
)
|
||||||
|
qs = course.scrobbles(user.id)
|
||||||
|
assert list(qs) == [s1, s2]
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiscGolfLogData:
|
||||||
|
def test_basic_logdata(self):
|
||||||
|
data = DiscGolfLogData()
|
||||||
|
assert data.scores is None
|
||||||
|
assert data.weather is None
|
||||||
|
assert data.fun_factor is None
|
||||||
|
assert data.course_name is None
|
||||||
|
|
||||||
|
def test_logdata_with_scores(self):
|
||||||
|
data = DiscGolfLogData(
|
||||||
|
scores={"Alice": {"person_id": 1, "total": 9}},
|
||||||
|
weather="Sunny",
|
||||||
|
fun_factor="High",
|
||||||
|
course_name="Maple Hill",
|
||||||
|
par=9,
|
||||||
|
round_type="Singles",
|
||||||
|
)
|
||||||
|
assert data.scores["Alice"]["total"] == 9
|
||||||
|
assert data.weather == "Sunny"
|
||||||
|
assert data.round_type == "Singles"
|
||||||
150
tests/discgolf_tests/test_utils.py
Normal file
150
tests/discgolf_tests/test_utils.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from discgolf.models import DiscGolfCourse
|
||||||
|
from discgolf.utils import _parse_udisc_datetime, import_udisc_csv
|
||||||
|
from people.models import Person
|
||||||
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
|
|
||||||
|
class TestParserHelpers:
|
||||||
|
def test_parse_udisc_datetime(self):
|
||||||
|
dt = _parse_udisc_datetime("Jun 15, 2026 10:00 AM")
|
||||||
|
assert dt is not None
|
||||||
|
assert dt.year == 2026
|
||||||
|
assert dt.month == 6
|
||||||
|
assert dt.day == 15
|
||||||
|
assert dt.hour == 10
|
||||||
|
assert dt.minute == 0
|
||||||
|
|
||||||
|
def test_parse_udisc_datetime_date_only(self):
|
||||||
|
dt = _parse_udisc_datetime("Jun 15, 2026")
|
||||||
|
assert dt is not None
|
||||||
|
assert dt.year == 2026
|
||||||
|
|
||||||
|
|
||||||
|
class TestImportUdiscCSV:
|
||||||
|
def test_import_singles_creates_course(self, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
course = DiscGolfCourse.objects.filter(title="Maple Hill").first()
|
||||||
|
assert course is not None
|
||||||
|
assert course.layout_name == "Mountains"
|
||||||
|
assert course.number_of_holes == 3
|
||||||
|
assert course.par_total == 9
|
||||||
|
assert course.par_per_hole == {"hole_1": 3, "hole_2": 3, "hole_3": 3}
|
||||||
|
|
||||||
|
def test_import_singles_creates_scrobble(self, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
assert Scrobble.objects.filter(source="uDisc").count() == 1
|
||||||
|
|
||||||
|
def test_import_singles_logdata(self, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
scrobble = Scrobble.objects.filter(source="uDisc").first()
|
||||||
|
log = scrobble.log
|
||||||
|
assert log["course_name"] == "Maple Hill"
|
||||||
|
assert log["par"] == 9
|
||||||
|
assert log["round_type"] == "Singles"
|
||||||
|
assert "Alice" in log["scores"]
|
||||||
|
assert "Bob" in log["scores"]
|
||||||
|
assert log["scores"]["Alice"]["total"] == 9
|
||||||
|
assert log["scores"]["Bob"]["total"] == 12
|
||||||
|
|
||||||
|
def test_import_singles_creates_people(self, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
assert Person.objects.filter(name="Alice").exists()
|
||||||
|
assert Person.objects.filter(name="Bob").exists()
|
||||||
|
|
||||||
|
def test_import_teams_creates_scrobble(self, user, udisc_teams_csv_file):
|
||||||
|
import_udisc_csv(udisc_teams_csv_file, user.id)
|
||||||
|
assert Scrobble.objects.filter(source="uDisc").count() == 1
|
||||||
|
|
||||||
|
def test_import_teams_logdata(self, user, udisc_teams_csv_file):
|
||||||
|
import_udisc_csv(udisc_teams_csv_file, user.id)
|
||||||
|
scrobble = Scrobble.objects.filter(source="uDisc").first()
|
||||||
|
assert scrobble.log["round_type"] == "Teams"
|
||||||
|
alice_bob = scrobble.log["scores"]["Alice+Bob"]
|
||||||
|
assert "person_ids" in alice_bob
|
||||||
|
assert len(alice_bob["person_ids"]) == 2
|
||||||
|
|
||||||
|
def test_import_creates_team_people(self, user, udisc_teams_csv_file):
|
||||||
|
import_udisc_csv(udisc_teams_csv_file, user.id)
|
||||||
|
assert Person.objects.filter(name="Alice").exists()
|
||||||
|
assert Person.objects.filter(name="Bob").exists()
|
||||||
|
assert Person.objects.filter(name="Charlie").exists()
|
||||||
|
assert Person.objects.filter(name="Diana").exists()
|
||||||
|
|
||||||
|
def test_import_teams_par_per_hole(self, user, udisc_teams_csv_file):
|
||||||
|
import_udisc_csv(udisc_teams_csv_file, user.id)
|
||||||
|
course = DiscGolfCourse.objects.get(title="Maple Hill")
|
||||||
|
assert course.par_per_hole == {"hole_1": 3, "hole_2": 3, "hole_3": 3}
|
||||||
|
|
||||||
|
def test_import_no_par_returns_empty(self, user, udisc_csv_no_par_file):
|
||||||
|
result = import_udisc_csv(udisc_csv_no_par_file, user.id)
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
def test_import_empty_csv(self, user, db):
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".csv", delete=False, encoding="utf-8-sig"
|
||||||
|
) as f:
|
||||||
|
f.write("PlayerName,CourseName,LayoutName,StartDate,Hole1,Total\n")
|
||||||
|
path = f.name
|
||||||
|
|
||||||
|
result = import_udisc_csv(path, user.id)
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
def test_import_idempotent(self, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
assert DiscGolfCourse.objects.filter(title="Maple Hill").count() == 1
|
||||||
|
assert Scrobble.objects.filter(source="uDisc").count() == 2
|
||||||
|
|
||||||
|
def test_import_course_defaults_only_on_create(
|
||||||
|
self, user, udisc_singles_csv_file
|
||||||
|
):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
course = DiscGolfCourse.objects.get(title="Maple Hill")
|
||||||
|
assert course.layout_name == "Mountains"
|
||||||
|
|
||||||
|
course.layout_name = "Updated"
|
||||||
|
course.save()
|
||||||
|
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
course.refresh_from_db()
|
||||||
|
assert course.layout_name == "Updated"
|
||||||
|
|
||||||
|
@patch("discgolf.utils.ScrobbleNtfyNotification")
|
||||||
|
def test_import_sends_notification(self, mock_notification_class, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
mock_notification_class.assert_called_once()
|
||||||
|
mock_notification_class.return_value.send.assert_called_once()
|
||||||
|
|
||||||
|
def test_import_hole_scores_per_player(self, user, udisc_singles_csv_file):
|
||||||
|
import_udisc_csv(udisc_singles_csv_file, user.id)
|
||||||
|
scrobble = Scrobble.objects.filter(source="uDisc").first()
|
||||||
|
alice = scrobble.log["scores"]["Alice"]
|
||||||
|
assert alice["hole_1"] == 4
|
||||||
|
assert alice["hole_2"] == 2
|
||||||
|
assert alice["hole_3"] == 3
|
||||||
|
bob = scrobble.log["scores"]["Bob"]
|
||||||
|
assert bob["hole_1"] == 3
|
||||||
|
assert bob["hole_2"] == 4
|
||||||
|
assert bob["hole_3"] == 5
|
||||||
|
|
||||||
|
def test_import_record_error_on_bad_data(self, user, db):
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
content = """PlayerName,CourseName,LayoutName,StartDate,Hole1,Hole2,Hole3,Total
|
||||||
|
Par,,Mountains,"Jun 15, 2026 10:00 AM",3,3,3,9
|
||||||
|
"""
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".csv", delete=False, encoding="utf-8-sig"
|
||||||
|
) as f:
|
||||||
|
f.write(content)
|
||||||
|
path = f.name
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
result = import_udisc_csv(path, user.id, record_error=errors.append)
|
||||||
|
assert len(result) == 1
|
||||||
|
course = DiscGolfCourse.objects.first()
|
||||||
|
assert course.title == ""
|
||||||
58
tests/discgolf_tests/test_views.py
Normal file
58
tests/discgolf_tests/test_views.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test import Client
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from discgolf.models import DiscGolfCourse
|
||||||
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiscGolfCourseViews:
|
||||||
|
def _make_scrobble(self, user, course):
|
||||||
|
dt = datetime(2026, 6, 15, 14, 0, 0, tzinfo=pytz.UTC)
|
||||||
|
return Scrobble.objects.create(
|
||||||
|
user=user,
|
||||||
|
disc_golf_course=course,
|
||||||
|
media_type=Scrobble.MediaType.DISC_GOLF,
|
||||||
|
timestamp=dt,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_course_list_anonymous(self, db, user):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
self._make_scrobble(user, course)
|
||||||
|
client = Client()
|
||||||
|
response = client.get(reverse("discgolf:course_list"))
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
def test_course_list_shows_course(self, db, user):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
self._make_scrobble(user, course)
|
||||||
|
client = Client()
|
||||||
|
response = client.get(reverse("discgolf:course_list"))
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Maple Hill" in response.content.decode()
|
||||||
|
|
||||||
|
def test_course_detail_anonymous(self, db, user):
|
||||||
|
course = DiscGolfCourse.objects.create(title="Maple Hill")
|
||||||
|
self._make_scrobble(user, course)
|
||||||
|
client = Client()
|
||||||
|
response = client.get(
|
||||||
|
reverse("discgolf:course_detail", kwargs={"slug": course.uuid})
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
def test_course_detail_shows_course(self, db, user):
|
||||||
|
course = DiscGolfCourse.objects.create(
|
||||||
|
title="Maple Hill", layout_name="Mountains"
|
||||||
|
)
|
||||||
|
self._make_scrobble(user, course)
|
||||||
|
client = Client()
|
||||||
|
response = client.get(
|
||||||
|
reverse("discgolf:course_detail", kwargs={"slug": course.uuid})
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "Maple Hill" in response.content.decode()
|
||||||
@ -71,6 +71,6 @@ class DiscGolfCourse(ScrobblableMixin):
|
|||||||
|
|
||||||
def scrobbles(self, user_id):
|
def scrobbles(self, user_id):
|
||||||
Scrobble = apps.get_model("scrobbles", "Scrobble")
|
Scrobble = apps.get_model("scrobbles", "Scrobble")
|
||||||
return Scrobble.objects.filter(user_id=user_id, disc_golf=self).order_by(
|
return Scrobble.objects.filter(user_id=user_id, disc_golf_course=self).order_by(
|
||||||
"-timestamp"
|
"-timestamp"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -123,7 +123,7 @@ class ScrobbleAdmin(admin.ModelAdmin):
|
|||||||
"web_page",
|
"web_page",
|
||||||
"life_event",
|
"life_event",
|
||||||
"birding_location",
|
"birding_location",
|
||||||
"disc_golf",
|
"disc_golf_course",
|
||||||
"long_play_last_scrobble",
|
"long_play_last_scrobble",
|
||||||
)
|
)
|
||||||
list_filter = (
|
list_filter = (
|
||||||
@ -185,5 +185,5 @@ class FavoriteMediaAdmin(admin.ModelAdmin):
|
|||||||
"web_page",
|
"web_page",
|
||||||
"life_event",
|
"life_event",
|
||||||
"birding_location",
|
"birding_location",
|
||||||
"disc_golf",
|
"disc_golf_course",
|
||||||
)
|
)
|
||||||
|
|||||||
@ -36,7 +36,7 @@ PLAY_AGAIN_MEDIA = {
|
|||||||
"locations": "GeoLocation",
|
"locations": "GeoLocation",
|
||||||
"videos": "Video",
|
"videos": "Video",
|
||||||
"birds": "BirdingLocation",
|
"birds": "BirdingLocation",
|
||||||
"discgolf": "DiscGolf",
|
"discgolf": "DiscGolfCourse",
|
||||||
}
|
}
|
||||||
|
|
||||||
MEDIA_END_PADDING_SECONDS = {
|
MEDIA_END_PADDING_SECONDS = {
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
# Generated by Django 4.2.29 on 2026-06-20 04:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("scrobbles", "0099_favoritemedia_disc_golf_scrobble_disc_golf_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="favoritemedia",
|
||||||
|
old_name="disc_golf",
|
||||||
|
new_name="disc_golf_course",
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="scrobble",
|
||||||
|
old_name="disc_golf",
|
||||||
|
new_name="disc_golf_course",
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="favoritemedia",
|
||||||
|
name="media_type",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("Video", "Video"),
|
||||||
|
("Track", "Track"),
|
||||||
|
("PodcastEpisode", "Podcast episode"),
|
||||||
|
("SportEvent", "Sport event"),
|
||||||
|
("Book", "Book"),
|
||||||
|
("Paper", "Paper"),
|
||||||
|
("VideoGame", "Video game"),
|
||||||
|
("BoardGame", "Board game"),
|
||||||
|
("GeoLocation", "GeoLocation"),
|
||||||
|
("Trail", "Trail"),
|
||||||
|
("Beer", "Beer"),
|
||||||
|
("Puzzle", "Puzzle"),
|
||||||
|
("Food", "Food"),
|
||||||
|
("Task", "Task"),
|
||||||
|
("WebPage", "Web Page"),
|
||||||
|
("LifeEvent", "Life event"),
|
||||||
|
("Mood", "Mood"),
|
||||||
|
("BrickSet", "Brick set"),
|
||||||
|
("Channel", "Channel"),
|
||||||
|
("BirdingLocation", "Birding location"),
|
||||||
|
("DiscGolfCourse", "Disc golf"),
|
||||||
|
],
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="scrobble",
|
||||||
|
name="media_type",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("Video", "Video"),
|
||||||
|
("Track", "Track"),
|
||||||
|
("PodcastEpisode", "Podcast episode"),
|
||||||
|
("SportEvent", "Sport event"),
|
||||||
|
("Book", "Book"),
|
||||||
|
("Paper", "Paper"),
|
||||||
|
("VideoGame", "Video game"),
|
||||||
|
("BoardGame", "Board game"),
|
||||||
|
("GeoLocation", "GeoLocation"),
|
||||||
|
("Trail", "Trail"),
|
||||||
|
("Beer", "Beer"),
|
||||||
|
("Puzzle", "Puzzle"),
|
||||||
|
("Food", "Food"),
|
||||||
|
("Task", "Task"),
|
||||||
|
("WebPage", "Web Page"),
|
||||||
|
("LifeEvent", "Life event"),
|
||||||
|
("Mood", "Mood"),
|
||||||
|
("BrickSet", "Brick set"),
|
||||||
|
("Channel", "Channel"),
|
||||||
|
("BirdingLocation", "Birding location"),
|
||||||
|
("DiscGolfCourse", "Disc golf"),
|
||||||
|
],
|
||||||
|
default="Video",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -693,7 +693,7 @@ TYPE_FK_PREFETCHES: dict[str, tuple[str, ...]] = {
|
|||||||
"BrickSet": ("brick_set",),
|
"BrickSet": ("brick_set",),
|
||||||
"Channel": ("channel",),
|
"Channel": ("channel",),
|
||||||
"BirdingLocation": ("birding_location",),
|
"BirdingLocation": ("birding_location",),
|
||||||
"DiscGolf": ("disc_golf",),
|
"DiscGolfCourse": ("disc_golf_course",),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -721,7 +721,7 @@ class ScrobbleQuerySet(models.QuerySet):
|
|||||||
"mood",
|
"mood",
|
||||||
"brick_set",
|
"brick_set",
|
||||||
"birding_location",
|
"birding_location",
|
||||||
"disc_golf",
|
"disc_golf_course",
|
||||||
)
|
)
|
||||||
|
|
||||||
def with_related_for_types(self, media_types: list[str]):
|
def with_related_for_types(self, media_types: list[str]):
|
||||||
@ -772,7 +772,7 @@ class Scrobble(TimeStampedModel):
|
|||||||
BRICKSET = "BrickSet", "Brick set"
|
BRICKSET = "BrickSet", "Brick set"
|
||||||
CHANNEL = "Channel", "Channel"
|
CHANNEL = "Channel", "Channel"
|
||||||
BIRDING_LOCATION = "BirdingLocation", "Birding location"
|
BIRDING_LOCATION = "BirdingLocation", "Birding location"
|
||||||
DISC_GOLF = "DiscGolf", "Disc golf"
|
DISC_GOLF = "DiscGolfCourse", "Disc golf"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls):
|
def list(cls):
|
||||||
@ -803,7 +803,7 @@ class Scrobble(TimeStampedModel):
|
|||||||
birding_location = models.ForeignKey(
|
birding_location = models.ForeignKey(
|
||||||
BirdingLocation, on_delete=models.DO_NOTHING, **BNULL
|
BirdingLocation, on_delete=models.DO_NOTHING, **BNULL
|
||||||
)
|
)
|
||||||
disc_golf = models.ForeignKey(
|
disc_golf_course = models.ForeignKey(
|
||||||
DiscGolfCourse, on_delete=models.DO_NOTHING, **BNULL
|
DiscGolfCourse, on_delete=models.DO_NOTHING, **BNULL
|
||||||
)
|
)
|
||||||
media_type = models.CharField(
|
media_type = models.CharField(
|
||||||
@ -1377,8 +1377,8 @@ class Scrobble(TimeStampedModel):
|
|||||||
media_obj = self.channel
|
media_obj = self.channel
|
||||||
if self.birding_location:
|
if self.birding_location:
|
||||||
media_obj = self.birding_location
|
media_obj = self.birding_location
|
||||||
if self.disc_golf:
|
if self.disc_golf_course:
|
||||||
media_obj = self.disc_golf
|
media_obj = self.disc_golf_course
|
||||||
return media_obj
|
return media_obj
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -1937,7 +1937,7 @@ class FavoriteMedia(TimeStampedModel):
|
|||||||
birding_location = models.ForeignKey(
|
birding_location = models.ForeignKey(
|
||||||
BirdingLocation, on_delete=models.CASCADE, **BNULL
|
BirdingLocation, on_delete=models.CASCADE, **BNULL
|
||||||
)
|
)
|
||||||
disc_golf = models.ForeignKey(
|
disc_golf_course = models.ForeignKey(
|
||||||
DiscGolfCourse, on_delete=models.CASCADE, **BNULL
|
DiscGolfCourse, on_delete=models.CASCADE, **BNULL
|
||||||
)
|
)
|
||||||
media_type = models.CharField(max_length=20, choices=Scrobble.MediaType.choices)
|
media_type = models.CharField(max_length=20, choices=Scrobble.MediaType.choices)
|
||||||
@ -1990,8 +1990,8 @@ class FavoriteMedia(TimeStampedModel):
|
|||||||
media_obj = self.channel
|
media_obj = self.channel
|
||||||
if self.birding_location:
|
if self.birding_location:
|
||||||
media_obj = self.birding_location
|
media_obj = self.birding_location
|
||||||
if self.disc_golf:
|
if self.disc_golf_course:
|
||||||
media_obj = self.disc_golf
|
media_obj = self.disc_golf_course
|
||||||
return media_obj
|
return media_obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -2021,7 +2021,7 @@ class FavoriteMedia(TimeStampedModel):
|
|||||||
"Mood": "mood",
|
"Mood": "mood",
|
||||||
"BrickSet": "brick_set",
|
"BrickSet": "brick_set",
|
||||||
"BirdingLocation": "birding_location",
|
"BirdingLocation": "birding_location",
|
||||||
"DiscGolf": "disc_golf",
|
"DiscGolfCourse": "disc_golf_course",
|
||||||
}
|
}
|
||||||
|
|
||||||
fk = fk_map.get(media_type)
|
fk = fk_map.get(media_type)
|
||||||
|
|||||||
@ -1115,7 +1115,7 @@ def toggle_favorite(request, media_type, object_id):
|
|||||||
"Mood": ("moods", "Mood"),
|
"Mood": ("moods", "Mood"),
|
||||||
"BrickSet": ("bricksets", "BrickSet"),
|
"BrickSet": ("bricksets", "BrickSet"),
|
||||||
"BirdingLocation": ("birds", "BirdingLocation"),
|
"BirdingLocation": ("birds", "BirdingLocation"),
|
||||||
"DiscGolf": ("discgolf", "DiscGolfCourse"),
|
"DiscGolfCourse": ("discgolf", "DiscGolfCourse"),
|
||||||
}
|
}
|
||||||
|
|
||||||
app_label, model_name = app_model_map.get(media_type, (None, None))
|
app_label, model_name = app_model_map.get(media_type, (None, None))
|
||||||
|
|||||||
@ -82,8 +82,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3><a href="{% url 'discgolf:course_list' %}">Disc Golf</a></h3>
|
<h3><a href="{% url 'discgolf:course_list' %}">Disc Golf</a></h3>
|
||||||
{% if DiscGolf %}
|
{% if DiscGolfCourse %}
|
||||||
{% with scrobbles=DiscGolf count=DiscGolf_count time=DiscGolf_time %}
|
{% with scrobbles=DiscGolfCourse count=DiscGolfCourse_count time=DiscGolfCourse_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
Reference in New Issue
Block a user