artemis-model 0.1.178__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.
Potentially problematic release.
This version of artemis-model might be problematic. Click here for more details.
- artemis_model-0.1.178/PKG-INFO +116 -0
- artemis_model-0.1.178/README.md +96 -0
- artemis_model-0.1.178/artemis_model/__init__.py +22 -0
- artemis_model-0.1.178/artemis_model/album.py +69 -0
- artemis_model-0.1.178/artemis_model/approved_playlist_list.py +55 -0
- artemis_model-0.1.178/artemis_model/artist.py +49 -0
- artemis_model-0.1.178/artemis_model/auth.py +184 -0
- artemis_model-0.1.178/artemis_model/banned_tracks.py +24 -0
- artemis_model-0.1.178/artemis_model/base.py +229 -0
- artemis_model-0.1.178/artemis_model/category.py +53 -0
- artemis_model-0.1.178/artemis_model/dj_set.py +112 -0
- artemis_model-0.1.178/artemis_model/genre.py +47 -0
- artemis_model-0.1.178/artemis_model/location.py +156 -0
- artemis_model-0.1.178/artemis_model/location_genre_exclusion.py +38 -0
- artemis_model-0.1.178/artemis_model/message.py +106 -0
- artemis_model-0.1.178/artemis_model/organization.py +95 -0
- artemis_model-0.1.178/artemis_model/organization_include_pal_setting.py +32 -0
- artemis_model-0.1.178/artemis_model/otp.py +29 -0
- artemis_model-0.1.178/artemis_model/permission.py +47 -0
- artemis_model-0.1.178/artemis_model/playlist.py +198 -0
- artemis_model-0.1.178/artemis_model/redis/__init__.py +40 -0
- artemis_model-0.1.178/artemis_model/redis/bucket.py +50 -0
- artemis_model-0.1.178/artemis_model/redis/device.py +22 -0
- artemis_model-0.1.178/artemis_model/redis/keys.py +18 -0
- artemis_model-0.1.178/artemis_model/redis/play_history.py +44 -0
- artemis_model-0.1.178/artemis_model/redis/zone_state.py +111 -0
- artemis_model-0.1.178/artemis_model/schedule.py +90 -0
- artemis_model-0.1.178/artemis_model/setting.py +46 -0
- artemis_model-0.1.178/artemis_model/sqs/__init__.py +1 -0
- artemis_model-0.1.178/artemis_model/sqs/messages.py +122 -0
- artemis_model-0.1.178/artemis_model/track.py +112 -0
- artemis_model-0.1.178/artemis_model/user.py +65 -0
- artemis_model-0.1.178/artemis_model/zone.py +69 -0
- artemis_model-0.1.178/artemis_model/zone_activity.py +48 -0
- artemis_model-0.1.178/artemis_model/zone_state.py +88 -0
- artemis_model-0.1.178/pyproject.toml +99 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: artemis-model
|
|
3
|
+
Version: 0.1.178
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Jukeboxy
|
|
6
|
+
Requires-Python: >=3.10.6,<4.0.0
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Requires-Dist: alembic (>=1.13.1,<2.0.0)
|
|
13
|
+
Requires-Dist: asyncpg (>=0.30.0,<0.31.0)
|
|
14
|
+
Requires-Dist: greenlet (>=3.0.2,<4.0.0)
|
|
15
|
+
Requires-Dist: pydantic (>=2.10.2,<3.0.0)
|
|
16
|
+
Requires-Dist: python-slugify (>=8.0.4,<9.0.0)
|
|
17
|
+
Requires-Dist: sqlalchemy (>=2.0.23,<3.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# artemis-model
|
|
21
|
+
|
|
22
|
+
Welcome to `artemis-model`, the backbone repository that contains all the essential models used in both the `artemis-api` and the `prophet` project. This project includes asynchronous models used by `artemis-api`, such as `Artist`, and synchronous models like `ArtistSync` for other implementations.
|
|
23
|
+
|
|
24
|
+
## Getting Started
|
|
25
|
+
|
|
26
|
+
To set up your development environment for `artemis-model`, follow these initial setup steps:
|
|
27
|
+
|
|
28
|
+
1. **Environment Setup**
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
cp ./alembic/.env.example ./alembic/.env
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
After copying the example environment file, make sure to fill in the `.env` file with your specific configurations.
|
|
35
|
+
|
|
36
|
+
2. **Install Dependencies**
|
|
37
|
+
|
|
38
|
+
```shell
|
|
39
|
+
poetry install --all-extras
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This will install all necessary dependencies to get you started with `artemis-models` and `alembic`.
|
|
43
|
+
|
|
44
|
+
## Creating a New Model
|
|
45
|
+
|
|
46
|
+
To introduce a new model in the project, you should start by creating a mixin and then define two different model classes that inherit from this mixin.
|
|
47
|
+
|
|
48
|
+
### Example: Adding a `LoginHistory` Model
|
|
49
|
+
|
|
50
|
+
1. **Define the Mixin**
|
|
51
|
+
This mixin will include all the common attributes of your model.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
class LoginHistoryMixin:
|
|
55
|
+
"""
|
|
56
|
+
Stores the login history of users.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, nullable=False)
|
|
60
|
+
account_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("user_account.id"), nullable=False, index=True)
|
|
61
|
+
ip_address: Mapped[str] = mapped_column(nullable=True)
|
|
62
|
+
created_at = mapped_column(DateTime, default=datetime.utcnow)
|
|
63
|
+
|
|
64
|
+
@declared_attr
|
|
65
|
+
def account(cls) -> Mapped["UserAccount"]:
|
|
66
|
+
return relationship("UserAccount", back_populates="login_histories")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. **Inherit from Base Classes**
|
|
70
|
+
Create two classes that inherit from CustomBase and CustomSyncBase respectively, using the mixin for shared attributes.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
class LoginHistorySync(CustomSyncBase, LoginHistoryMixin):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
class LoginHistory(CustomBase, LoginHistoryMixin):
|
|
77
|
+
pass
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Version Management and Builds
|
|
81
|
+
|
|
82
|
+
1. **Update the Project Version**
|
|
83
|
+
|
|
84
|
+
Open pyproject.toml and increment the minor version number.
|
|
85
|
+
|
|
86
|
+
2. **Build the Project**
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
poetry build
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
3. **Update Dependency in artemis-api/prophet**
|
|
93
|
+
|
|
94
|
+
If the build succeeds, remember to also bump the version number in the pyproject.toml of artemis-api and prophet to match the new version of artemis-model.
|
|
95
|
+
|
|
96
|
+
## Using Alembic for Model Changes
|
|
97
|
+
|
|
98
|
+
If modifications are necessary for any model:
|
|
99
|
+
|
|
100
|
+
1. **Modify the Model**
|
|
101
|
+
2. **Create an Alembic Revision**
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
alembic revision --autogenerate -m "Description of changes"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
3. **Upgrade Database Schema**
|
|
108
|
+
|
|
109
|
+
```shell
|
|
110
|
+
alembic upgrade head
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Ensure that the new Alembic script in the versions directory is committed to your git repository.
|
|
114
|
+
Repeat the build and version update steps as necessary after making changes.
|
|
115
|
+
|
|
116
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# artemis-model
|
|
2
|
+
|
|
3
|
+
Welcome to `artemis-model`, the backbone repository that contains all the essential models used in both the `artemis-api` and the `prophet` project. This project includes asynchronous models used by `artemis-api`, such as `Artist`, and synchronous models like `ArtistSync` for other implementations.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
To set up your development environment for `artemis-model`, follow these initial setup steps:
|
|
8
|
+
|
|
9
|
+
1. **Environment Setup**
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
cp ./alembic/.env.example ./alembic/.env
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
After copying the example environment file, make sure to fill in the `.env` file with your specific configurations.
|
|
16
|
+
|
|
17
|
+
2. **Install Dependencies**
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
poetry install --all-extras
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This will install all necessary dependencies to get you started with `artemis-models` and `alembic`.
|
|
24
|
+
|
|
25
|
+
## Creating a New Model
|
|
26
|
+
|
|
27
|
+
To introduce a new model in the project, you should start by creating a mixin and then define two different model classes that inherit from this mixin.
|
|
28
|
+
|
|
29
|
+
### Example: Adding a `LoginHistory` Model
|
|
30
|
+
|
|
31
|
+
1. **Define the Mixin**
|
|
32
|
+
This mixin will include all the common attributes of your model.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
class LoginHistoryMixin:
|
|
36
|
+
"""
|
|
37
|
+
Stores the login history of users.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, nullable=False)
|
|
41
|
+
account_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("user_account.id"), nullable=False, index=True)
|
|
42
|
+
ip_address: Mapped[str] = mapped_column(nullable=True)
|
|
43
|
+
created_at = mapped_column(DateTime, default=datetime.utcnow)
|
|
44
|
+
|
|
45
|
+
@declared_attr
|
|
46
|
+
def account(cls) -> Mapped["UserAccount"]:
|
|
47
|
+
return relationship("UserAccount", back_populates="login_histories")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
2. **Inherit from Base Classes**
|
|
51
|
+
Create two classes that inherit from CustomBase and CustomSyncBase respectively, using the mixin for shared attributes.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
class LoginHistorySync(CustomSyncBase, LoginHistoryMixin):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
class LoginHistory(CustomBase, LoginHistoryMixin):
|
|
58
|
+
pass
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Version Management and Builds
|
|
62
|
+
|
|
63
|
+
1. **Update the Project Version**
|
|
64
|
+
|
|
65
|
+
Open pyproject.toml and increment the minor version number.
|
|
66
|
+
|
|
67
|
+
2. **Build the Project**
|
|
68
|
+
|
|
69
|
+
```shell
|
|
70
|
+
poetry build
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
3. **Update Dependency in artemis-api/prophet**
|
|
74
|
+
|
|
75
|
+
If the build succeeds, remember to also bump the version number in the pyproject.toml of artemis-api and prophet to match the new version of artemis-model.
|
|
76
|
+
|
|
77
|
+
## Using Alembic for Model Changes
|
|
78
|
+
|
|
79
|
+
If modifications are necessary for any model:
|
|
80
|
+
|
|
81
|
+
1. **Modify the Model**
|
|
82
|
+
2. **Create an Alembic Revision**
|
|
83
|
+
|
|
84
|
+
```shell
|
|
85
|
+
alembic revision --autogenerate -m "Description of changes"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
3. **Upgrade Database Schema**
|
|
89
|
+
|
|
90
|
+
```shell
|
|
91
|
+
alembic upgrade head
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Ensure that the new Alembic script in the versions directory is committed to your git repository.
|
|
95
|
+
Repeat the build and version update steps as necessary after making changes.
|
|
96
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from artemis_model.album import * # noqa
|
|
2
|
+
from artemis_model.approved_playlist_list import * # noqa
|
|
3
|
+
from artemis_model.artist import * # noqa
|
|
4
|
+
from artemis_model.auth import * # noqa
|
|
5
|
+
from artemis_model.category import * # noqa
|
|
6
|
+
from artemis_model.dj_set import * # noqa
|
|
7
|
+
from artemis_model.genre import * # noqa
|
|
8
|
+
from artemis_model.location import * # noqa
|
|
9
|
+
from artemis_model.location_genre_exclusion import * # noqa
|
|
10
|
+
from artemis_model.message import * # noqa
|
|
11
|
+
from artemis_model.organization import * # noqa
|
|
12
|
+
from artemis_model.organization_include_pal_setting import * # noqa
|
|
13
|
+
from artemis_model.playlist import * # noqa
|
|
14
|
+
from artemis_model.schedule import * # noqa
|
|
15
|
+
from artemis_model.setting import * # noqa
|
|
16
|
+
from artemis_model.track import * # noqa
|
|
17
|
+
from artemis_model.user import * # noqa
|
|
18
|
+
from artemis_model.zone import * # noqa
|
|
19
|
+
from artemis_model.otp import * # noqa
|
|
20
|
+
from artemis_model.banned_tracks import * # noqa
|
|
21
|
+
from artemis_model.zone_activity import * # noqa
|
|
22
|
+
from artemis_model.zone_state import * # noqa
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Album models"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import Computed, ForeignKey, func, literal, text
|
|
6
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
7
|
+
|
|
8
|
+
from artemis_model.base import CustomSyncBase, CustomBase, TSVector, TimeStampMixin
|
|
9
|
+
|
|
10
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def to_tsvector_ix(*columns):
|
|
14
|
+
s = " || ' ' || ".join(columns)
|
|
15
|
+
return func.to_tsvector(literal("english"), text(s))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AlbumMixin(TimeStampMixin):
|
|
19
|
+
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
20
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
21
|
+
description: Mapped[str | None] = mapped_column(nullable=True)
|
|
22
|
+
entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
|
|
23
|
+
disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
|
|
24
|
+
legacy_id: Mapped[str] = mapped_column(nullable=False)
|
|
25
|
+
is_internal: Mapped[bool] = mapped_column(nullable=False, default=False)
|
|
26
|
+
|
|
27
|
+
name_tsv = mapped_column(
|
|
28
|
+
TSVector(),
|
|
29
|
+
Computed("to_tsvector('english', name)", persisted=True),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# __table_args__ = (
|
|
33
|
+
# Index("fts_ix_album_name_tsv", to_tsvector_ix("name"), postgresql_using="gin"),
|
|
34
|
+
# )
|
|
35
|
+
|
|
36
|
+
@declared_attr
|
|
37
|
+
def artists(cls) -> Mapped[list["Artist"]]:
|
|
38
|
+
return relationship(secondary="album_artist_assoc", back_populates="albums")
|
|
39
|
+
|
|
40
|
+
@declared_attr
|
|
41
|
+
def tracks(cls) -> Mapped[list["Track"]]:
|
|
42
|
+
return relationship(
|
|
43
|
+
"Track",
|
|
44
|
+
back_populates="album",
|
|
45
|
+
cascade="all, delete-orphan",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AlbumSync(CustomSyncBase, AlbumMixin):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Album(CustomBase, AlbumMixin):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AlbumArtistAssocMixin:
|
|
58
|
+
album_id: Mapped[int] = mapped_column(ForeignKey("album.id"), primary_key=True, nullable=False)
|
|
59
|
+
artist_id: Mapped[int] = mapped_column(
|
|
60
|
+
ForeignKey("artist.id"), primary_key=True, nullable=False
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AlbumArtistAssocSync(CustomSyncBase, AlbumArtistAssocMixin):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AlbumArtistAssoc(CustomBase, AlbumArtistAssocMixin):
|
|
69
|
+
pass
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import ForeignKey
|
|
5
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
6
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
7
|
+
|
|
8
|
+
from artemis_model.base import CustomSyncBase, TimeStampMixin, AuditMixin, CustomBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApprovedPlaylistListMixin(TimeStampMixin, AuditMixin):
|
|
12
|
+
id: Mapped[int] = mapped_column(autoincrement=True, primary_key=True, index=True)
|
|
13
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
14
|
+
organization_id: Mapped[uuid.UUID] = mapped_column(
|
|
15
|
+
ForeignKey("organization.id"), nullable=False, index=True
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
@declared_attr
|
|
19
|
+
def organization(cls) -> Mapped["Organization"]:
|
|
20
|
+
return relationship("Organization", back_populates="approved_playlist_lists")
|
|
21
|
+
|
|
22
|
+
@declared_attr
|
|
23
|
+
def playlist_associations(cls) -> Mapped[List["ApprovedPlaylistListPlaylistAssoc"]]:
|
|
24
|
+
return relationship(cascade="all, delete-orphan")
|
|
25
|
+
|
|
26
|
+
@declared_attr
|
|
27
|
+
def playlists(cls) -> Mapped[List["Playlist"]]:
|
|
28
|
+
return relationship(
|
|
29
|
+
"Playlist", secondary="approved_playlist_list_playlist_assoc", viewonly=True
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ApprovedPlaylistListSync(CustomSyncBase, ApprovedPlaylistListMixin):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ApprovedPlaylistList(CustomBase, ApprovedPlaylistListMixin):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ApprovedPlaylistListPlaylistAssocMixin(TimeStampMixin):
|
|
42
|
+
approved_playlist_list_id: Mapped[int] = mapped_column(
|
|
43
|
+
ForeignKey("approved_playlist_list.id"), primary_key=True, nullable=False
|
|
44
|
+
)
|
|
45
|
+
playlist_id: Mapped[int] = mapped_column(
|
|
46
|
+
ForeignKey("playlist.id"), primary_key=True, nullable=False
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ApprovedPlaylistListPlaylistAssocSync(CustomSyncBase, ApprovedPlaylistListPlaylistAssocMixin):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ApprovedPlaylistListPlaylistAssoc(CustomBase, ApprovedPlaylistListPlaylistAssocMixin):
|
|
55
|
+
pass
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Artist models"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import Computed, func, literal, text
|
|
6
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
7
|
+
|
|
8
|
+
from artemis_model.base import CustomBase, CustomSyncBase, TSVector, TimeStampMixin
|
|
9
|
+
|
|
10
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def to_tsvector_ix(*columns):
|
|
14
|
+
s = " || ' ' || ".join(columns)
|
|
15
|
+
return func.to_tsvector(literal("english"), text(s))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ArtistMixin(TimeStampMixin):
|
|
19
|
+
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
20
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
21
|
+
entry_date: Mapped[datetime] = mapped_column(nullable=False, default=datetime.utcnow)
|
|
22
|
+
disabled: Mapped[bool] = mapped_column(nullable=False, default=False)
|
|
23
|
+
is_internal: Mapped[bool] = mapped_column(nullable=False, default=False)
|
|
24
|
+
legacy_id: Mapped[str] = mapped_column(nullable=False)
|
|
25
|
+
|
|
26
|
+
name_tsv = mapped_column(
|
|
27
|
+
TSVector(),
|
|
28
|
+
Computed("to_tsvector('english', name)", persisted=True),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# __table_args__ = (
|
|
32
|
+
# Index("fts_ix_artist_name_tsv", to_tsvector_ix("name"), postgresql_using="gin"),
|
|
33
|
+
# )
|
|
34
|
+
|
|
35
|
+
@declared_attr
|
|
36
|
+
def albums(cls) -> Mapped[list["Album"]]:
|
|
37
|
+
return relationship(secondary="album_artist_assoc", back_populates="artists")
|
|
38
|
+
|
|
39
|
+
@declared_attr
|
|
40
|
+
def tracks(cls) -> Mapped[list["Track"]]:
|
|
41
|
+
return relationship("Track", back_populates="artist", cascade="all, delete-orphan")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ArtistSync(CustomSyncBase, ArtistMixin):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Artist(CustomBase, ArtistMixin):
|
|
49
|
+
pass
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Auth models."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from sqlalchemy import UUID, DateTime, ForeignKey, LargeBinary
|
|
10
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
11
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
12
|
+
|
|
13
|
+
from artemis_model.base import CustomSyncBase, TimeStampMixin, CustomBase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UserUnverifiedAccountMixin(TimeStampMixin):
|
|
17
|
+
"""
|
|
18
|
+
This table is used to store the account info of users who have requested an account but have not yet
|
|
19
|
+
been verified. Once the user has been verified, the account will be moved to the UserAccount table.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
23
|
+
email: Mapped[str] = mapped_column(index=True)
|
|
24
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
25
|
+
mobile: Mapped[str] = mapped_column(nullable=True, index=True)
|
|
26
|
+
is_root_user: Mapped[bool] = mapped_column(default=True)
|
|
27
|
+
is_email_verified: Mapped[bool] = mapped_column(default=False)
|
|
28
|
+
is_mobile_verified: Mapped[bool] = mapped_column(default=False)
|
|
29
|
+
is_onboarded: Mapped[bool] = mapped_column(default=False)
|
|
30
|
+
password = mapped_column(LargeBinary, nullable=False)
|
|
31
|
+
provider: Mapped[str] = mapped_column(default="internal")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class UserUnverifiedAccountSync(CustomSyncBase, UserUnverifiedAccountMixin):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UserUnverifiedAccount(CustomBase, UserUnverifiedAccountMixin):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class UserAccountMixin(TimeStampMixin):
|
|
43
|
+
"""
|
|
44
|
+
This table is used to store the account info of users who have been verified.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
48
|
+
email: Mapped[str] = mapped_column(index=True, unique=True)
|
|
49
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
50
|
+
mobile: Mapped[Optional[str]] = mapped_column(nullable=True, unique=True, index=True)
|
|
51
|
+
password = mapped_column(LargeBinary, nullable=True)
|
|
52
|
+
provider: Mapped[str] = mapped_column(nullable=False)
|
|
53
|
+
oauth_id: Mapped[Optional[str]] = mapped_column(nullable=True, index=True)
|
|
54
|
+
image_url: Mapped[Optional[str]] = mapped_column(nullable=True)
|
|
55
|
+
is_email_verified: Mapped[bool] = mapped_column(default=False)
|
|
56
|
+
is_mobile_verified: Mapped[bool] = mapped_column(default=False)
|
|
57
|
+
is_root_user: Mapped[bool] = mapped_column(default=True)
|
|
58
|
+
is_super_admin: Mapped[bool] = mapped_column(default=False)
|
|
59
|
+
disabled: Mapped[bool] = mapped_column(default=False)
|
|
60
|
+
disabled_reason: Mapped[Optional[str]] = mapped_column(nullable=True)
|
|
61
|
+
is_onboarded: Mapped[bool] = mapped_column(default=False)
|
|
62
|
+
|
|
63
|
+
@declared_attr
|
|
64
|
+
def login_histories(cls) -> Mapped["LoginHistory"]:
|
|
65
|
+
return relationship("LoginHistory", back_populates="account")
|
|
66
|
+
|
|
67
|
+
@declared_attr
|
|
68
|
+
def user(cls) -> Mapped["User"]:
|
|
69
|
+
return relationship("User", back_populates="account")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class UserAccountSync(CustomSyncBase, UserAccountMixin):
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class UserAccount(CustomBase, UserAccountMixin):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class LoginHistoryMixin:
|
|
81
|
+
"""
|
|
82
|
+
This table is used to store the login history of users.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, nullable=False)
|
|
86
|
+
account_id: Mapped[uuid.UUID] = mapped_column(
|
|
87
|
+
ForeignKey("user_account.id"), nullable=False, index=True
|
|
88
|
+
)
|
|
89
|
+
ip_address: Mapped[str] = mapped_column(nullable=True)
|
|
90
|
+
created_at = mapped_column(DateTime, default=datetime.utcnow)
|
|
91
|
+
|
|
92
|
+
@declared_attr
|
|
93
|
+
def account(cls) -> Mapped["UserAccount"]:
|
|
94
|
+
return relationship("UserAccount", back_populates="login_histories")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LoginHistorySync(CustomSyncBase, LoginHistoryMixin):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class LoginHistory(CustomBase, LoginHistoryMixin):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class OAuthCsrfStateMixin(TimeStampMixin):
|
|
106
|
+
id: Mapped[str] = mapped_column(primary_key=True, unique=True)
|
|
107
|
+
client_base_url: Mapped[str] = mapped_column(nullable=True)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class OAuthCsrfStateSync(CustomSyncBase, OAuthCsrfStateMixin):
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class OAuthCsrfState(CustomBase, OAuthCsrfStateMixin):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class OrionWebplayerCodeMixin(TimeStampMixin):
|
|
119
|
+
"""Orion webplayer code mixin."""
|
|
120
|
+
|
|
121
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
122
|
+
code_hash: Mapped[str] = mapped_column(
|
|
123
|
+
unique=True, nullable=False
|
|
124
|
+
) # Argon2id of normalized code
|
|
125
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
126
|
+
zone_id: Mapped[int] = mapped_column(nullable=False, index=True)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class OrionWebplayerCodeSync(CustomSyncBase, OrionWebplayerCodeMixin):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class OrionWebplayerCode(CustomBase, OrionWebplayerCodeMixin):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class PlayerRefreshTokenMixin(TimeStampMixin):
|
|
138
|
+
"""
|
|
139
|
+
Refresh token for Player.
|
|
140
|
+
We look up by token_id (PK), then verify `secret` against `secret_hash`.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
# this is the public part we send to the client
|
|
144
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
145
|
+
UUID(as_uuid=True),
|
|
146
|
+
primary_key=True,
|
|
147
|
+
default=uuid.uuid4,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
code_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
|
151
|
+
UUID(as_uuid=True),
|
|
152
|
+
ForeignKey("orion_webplayer_code.id", ondelete="SET NULL"),
|
|
153
|
+
index=True,
|
|
154
|
+
nullable=True,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# store zone_id too (helps auth decisions / debugging)
|
|
158
|
+
zone_id: Mapped[Optional[int]] = mapped_column(index=True)
|
|
159
|
+
|
|
160
|
+
# bcrypt/argon2 hash of the secret part
|
|
161
|
+
secret_hash: Mapped[str] = mapped_column(nullable=False)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class PlayerRefreshTokenSync(CustomSyncBase, PlayerRefreshTokenMixin):
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class PlayerRefreshToken(CustomBase, PlayerRefreshTokenMixin):
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class Scope(str, Enum):
|
|
173
|
+
"""Scope enum."""
|
|
174
|
+
|
|
175
|
+
PLAYER = "player"
|
|
176
|
+
MANAGE = "manage"
|
|
177
|
+
|
|
178
|
+
class TokenData(BaseModel):
|
|
179
|
+
"""Token data."""
|
|
180
|
+
|
|
181
|
+
account_id: uuid.UUID
|
|
182
|
+
user_id: uuid.UUID
|
|
183
|
+
scope: Scope
|
|
184
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Banned Tracks per Zone Model"""
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import UUID, Integer
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
5
|
+
from artemis_model.base import AuditMixin, TimeStampMixin, CustomSyncBase, CustomBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BannedTracksMixin(TimeStampMixin, AuditMixin):
|
|
9
|
+
"""Banned Tracks per Zone Model"""
|
|
10
|
+
|
|
11
|
+
zone_id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
12
|
+
track_id: Mapped[UUID] = mapped_column(UUID(as_uuid=True), primary_key=True)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BannedTracksSync(CustomSyncBase, BannedTracksMixin):
|
|
16
|
+
"""Banned Tracks per Zone Model"""
|
|
17
|
+
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BannedTracks(CustomBase, BannedTracksMixin):
|
|
22
|
+
"""Banned Tracks per Zone Model"""
|
|
23
|
+
|
|
24
|
+
pass
|