Files
vrobbler/vrobbler/apps/profiles/forms.py
Colin Powell c3673e2679 [nature] Add iNaturalist observation importer and scrobbles
- New nature app with SpeciesObservation model (scrobblable)
- SpeciesObservationLogData for observation-specific fields
- Atom feed parser + iNat REST API enrichment
- Photo download and local storage
- Celery periodic import (every 4 hours)
- Management command: import_from_inaturalist
- Species fields: title, scientific_name, taxon_id, iconic_taxon_name, rank, wikipedia_url
- Log fields: observation_id, photo_urls, quality_grade, place_guess, lat/lon, uri, identifications_count
- Updates existing scrobbles when observation data changes
- iNaturalist user mapping on UserProfile
- Templates, admin, API, URLs
- Default 5 minute run time for species observations
2026-07-16 23:43:21 -04:00

104 lines
3.3 KiB
Python

from django import forms
from profiles.models import UserProfile
from scrobbles.constants import Visibility
from scrobbles.models import Scrobble
class UserProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
self.profile = UserProfile.objects.filter(user=self.request.user).first()
if not self.profile:
raise Exception
super(UserProfileForm, self).__init__(*args, instance=self.profile, **kwargs)
class Meta:
model = UserProfile
fields = [
"timezone",
"lastfm_username",
"lastfm_password",
"lastfm_auto_import",
"retroarch_path",
"retroarch_auto_import",
"archivebox_username",
"archivebox_password",
"archivebox_url",
"bgg_username",
"lichess_username",
"inaturalist_username",
"inaturalist_user_id",
"inaturalist_auto_import",
"webdav_url",
"webdav_user",
"webdav_pass",
"webdav_auto_import",
"ntfy_url",
"ntfy_enabled",
"mopidy_api_url",
"favorites_mopidy_playlist",
"monthly_mopidy_playlist_pattern",
"redirect_to_webpage",
"enable_public_widgets",
"widget_custom_css",
"home_scrobble_limit",
"live_now_playing",
"weigh_in_units",
"volume_unit",
"default_water_format",
"default_water_size",
"water_reminder_enabled",
"trends_disabled",
]
widgets = {
"lastfm_password": forms.PasswordInput(render_value=True),
"archivebox_password": forms.PasswordInput(render_value=True),
"webdav_pass": forms.PasswordInput(render_value=True),
}
MEDIA_TYPE_LABELS = {mt.value: mt.label for mt in Scrobble.MediaType}
INHERIT = ""
class BulkVisibilityForm(forms.Form):
bulk_action = forms.ChoiceField(
choices=[
(Visibility.PUBLIC, "Public"),
(Visibility.PRIVATE, "Private"),
],
widget=forms.RadioSelect,
required=False,
label="Set all non-shared scrobbles to",
)
def __init__(self, *args, **kwargs):
self.profile = kwargs.pop("profile")
super().__init__(*args, **kwargs)
media_types = Scrobble.MediaType.values
choices = [
(Visibility.PUBLIC, "Public"),
(Visibility.SHARED, "Shared"),
(Visibility.PRIVATE, "Private"),
]
existing_overrides = self.profile.media_type_visibility or {}
for mt in sorted(media_types):
label = MEDIA_TYPE_LABELS.get(mt, mt)
self.fields[f"media_type_{mt}"] = forms.ChoiceField(
choices=choices,
required=False,
label=label,
initial=existing_overrides.get(mt, Visibility.PRIVATE),
)
def clean(self):
cleaned = super().clean()
overrides = {}
for mt in Scrobble.MediaType.values:
val = cleaned.get(f"media_type_{mt}")
if val:
overrides[mt] = val
cleaned["media_type_visibility"] = overrides
return cleaned