artemis-model 0.1.89__tar.gz → 0.1.90__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {artemis_model-0.1.89 → artemis_model-0.1.90}/PKG-INFO +2 -1
  2. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/album.py +4 -8
  3. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/artist.py +1 -3
  4. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/auth.py +6 -12
  5. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/base.py +14 -6
  6. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/dj_set.py +7 -15
  7. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/genre.py +2 -7
  8. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/location.py +3 -9
  9. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/message.py +12 -3
  10. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/organization.py +2 -6
  11. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/otp.py +2 -7
  12. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/permission.py +7 -13
  13. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/playlist.py +10 -28
  14. artemis_model-0.1.90/artemis_model/redis/__init__.py +6 -0
  15. artemis_model-0.1.90/artemis_model/redis/device.py +22 -0
  16. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/redis/zone_state.py +1 -4
  17. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/setting.py +20 -19
  18. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/track.py +6 -18
  19. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/user.py +4 -12
  20. {artemis_model-0.1.89 → artemis_model-0.1.90}/pyproject.toml +2 -1
  21. artemis_model-0.1.89/artemis_model/redis/__init__.py +0 -5
  22. {artemis_model-0.1.89 → artemis_model-0.1.90}/README.md +0 -0
  23. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/__init__.py +0 -0
  24. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/category.py +0 -0
  25. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/schedule.py +0 -0
  26. {artemis_model-0.1.89 → artemis_model-0.1.90}/artemis_model/zone.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: artemis-model
3
- Version: 0.1.89
3
+ Version: 0.1.90
4
4
  Summary:
5
5
  Author: Jukeboxy
6
6
  Requires-Python: >=3.10.6,<4.0.0
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.13
11
11
  Requires-Dist: alembic (>=1.13.1,<2.0.0)
12
12
  Requires-Dist: asyncpg (>=0.29.0,<0.30.0)
13
13
  Requires-Dist: greenlet (>=3.0.2,<4.0.0)
14
+ Requires-Dist: pydantic (>=2.10.2,<3.0.0)
14
15
  Requires-Dist: python-slugify (>=8.0.4,<9.0.0)
15
16
  Requires-Dist: sqlalchemy (>=2.0.23,<3.0.0)
16
17
  Description-Content-Type: text/markdown
@@ -19,9 +19,7 @@ class AlbumMixin(TimeStampMixin):
19
19
  id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
20
20
  name: Mapped[str] = mapped_column(nullable=False)
21
21
  description: Mapped[str | None] = mapped_column(nullable=True)
22
- entry_date: Mapped[datetime] = mapped_column(
23
- nullable=False, default=datetime.utcnow
24
- )
22
+ entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
25
23
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
26
24
  legacy_id: Mapped[str] = mapped_column(nullable=False)
27
25
  is_internal: Mapped[bool] = mapped_column(nullable=False, default=False)
@@ -45,7 +43,7 @@ class AlbumMixin(TimeStampMixin):
45
43
  "Track",
46
44
  back_populates="album",
47
45
  cascade="all, delete-orphan",
48
- )
46
+ )
49
47
 
50
48
 
51
49
  class AlbumSync(CustomSyncBase, AlbumMixin):
@@ -56,10 +54,8 @@ class Album(CustomBase, AlbumMixin):
56
54
  pass
57
55
 
58
56
 
59
- class AlbumArtistAssocMixin():
60
- album_id: Mapped[int] = mapped_column(
61
- ForeignKey("album.id"), primary_key=True, nullable=False
62
- )
57
+ class AlbumArtistAssocMixin:
58
+ album_id: Mapped[int] = mapped_column(ForeignKey("album.id"), primary_key=True, nullable=False)
63
59
  artist_id: Mapped[int] = mapped_column(
64
60
  ForeignKey("artist.id"), primary_key=True, nullable=False
65
61
  )
@@ -18,9 +18,7 @@ def to_tsvector_ix(*columns):
18
18
  class ArtistMixin(TimeStampMixin):
19
19
  id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
