|
@@ -1,5 +1,3 @@
|
|
|
-from functools import cached_property
|
|
|
-import inspect
|
|
|
import json
|
|
|
from dataclasses import asdict, dataclass
|
|
|
from typing import Optional
|
|
@@ -33,214 +31,25 @@ class JSONDataclass(JSONWizard):
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
-class ScrobbleLogData(JSONDataclass):
|
|
|
- description: Optional[str] = None
|
|
|
-
|
|
|
-
|
|
|
-class LongPlayLogData(JSONDataclass):
|
|
|
- serial_scrobble_id: Optional[int]
|
|
|
- long_play_complete: bool = False
|
|
|
-
|
|
|
-
|
|
|
-class WithOthersLogData(JSONDataclass):
|
|
|
- with_user_ids: Optional[list[int]] = None
|
|
|
- with_names_str: Optional[list[str]] = None
|
|
|
-
|
|
|
- @property
|
|
|
- def with_names(self) -> list[str]:
|
|
|
- with_names = []
|
|
|
- if self.with_user_ids:
|
|
|
- with_names += [u.full_name for u in self.with_users if u]
|
|
|
- if self.with_names_str:
|
|
|
- with_names += [u for u in self.with_names_str]
|
|
|
- return with_names
|
|
|
-
|
|
|
- @property
|
|
|
- def with_users(self) -> list[User]:
|
|
|
- with_users = []
|
|
|
- if self.with_user_ids:
|
|
|
- with_users = [
|
|
|
- User.objects.filter(id=i).first() for i in self.with_user_ids
|
|
|
- ]
|
|
|
- return with_users
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class BoardGameScoreLogData(JSONDataclass):
|
|
|
- person_id: Optional[int] = None
|
|
|
- bgg_username: str = ""
|
|
|
- color: Optional[str] = None
|
|
|
- character: Optional[str] = None
|
|
|
- team: Optional[str] = None
|
|
|
- score: Optional[int] = None
|
|
|
- win: Optional[bool] = None
|
|
|
- new: Optional[bool] = None
|
|
|
- rank: Optional[int] = None
|
|
|
- seat_order: Optional[int] = None
|
|
|
- role: Optional[str] = None
|
|
|
- rank: Optional[int] = None
|
|
|
- seat_order: Optional[int] = None
|
|
|
- role: Optional[str] = None
|
|
|
- lichess_username: Optional[str] = None
|
|
|
- # Legacy
|
|
|
- user_id: Optional[int] = None
|
|
|
- name_str: Optional[str] = None
|
|
|
-
|
|
|
-
|
|
|
- @property
|
|
|
- def person(self) -> Optional[Person]:
|
|
|
- return Person.objects.filter(id=self.person_id).first()
|
|
|
-
|
|
|
- @property
|
|
|
- def name(self) -> str:
|
|
|
- name = ""
|
|
|
- if self.person:
|
|
|
- name = self.person.name
|
|
|
- return name
|
|
|
-
|
|
|
- def __str__(self) -> str:
|
|
|
- out = self.name
|
|
|
- if self.score:
|
|
|
- out += f" {self.score}"
|
|
|
- if self.color:
|
|
|
- out += f" ({self.color})"
|
|
|
- if self.win:
|
|
|
- out += f" [W]"
|
|
|
- return out
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class BoardGameLogData(LongPlayLogData):
|
|
|
- serial_scrobble_id: Optional[int] = None
|
|
|
- long_play_complete: Optional[bool] = None
|
|
|
- players: Optional[list[BoardGameScoreLogData]] = None
|
|
|
- location_id: Optional[int] = None
|
|
|
- difficulty: Optional[int] = None
|
|
|
- solo: Optional[bool] = None
|
|
|
- two_handed: Optional[bool] = None
|
|
|
- expansion_ids: Optional[int] = None
|
|
|
- moves: Optional[list] = None
|
|
|
- rated: Optional[str] = None
|
|
|
- speed: Optional[str] = None
|
|
|
- variant: Optional[str] = None
|
|
|
- lichess_id: Optional[int] = None
|
|
|
- board: Optional[str] = None
|
|
|
- rounds: Optional[int] = None
|
|
|
- details: Optional[str] = None
|
|
|
- # Legacy
|
|
|
- learning: Optional[bool] = None
|
|
|
- location: Optional[str] = None
|
|
|
- geo_location_id: Optional[int] = None
|
|
|
- scenario: Optional[str] = None
|
|
|
- @cached_property
|
|
|
- def geo_location(self) -> Optional[GeoLocation]:
|
|
|
- if self.geo_location_id:
|
|
|
- return GeoLocation.objects.filter(id=self.geo_location_id).first()
|
|
|
-
|
|
|
- @cached_property
|
|
|
- def player_log(self) -> str:
|
|
|
- if self.players:
|
|
|
- return ", ".join([BoardGameScoreLogData(**player).__str__() for player in self.players])
|
|
|
- return ""
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class BookPageLogData(JSONDataclass):
|
|
|
- page_number: Optional[int] = None
|
|
|
- end_ts: Optional[int] = None
|
|
|
- start_ts: Optional[int] = None
|
|
|
- duration: Optional[int] = None
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class BookLogData(LongPlayLogData):
|
|
|
- long_play_complete: Optional[bool] = None
|
|
|
- koreader_hash: Optional[str] = None
|
|
|
- page_data: Optional[dict[int, BookPageLogData]] = None
|
|
|
- pages_read: Optional[int] = None
|
|
|
- page_start: Optional[int] = None
|
|
|
- page_end: Optional[int] = None
|
|
|
- serial_scrobble_id: Optional[int] = None
|
|
|
- details: Optional[str] = None
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class LifeEventLogData(WithOthersLogData):
|
|
|
- with_user_ids: Optional[list[int]] = None
|
|
|
- with_names_str: Optional[list[str]] = None
|
|
|
- location: Optional[str] = None
|
|
|
- geo_location_id: Optional[int] = None
|
|
|
+class BaseLogData(JSONDataclass):
|
|
|
details: Optional[str] = None
|
|
|
-
|
|
|
- def geo_location(self):
|
|
|
- return GeoLocation.objects.filter(id=self.geo_location_id).first()
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class MoodLogData(JSONDataclass):
|
|
|
- reasons: Optional[str] = None
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class VideoLogData(JSONDataclass):
|
|
|
- title: str
|
|
|
- video_type: str
|
|
|
- run_time_seconds: int
|
|
|
- kind: str
|
|
|
- year: Optional[int]
|
|
|
- episode_number: Optional[int] = None
|
|
|
- source_url: Optional[str] = None
|
|
|
- imdbID: Optional[str] = None
|
|
|
- season_number: Optional[int] = None
|
|
|
- cover_url: Optional[str] = None
|
|
|
- next_imdb_id: Optional[int] = None
|
|
|
- tv_series_id: Optional[str] = None
|
|
|
+ notes: Optional[str] = None
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
-class VideoGameLogData(LongPlayLogData):
|
|
|
+class LongPlayLogData(JSONDataclass):
|
|
|
+ complete: Optional[bool] = None
|
|
|
serial_scrobble_id: Optional[int] = None
|
|
|
- long_play_complete: Optional[bool] = False
|
|
|
- console: Optional[str] = None
|
|
|
- emulated: Optional[bool] = False
|
|
|
- emulator: Optional[str] = None
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
-class BrickSetLogData(LongPlayLogData, WithOthersLogData):
|
|
|
- serial_scrobble_id: Optional[int]
|
|
|
- long_play_complete: bool = False
|
|
|
- with_user_ids: Optional[list[int]] = None
|
|
|
- with_names_str: Optional[list[str]] = None
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class TrailLogData(WithOthersLogData):
|
|
|
- with_user_ids: Optional[list[int]] = None
|
|
|
- with_names_str: Optional[list[str]] = None
|
|
|
- details: Optional[str] = None
|
|
|
- effort: Optional[str] = None
|
|
|
- difficulty: Optional[str] = None
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class BeerLogData(WithOthersLogData):
|
|
|
- with_user_ids: Optional[list[int]] = None
|
|
|
- with_names_str: Optional[list[str]] = None
|
|
|
- details: Optional[str] = None
|
|
|
- rating: Optional[str] = None
|
|
|
- notes: Optional[str] = None
|
|
|
-
|
|
|
-
|
|
|
-@dataclass
|
|
|
-class FoodLogData(JSONDataclass):
|
|
|
- meal: Optional[str] = None
|
|
|
- details: Optional[str] = None
|
|
|
- rating: Optional[str] = None
|
|
|
- notes: Optional[str] = None
|
|
|
+class WithPeopleLogData(JSONDataclass):
|
|
|
+ with_people_ids: Optional[list[int]] = None
|
|
|
|
|
|
+ @property
|
|
|
+ def with_people(self) -> list["Person"]:
|
|
|
+ from people.models import Person
|
|
|
|
|
|
-@dataclass
|
|
|
-class PuzzleLogData(JSONDataclass):
|
|
|
- with_others: Optional[str] = None
|
|
|
- rating: Optional[str] = None
|
|
|
- notes: Optional[str] = None
|
|
|
+ if not self.with_people_ids:
|
|
|
+ return []
|
|
|
+ return [Person.objects.filter(id=pid) for pid in self.with_people_ids]
|