dataclasses.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. from functools import cached_property
  2. import inspect
  3. import json
  4. from dataclasses import asdict, dataclass
  5. from typing import Optional
  6. from dataclass_wizard import JSONWizard
  7. from django.contrib.auth import get_user_model
  8. from locations.models import GeoLocation
  9. User = get_user_model()
  10. class ScrobbleLogDataEncoder(json.JSONEncoder):
  11. def default(self, o):
  12. return o.__dict__
  13. class ScrobbleLogDataDecoder(json.JSONDecoder):
  14. def default(self, o):
  15. return o.__dict__
  16. class JSONDataclass(JSONWizard):
  17. @property
  18. def asdict(self):
  19. return asdict(self)
  20. @property
  21. def json(self):
  22. return json.dumps(self.asdict)
  23. @dataclass
  24. class ScrobbleLogData(JSONDataclass):
  25. description: Optional[str] = None
  26. class LongPlayLogData(JSONDataclass):
  27. serial_scrobble_id: Optional[int]
  28. long_play_complete: bool = False
  29. class WithOthersLogData(JSONDataclass):
  30. with_user_ids: Optional[list[int]] = None
  31. with_names_str: Optional[list[str]] = None
  32. @property
  33. def with_names(self) -> list[str]:
  34. with_names = []
  35. if self.with_user_ids:
  36. with_names += [u.full_name for u in self.with_users if u]
  37. if self.with_names_str:
  38. with_names += [u for u in self.with_names_str]
  39. return with_names
  40. @property
  41. def with_users(self) -> list[User]:
  42. with_users = []
  43. if self.with_user_ids:
  44. with_users = [
  45. User.objects.filter(id=i).first() for i in self.with_user_ids
  46. ]
  47. return with_users
  48. @dataclass
  49. class BoardGameScoreLogData(JSONDataclass):
  50. user_id: Optional[int] = None
  51. name_str: str = ""
  52. bgg_username: str = ""
  53. color: Optional[str] = None
  54. character: Optional[str] = None
  55. team: Optional[str] = None
  56. score: Optional[int] = None
  57. win: Optional[bool] = None
  58. new: Optional[bool] = None
  59. @property
  60. def user(self) -> Optional[User]:
  61. user = None
  62. if self.user_id:
  63. user = User.objects.filter(id=self.user_id).first()
  64. return user
  65. @property
  66. def name(self) -> str:
  67. name = self.name_str
  68. if self.user_id:
  69. name = self.user.first_name
  70. return name
  71. def __str__(self) -> str:
  72. out = self.name
  73. if self.score:
  74. out += f" {self.score}"
  75. if self.color:
  76. out += f" ({self.color})"
  77. if self.win:
  78. out += f" [W]"
  79. return out
  80. @dataclass
  81. class BoardGameLogData(LongPlayLogData):
  82. serial_scrobble_id: Optional[int] = None
  83. long_play_complete: Optional[bool] = None
  84. players: Optional[list[BoardGameScoreLogData]] = None
  85. location: Optional[str] = None
  86. geo_location_id: Optional[int] = None
  87. difficulty: Optional[int] = None
  88. solo: Optional[bool] = None
  89. two_handed: Optional[bool] = None
  90. @cached_property
  91. def geo_location(self) -> Optional[GeoLocation]:
  92. if self.geo_location_id:
  93. return GeoLocation.objects.filter(id=self.geo_location_id).first()
  94. @dataclass
  95. class BookPageLogData(JSONDataclass):
  96. page_number: Optional[int] = None
  97. end_ts: Optional[int] = None
  98. start_ts: Optional[int] = None
  99. duration: Optional[int] = None
  100. @dataclass
  101. class BookLogData(LongPlayLogData):
  102. long_play_complete: Optional[bool] = None
  103. koreader_hash: Optional[str] = None
  104. page_data: Optional[dict[int, BookPageLogData]] = None
  105. pages_read: Optional[int] = None
  106. page_start: Optional[int] = None
  107. page_end: Optional[int] = None
  108. serial_scrobble_id: Optional[int] = None
  109. @dataclass
  110. class LifeEventLogData(WithOthersLogData):
  111. with_user_ids: Optional[list[int]] = None
  112. with_names_str: Optional[list[str]] = None
  113. location: Optional[str] = None
  114. geo_location_id: Optional[int] = None
  115. details: Optional[str] = None
  116. def geo_location(self):
  117. return GeoLocation.objects.filter(id=self.geo_location_id).first()
  118. @dataclass
  119. class MoodLogData(JSONDataclass):
  120. reasons: Optional[str] = None
  121. @dataclass
  122. class VideoLogData(JSONDataclass):
  123. title: str
  124. video_type: str
  125. run_time_seconds: int
  126. kind: str
  127. year: Optional[int]
  128. episode_number: Optional[int] = None
  129. source_url: Optional[str] = None
  130. imdbID: Optional[str] = None
  131. season_number: Optional[int] = None
  132. cover_url: Optional[str] = None
  133. next_imdb_id: Optional[int] = None
  134. tv_series_id: Optional[str] = None
  135. @dataclass
  136. class VideoGameLogData(LongPlayLogData):
  137. serial_scrobble_id: Optional[int] = None
  138. long_play_complete: Optional[bool] = False
  139. console: Optional[str] = None
  140. emulated: Optional[bool] = False
  141. emulator: Optional[str] = None
  142. @dataclass
  143. class BrickSetLogData(LongPlayLogData, WithOthersLogData):
  144. serial_scrobble_id: Optional[int]
  145. long_play_complete: bool = False
  146. with_user_ids: Optional[list[int]] = None
  147. with_names_str: Optional[list[str]] = None
  148. @dataclass
  149. class TrailLogData(WithOthersLogData):
  150. with_user_ids: Optional[list[int]] = None
  151. with_names_str: Optional[list[str]] = None
  152. details: Optional[str] = None
  153. effort: Optional[str] = None
  154. difficulty: Optional[str] = None
  155. @dataclass
  156. class BeerLogData(WithOthersLogData):
  157. with_user_ids: Optional[list[int]] = None
  158. with_names_str: Optional[list[str]] = None
  159. details: Optional[str] = None
  160. rating: Optional[str] = None
  161. notes: Optional[str] = None
  162. @dataclass
  163. class FoodLogData(JSONDataclass):
  164. meal: Optional[str] = None
  165. details: Optional[str] = None
  166. rating: Optional[str] = None
  167. notes: Optional[str] = None
  168. @dataclass
  169. class PuzzleLogData(JSONDataclass):
  170. with_others: Optional[str] = None
  171. rating: Optional[str] = None
  172. notes: Optional[str] = None