20
20
  name: Mapped[str] = mapped_column(nullable=False)
21
- entry_date: Mapped[datetime] = mapped_column(
22
- nullable=False, default=datetime.utcnow
23
- )
21
+ entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
24
22
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
25
23
  is_internal: Mapped[bool] = mapped_column(nullable=False, default=False)
26
24
  legacy_id: Mapped[str] = mapped_column(nullable=False)
@@ -1,4 +1,5 @@
1
1
  """Auth models."""
2
+
2
3
  import uuid
3
4
  from datetime import datetime
4
5
  from typing import Optional
@@ -16,9 +17,7 @@ class UserUnverifiedAccountMixin(TimeStampMixin):
16
17
  been verified. Once the user has been verified, the account will be moved to the UserAccount table.
17
18
  """
18
19
 
19
- id: Mapped[uuid.UUID] = mapped_column(
20
- UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
21
- )
20
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
22
21
  email: Mapped[str] = mapped_column(index=True)
23
22
  name: Mapped[str] = mapped_column(nullable=False)
24
23
  mobile: Mapped[str] = mapped_column(nullable=True, index=True)
@@ -43,14 +42,10 @@ class UserAccountMixin(TimeStampMixin):
43
42
  This table is used to store the account info of users who have been verified.
44
43
  """
45
44
 
46
- id: Mapped[uuid.UUID] = mapped_column(
47
- UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
48
- )
45
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
49
46
  email: Mapped[str] = mapped_column(index=True, unique=True)
50
47
  name: Mapped[str] = mapped_column(nullable=False)
51
- mobile: Mapped[Optional[str]] = mapped_column(
52
- nullable=True, unique=True, index=True
53
- )
48
+ mobile: Mapped[Optional[str]] = mapped_column(nullable=True, unique=True, index=True)
54
49
  password = mapped_column(LargeBinary, nullable=True)
55
50
  provider: Mapped[str] = mapped_column(nullable=False)
56
51
  oauth_id: Mapped[Optional[str]] = mapped_column(nullable=True, index=True)
@@ -84,9 +79,8 @@ class LoginHistoryMixin:
84
79
  """
85
80
  This table is used to store the login history of users.
86
81
  """
87
- id: Mapped[int] = mapped_column(
88
- primary_key=True, autoincrement=True, nullable=False
89
- )
82
+
83
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, nullable=False)
90
84
  account_id: Mapped[uuid.UUID] = mapped_column(
91
85
  ForeignKey("user_account.id"), nullable=False, index=True
92
86
  )
@@ -2,8 +2,14 @@ import re
2
2
  from datetime import datetime
3
3
  from typing import Any
4
4
 
5
- from sqlalchemy.orm import (DeclarativeBase, Mapped, declared_attr,
6
- mapped_column, object_session, relationship)
5
+ from sqlalchemy.orm import (
6
+ DeclarativeBase,
7
+ Mapped,
8
+ declared_attr,
9
+ mapped_column,
10
+ object_session,
11
+ relationship,
12
+ )
7
13
  from sqlalchemy.ext.asyncio import AsyncAttrs
8
14
  from sqlalchemy import Column, Uuid, event, inspect, TypeDecorator
9
15
  from sqlalchemy.dialects.postgresql import TSVECTOR
@@ -45,8 +51,9 @@ class CustomBase(DeclarativeBase, AsyncAttrs):
45
51
  for key in self.__repr_attrs__:
46
52
  if not hasattr(self, key):
47
53
  raise KeyError(
48
- "{} has incorrect attribute '{}' in "
49
- "__repr__attrs__".format(self.__class__, key)
54
+ "{} has incorrect attribute '{}' in " "__repr__attrs__".format(
55
+ self.__class__, key
56
+ )
50
57
  )
51
58
  value = getattr(self, key)
52
59
  wrap_in_quote = isinstance(value, str)
@@ -191,8 +198,9 @@ class CustomSyncBase(DeclarativeBase):
191
198
  for key in self.__repr_attrs__:
192
199
  if not hasattr(self, key):
193
200
  raise KeyError(
194
- "{} has incorrect attribute '{}' in "
195
- "__repr__attrs__".format(self.__class__, key)
201
+ "{} has incorrect attribute '{}' in " "__repr__attrs__".format(
202
+ self.__class__, key
203
+ )
196
204
  )
197
205
  value = getattr(self, key)
198
206
  wrap_in_quote = isinstance(value, str)
@@ -37,9 +37,7 @@ class DjSetMixin(TimeStampMixin, AuditMixin):
37
37
  organization_id: Mapped[uuid.UUID] = mapped_column(
38
38
  ForeignKey("organization.id"), nullable=True, index=True
39
39
  )
40
- entry_date: Mapped[datetime] = mapped_column(
41
- nullable=False, default=datetime.utcnow
42
- )
40
+ entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
43
41
  cover_image: Mapped[str] = mapped_column(nullable=True)
44
42
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
45
43
  legacy_id: Mapped[str] = mapped_column(nullable=True)
@@ -62,7 +60,9 @@ class DjSetMixin(TimeStampMixin, AuditMixin):
62
60
  return association_proxy(
63
61
  "dj_set_genre_associations",
64
62
  "genre",
65
- creator=lambda genre: DjSetGenreAssoc(genre_id=genre.get("id"), weight=genre.get("weight", 0)),
63
+ creator=lambda genre: DjSetGenreAssoc(
64
+ genre_id=genre.get("id"), weight=genre.get("weight", 0)
65
+ ),
66
66
  )
67
67
 
68
68
  @declared_attr
@@ -71,24 +71,16 @@ class DjSetMixin(TimeStampMixin, AuditMixin):
71
71
 
72
72
  @declared_attr
73
73
  def dj_set_track_associations(cls) -> Mapped[list["DjSetTrackAssoc"]]:
74
- return relationship(
75
- cascade="all, delete-orphan"
76
- )
74
+ return relationship(cascade="all, delete-orphan")
77
75
 
78
76
  @declared_attr
79
77
  def genres(cls) -> Mapped[list["Genre"]]:
80
- return relationship(
81
- secondary="dj_set_genre_assoc",
82
- lazy="joined",
83
- viewonly=True
84
- )
78
+ return relationship(secondary="dj_set_genre_assoc", lazy="joined", viewonly=True)
85
79
 
86
80
 
87
81
  @declared_attr
88
82
  def tracks(cls) -> Mapped[list["Track"]]:
89
- return relationship(
90
- secondary="dj_set_track_assoc", viewonly=True
91
- )
83
+ return relationship(secondary="dj_set_track_assoc", viewonly=True)
92
84
 
93
85
 
94
86
  class DjSetSync(CustomSyncBase, DjSetMixin):
@@ -1,4 +1,3 @@
1
-
2
1
  from sqlalchemy import Computed, func, literal, text
3
2
  from sqlalchemy.orm import Mapped, mapped_column, relationship
4
3
 
@@ -26,15 +25,11 @@ class GenreMixin(TimeStampMixin):
26
25
 
27
26
  @declared_attr
28
27
  def tracks(cls) -> Mapped[list["Track"]]:
29
- return relationship(
30
- "Track", secondary="track_genre_assoc", back_populates="genres"
31
- )
28
+ return relationship("Track", secondary="track_genre_assoc", back_populates="genres")
32
29
 
33
30
  @declared_attr
34
31
  def dj_set_genre_associations(cls) -> Mapped[list["DjSetGenreAssoc"]]:
35
- return relationship(
36
- back_populates="genre", cascade="all, delete-orphan"
37
- )
32
+ return relationship(back_populates="genre", cascade="all, delete-orphan")
38
33
 
39
34
  def dj_sets(cls) -> Mapped[list["DjSet"]]:
40
35
  return relationship("DjSet", secondary="dj_set_genre_assoc", back_populates="genre")
@@ -38,9 +38,7 @@ class Address(CustomBase, AddressMixin):
38
38
 
39
39
 
40
40
  class LocationMixin(TimeStampMixin):
41
- id: Mapped[uuid.UUID] = mapped_column(
42
- UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
43
- )
41
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
44
42
  name: Mapped[str] = mapped_column(nullable=False)
45
43
  slug: Mapped[str] = mapped_column(nullable=False)
46
44
  organization_id: Mapped[uuid.UUID] = mapped_column(
@@ -113,9 +111,7 @@ listen(Location.name, "set", generate_slug)
113
111
 
114
112
  class LocationGroupMixin(TimeStampMixin):
115
113
  id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True)
116
- organization_id = mapped_column(
117
- ForeignKey("organization.id"), nullable=False, index=True
118
- )
114
+ organization_id = mapped_column(ForeignKey("organization.id"), nullable=False, index=True)
119
115
  name: Mapped[str] = mapped_column(nullable=False)
120
116
  description: Mapped[str] = mapped_column(nullable=True)
121
117
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
@@ -136,9 +132,7 @@ class LocationGroup(CustomBase, LocationGroupMixin):
136
132
 
137
133
 
138
134
  class UserLocationAssocMixin(TimeStampMixin):
139
- location_id: Mapped[uuid.UUID] = mapped_column(
140
- ForeignKey("location.id"), primary_key=True
141
- )
135
+ location_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("location.id"), primary_key=True)
142
136
  user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id"), primary_key=True)
143
137
 
144
138
  @declared_attr
@@ -12,13 +12,18 @@ class MessageMixin(TimeStampMixin, AuditMixin):
12
12
  """
13
13
  This table is used to store the message.
14
14
  """
15
+
15
16
  id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True, index=True)
16
17
  name: Mapped[str] = mapped_column(nullable=False)
17
18
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
18
19
  s3_link: Mapped[str] = mapped_column(nullable=False)
19
20
 
20
- organization_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("organization.id"), nullable=False, index=True)
21
- message_group_id: Mapped[int] = mapped_column(ForeignKey("message_group.id"), nullable=True, index=True, default=None)
21
+ organization_id: Mapped[uuid.UUID] = mapped_column(
22
+ ForeignKey("organization.id"), nullable=False, index=True
23
+ )
24
+ message_group_id: Mapped[int] = mapped_column(
25
+ ForeignKey("message_group.id"), nullable=True, index=True, default=None
26
+ )
22
27
 
23
28
  @declared_attr
24
29
  def organization(cls) -> Mapped["Organization"]:
@@ -47,6 +52,7 @@ class MessagePlayDetailMixin(TimeStampMixin, AuditMixin):
47
52
  Frequency is used only for rotated messages.
48
53
  Timesheet is used only for scheduled messages.
49
54
  """
55
+
50
56
  id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True, index=True)
51
57
  message_id: Mapped[int] = mapped_column(ForeignKey("message.id"), nullable=False, index=True)
52
58
  zone_id: Mapped[int] = mapped_column(ForeignKey("zone.id"), index=True)
@@ -76,9 +82,12 @@ class MessageGroupMixin(TimeStampMixin, AuditMixin):
76
82
  """
77
83
  This table is used to store the message group.
78
84
  """
85
+
79
86
  id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True, index=True)
80
87
  name: Mapped[str] = mapped_column(nullable=False)
81
- organization_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("organization.id"), nullable=False, index=True)
88
+ organization_id: Mapped[uuid.UUID] = mapped_column(
89
+ ForeignKey("organization.id"), nullable=False, index=True
90
+ )
82
91
 
83
92
  @declared_attr
84
93
  def organization(cls) -> Mapped["Organization"]:
@@ -12,9 +12,7 @@ from sqlalchemy.ext.declarative import declared_attr
12
12
 
13
13
 
14
14
  class OrganizationMixin(TimeStampMixin):
15
- id: Mapped[uuid.UUID] = mapped_column(
16
- UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
17
- )
15
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
18
16
  name: Mapped[str] = mapped_column(nullable=False)
19
17
  description: Mapped[str | None] = mapped_column(nullable=True)
20
18
  slug: Mapped[str] = mapped_column(nullable=False, index=True)
@@ -30,9 +28,7 @@ class OrganizationMixin(TimeStampMixin):
30
28
 
31
29
  @declared_attr
32
30
  def users(cls) -> Mapped[List["User"]]:
33
- return relationship(
34
- secondary="organization_user_assoc", back_populates="organizations"
35
- )
31
+ return relationship(secondary="organization_user_assoc", back_populates="organizations")
36
32
 
37
33
  @declared_attr
38
34
  def messages(cls) -> Mapped[List["Message"]]:
@@ -1,4 +1,3 @@
1
-
2
1
  import uuid
3
2
  from datetime import datetime
4
3
  from sqlalchemy.orm import Mapped, mapped_column
@@ -12,18 +11,14 @@ class OtpMixin(TimeStampMixin):
12
11
  requested_by can be an UserAccount or UserUnverifiedAccount id.
13
12
  """
14
13
 
15
- id: Mapped[int] = mapped_column(
16
- primary_key=True, autoincrement=True, nullable=False
17
- )
14
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, nullable=False)
18
15
  code: Mapped[str] = mapped_column(nullable=False)
19
16
  purpose: Mapped[str] = mapped_column(nullable=False)
20
17
  requested_by_id: Mapped[uuid.UUID] = mapped_column(nullable=False, index=True)
21
18
  method: Mapped[str] = mapped_column(nullable=False, index=True)
22
19
  secret: Mapped[str] = mapped_column(nullable=False)
23
20
  created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, index=True)
24
- updated_at: Mapped[datetime] = mapped_column(
25
- default=datetime.utcnow, onupdate=datetime.utcnow
26
- )
21
+ updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
27
22
 
28
23
 
29
24
  class OtpSync(CustomSyncBase, OtpMixin):
@@ -10,27 +10,21 @@ from sqlalchemy.ext.declarative import declared_attr
10
10
 
11
11
 
12
12
  class PermissionMixin(TimeStampMixin):
13
- id: Mapped[str] = mapped_column(
14
- primary_key=True
15
- )
13
+ id: Mapped[str] = mapped_column(primary_key=True)
16
14
  name: Mapped[str] = mapped_column(nullable=False)
17
15
  description: Mapped[str] = mapped_column(nullable=False)
18
- type: Mapped[str] = mapped_column(nullable=False) # music-management, player-management, user-management
16
+ type: Mapped[str] = mapped_column(
17
+ nullable=False
18
+ ) # music-management, player-management, user-management
19
19
 
20
20
  @declared_attr
21
21
  def user_permission_associations(cls) -> Mapped[List["UserPermissionAssoc"]]:
22
- return relationship(
23
- back_populates="permission", cascade="all, delete-orphan"
24
- )
22
+ return relationship(back_populates="permission", cascade="all, delete-orphan")
25
23
 
26
24
 
27
25
  class UserPermissionAssocMixin(TimeStampMixin):
28
- user_id: Mapped[uuid.UUID] = mapped_column(
29
- ForeignKey("user.id"), primary_key=True
30
- )
31
- permission_id: Mapped[uuid.UUID] = mapped_column(
32
- ForeignKey("permission.id"), primary_key=True
33
- )
26
+ user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id"), primary_key=True)
27
+ permission_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("permission.id"), primary_key=True)
34
28
 
35
29
  @declared_attr
36
30
  def permission(cls) -> Mapped["Permission"]:
@@ -16,9 +16,7 @@ def to_tsvector_ix(*columns):
16
16
 
17
17
 
18
18
  class FavoritePlaylistMixin:
19
- playlist_id: Mapped[int] = mapped_column(
20
- ForeignKey("playlist.id"), primary_key=True
21
- )
19
+ playlist_id: Mapped[int] = mapped_column(ForeignKey("playlist.id"), primary_key=True)
22
20
  user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id"), primary_key=True)
23
21
 
24
22
 
@@ -33,9 +31,7 @@ class FavoritePlaylist(CustomBase, FavoritePlaylistMixin):
33
31
  class PlaylistMixin(TimeStampMixin):
34
32
  id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
35
33
  name: Mapped[str] = mapped_column(nullable=False, unique=True)
36
- entry_date: Mapped[datetime] = mapped_column(
37
- nullable=False, default=datetime.utcnow
38
- )
34
+ entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
39
35
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
40
36
  legacy_id: Mapped[str] = mapped_column(nullable=True)
41
37
  is_published: Mapped[bool] = mapped_column(default=False) # legacy isTest
@@ -59,7 +55,7 @@ class PlaylistMixin(TimeStampMixin):
59
55
  "FavoritePlaylist",
60
56
  primaryjoin="and_(FavoritePlaylist.playlist_id == Playlist.id)",
61
57
  viewonly=True,
62
- lazy="select"
58
+ lazy="select",
63
59
  )
64
60
 
65
61
  @hybrid_property
@@ -67,21 +63,15 @@ class PlaylistMixin(TimeStampMixin):
67
63
  return bool(self.favorite_playlist_associations)
68
64
 
69
65
  @declared_attr
70
- def category_playlist_associations(cls) -> Mapped[
71
- list["PlaylistCategoryAssoc"]
72
- ]:
66
+ def category_playlist_associations(cls) -> Mapped[list["PlaylistCategoryAssoc"]]:
73
67
  return relationship(cascade="all, delete-orphan")
74
68
 
75
69
  @declared_attr
76
70
  def track_playlist_associations(cls) -> Mapped[list["PlaylistTrackAssoc"]]:
77
- return relationship(
78
- cascade="all, delete-orphan"
79
- )
71
+ return relationship(cascade="all, delete-orphan")
80
72
 
81
73
  @declared_attr
82
- def organization_playlist_associations(cls) -> Mapped[
83
- list["PlaylistOrganizationAssoc"]
84
- ]:
74
+ def organization_playlist_associations(cls) -> Mapped[list["PlaylistOrganizationAssoc"]]:
85
75
  return relationship(cascade="all, delete-orphan")
86
76
 
87
77
  @declared_attr
@@ -110,15 +100,11 @@ class PlaylistMixin(TimeStampMixin):
110
100
 
111
101
  @declared_attr
112
102
  def tracks(cls) -> Mapped[list["Track"]]:
113
- return relationship(
114
- secondary="playlist_track_assoc", viewonly=True
115
- )
103
+ return relationship(secondary="playlist_track_assoc", viewonly=True)
116
104
 
117
105
  @declared_attr
118
106
  def categories(cls) -> Mapped[list["Category"]]:
119
- return relationship(
120
- secondary="playlist_category_assoc", viewonly=True
121
- )
107
+ return relationship(secondary="playlist_category_assoc", viewonly=True)
122
108
 
123
109
  # __table_args__ = (
124
110
  # Index(
@@ -176,18 +162,14 @@ class PlaylistTrackAssoc(CustomBase, PlaylistTrackAssocMixin):
176
162
 
177
163
 
178
164
  class PlaylistCategoryAssocMixin(TimeStampMixin):
179
- playlist_id = mapped_column(
180
- ForeignKey("playlist.id"), primary_key=True, nullable=False
181
- )
165
+ playlist_id = mapped_column(ForeignKey("playlist.id"), primary_key=True, nullable=False)
182
166
  category_id: Mapped[int] = mapped_column(
183
167
  ForeignKey("category.id"), primary_key=True, nullable=False
184
168
  )
185
169
 
186
170
  @declared_attr
187
171
  def category(cls) -> Mapped["Category"]:
188
- return relationship(
189
- back_populates="category_playlist_associations"
190
- )
172
+ return relationship(back_populates="category_playlist_associations")
191
173
 
192
174
 
193
175
  class PlaylistCategoryAssocSync(CustomSyncBase, PlaylistCategoryAssocMixin):
@@ -0,0 +1,6 @@
1
+ """Redis models."""
2
+
3
+ from .zone_state import ZoneState, NowPlaying, SessionId, BucketId
4
+ from .device import ActiveDevice
5
+
6
+ __all__ = ["ZoneState", "NowPlaying", "SessionId", "BucketId", "ActiveDevice"]
@@ -0,0 +1,22 @@
1
+ """Active device redis model."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import Literal
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class ActiveDevice(BaseModel):
11
+ """Active device schema."""
12
+
13
+ user_id: UUID
14
+ client_id: str
15
+ device_id: str
16
+ mode: Literal["player-mode", "controller-mode"]
17
+ connected_at: datetime | str = Field(
18
+ default_factory=lambda: datetime.now(timezone.utc).isoformat()
19
+ )
20
+
21
+
22
+ __all__ = ["ActiveDevice"]
@@ -1,6 +1,5 @@
1
1
  """Zone state schema."""
2
2
 
3
-
4
3
  from dataclasses import Field
5
4
  from typing import Annotated, Literal
6
5
 
@@ -29,7 +28,6 @@ BucketId = Annotated[
29
28
  ]
30
29
 
31
30
 
32
-
33
31
  class ZoneState(BaseModel):
34
32
  """Zone state schema."""
35
33
 
@@ -55,7 +53,6 @@ class ZoneState(BaseModel):
55
53
  def default_empty_dict_if_none(cls, v):
56
54
  """Override none value to empty dict."""
57
55
  return v or {}
58
-
59
-
56
+
60
57
 
61
58
  __all__ = ["ZoneState", "NowPlaying", "SessionId", "BucketId"]
@@ -8,26 +8,27 @@ from sqlalchemy.ext.declarative import declared_attr
8
8
 
9
9
  class SettingMixin(TimeStampMixin):
10
10
  """
11
- Exclusion Schema
12
- {
13
- "genres": {
14
- "<genre_id>": "<genre_name>",
15
- "<genre_id>": "<genre_name>",
16
- ...
17
- },
18
- "artists": {
19
- "<artist_id>": "<artist_name>",
20
- "<artist_id>": "<artist_name>",
21
- ...
22
- },
23
- "tracks": {
24
- "<track_id>": "<track_name>",
25
- "<track_id>": "<track_name>",
26
- ...
27
- },
28
- "exclude_pal": True/False
29
- }
11
+ Exclusion Schema
12
+ {
13
+ "genres": {
14
+ "<genre_id>": "<genre_name>",
15
+ "<genre_id>": "<genre_name>",
16
+ ...
17
+ },
18
+ "artists": {
19
+ "<artist_id>": "<artist_name>",
20
+ "<artist_id>": "<artist_name>",
21
+ ...
22
+ },
23
+ "tracks": {
24
+ "<track_id>": "<track_name>",
25
+ "<track_id>": "<track_name>",
26
+ ...
27
+ },
28
+ "exclude_pal": True/False
29
+ }
30
30
  """
31
+
31
32
  id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
32
33
  zone_id: Mapped[int] = mapped_column(ForeignKey("zone.id"), index=True)
33
34
  exclusion: Mapped[JSON] = mapped_column(JSON, nullable=True)
@@ -16,17 +16,11 @@ def to_tsvector_ix(*columns):
16
16
 
17
17
 
18
18
  class TrackMixin(TimeStampMixin):
19
- id: Mapped[uuid.UUID] = mapped_column(
20
- UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
21
- )
19
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
22
20
  name: Mapped[str] = mapped_column(nullable=False)
23
21
  album_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("album.id"), nullable=False)
24
- artist_id: Mapped[uuid.UUID] = mapped_column(
25
- ForeignKey("artist.id"), nullable=False
26
- )
27
- entry_date: Mapped[datetime] = mapped_column(
28
- nullable=False, default=datetime.utcnow
29
- )
22
+ artist_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("artist.id"), nullable=False)
23
+ entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
30
24
  disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
31
25
  legacy_id: Mapped[str] = mapped_column(nullable=False)
32
26
  is_internal: Mapped[bool] = mapped_column(nullable=False, default=False)
@@ -42,15 +36,11 @@ class TrackMixin(TimeStampMixin):
42
36
 
43
37
  @declared_attr
44
38
  def track_playlist_associations(cls) -> Mapped[list["PlaylistTrackAssoc"]]:
45
- return relationship(
46
- back_populates="track", cascade="all, delete-orphan"
47
- )
39
+ return relationship(back_populates="track", cascade="all, delete-orphan")
48
40
 
49
41
  @declared_attr
50
42
  def dj_set_track_associations(cls) -> Mapped[list["DjSetTrackAssoc"]]:
51
- return relationship(
52
- back_populates="track", cascade="all, delete-orphan"
53
- )
43
+ return relationship(back_populates="track", cascade="all, delete-orphan")
54
44
 
55
45
  @declared_attr
56
46
  def album(cls) -> Mapped["Album"]:
@@ -62,9 +52,7 @@ class TrackMixin(TimeStampMixin):
62
52
 
63
53
  @declared_attr
64
54
  def genres(cls) -> Mapped[List["Genre"]]:
65
- return relationship(
66
- "Genre", secondary="track_genre_assoc", back_populates="tracks"
67
- )
55
+ return relationship("Genre", secondary="track_genre_assoc", back_populates="tracks")
68
56
 
69
57
  @declared_attr
70
58
  def label(cls) -> Mapped["TrackLabel"]:
@@ -14,9 +14,7 @@ class UserMixin(TimeStampMixin):
14
14
  This table is used to store the user itself.
15
15
  """
16
16
 
17
- id: Mapped[uuid.UUID] = mapped_column(
18
- UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
19
- )
17
+ id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
20
18
  account_id: Mapped[uuid.UUID] = mapped_column(
21
19
  ForeignKey("user_account.id"), nullable=False, index=True
22
20
  )
@@ -40,15 +38,11 @@ class UserMixin(TimeStampMixin):
40
38
 
41
39
  @declared_attr
42
40
  def organizations(cls) -> Mapped[List["Organization"]]:
43
- return relationship(
44
- secondary="organization_user_assoc", back_populates="users"
45
- )
41
+ return relationship(secondary="organization_user_assoc", back_populates="users")
46
42
 
47
43
  @declared_attr
48
44
  def locations(cls) -> Mapped[List["Location"]]:
49
- return relationship(
50
- secondary="user_location_assoc", viewonly=True
51
- )
45
+ return relationship(secondary="user_location_assoc", viewonly=True)
52
46
 
53
47
  @declared_attr
54
48
  def permission_ids(cls) -> AssociationProxy[List["Permission"] | None]:
@@ -60,9 +54,7 @@ class UserMixin(TimeStampMixin):
60
54
 
61
55
  @declared_attr
62
56
  def permissions(cls) -> Mapped[list["Permission"]]:
63
- return relationship(
64
- secondary="user_permission_assoc", viewonly=True
65
- )
57
+ return relationship(secondary="user_permission_assoc", viewonly=True)
66
58
 
67
59
 
68
60
  class UserSync(CustomSyncBase, UserMixin):
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "artemis-model"
3
- version = "0.1.89"
3
+ version = "0.1.90"
4
4
  description = ""
5
5
  authors = ["Jukeboxy"]
6
6
  readme = "README.md"
@@ -12,6 +12,7 @@ greenlet = "^3.0.2"
12
12
  python-slugify = "^8.0.4"
13
13
  alembic = "^1.13.1"
14
14
  asyncpg = "^0.29.0"
15
+ pydantic = "^2.10.2"
15
16
 
16
17
 
17
18
  [tool.poetry.dev-dependencies]
@@ -1,5 +0,0 @@
1
- """Redis models."""
2
-
3
- from .zone_state import ZoneState, NowPlaying, SessionId, BucketId
4
-
5
- __all__ = ["ZoneState", "NowPlaying", "SessionId", "BucketId"]
File without changes