From 05ca410453373a44f457ae5405d876be3b29df9d Mon Sep 17 00:00:00 2001 From: jo Date: Tue, 28 Jun 2022 09:20:11 +0200 Subject: [PATCH] feat(api): rename track type to library Related to #1922 - rename library model fields --- .../management/commands/bulk_import.py | 36 +- api/libretime_api/storage/models/__init__.py | 2 +- api/libretime_api/storage/models/file.py | 8 +- api/libretime_api/storage/models/library.py | 21 + .../storage/models/track_type.py | 12 - api/libretime_api/storage/router.py | 4 +- .../storage/serializers/__init__.py | 2 +- .../storage/serializers/library.py | 9 + .../storage/serializers/track_type.py | 9 - .../management/commands/test_bulk_import.py | 42 +- api/libretime_api/storage/views/__init__.py | 2 +- api/libretime_api/storage/views/library.py | 10 + api/libretime_api/storage/views/track_type.py | 10 - api/schema.yml | 396 +++++++++--------- 14 files changed, 289 insertions(+), 274 deletions(-) create mode 100644 api/libretime_api/storage/models/library.py delete mode 100644 api/libretime_api/storage/models/track_type.py create mode 100644 api/libretime_api/storage/serializers/library.py delete mode 100644 api/libretime_api/storage/serializers/track_type.py create mode 100644 api/libretime_api/storage/views/library.py delete mode 100644 api/libretime_api/storage/views/track_type.py diff --git a/api/libretime_api/storage/management/commands/bulk_import.py b/api/libretime_api/storage/management/commands/bulk_import.py index 933274c5f..a38de0164 100644 --- a/api/libretime_api/storage/management/commands/bulk_import.py +++ b/api/libretime_api/storage/management/commands/bulk_import.py @@ -7,7 +7,7 @@ from django.conf import settings from django.core.management.base import BaseCommand, CommandParser from libretime_shared.files import compute_md5 -from ...models import File, TrackType +from ...models import File, Library logger = logging.getLogger(__name__) @@ -31,8 +31,8 @@ class Command(BaseCommand): required=True, ) parser.add_argument( - "--track-type", - help="Track type for the new files.", + "--library", + help="Library for the new files.", ) parser.add_argument( "--allowed-extensions", @@ -59,11 +59,11 @@ class Command(BaseCommand): delete_if_exists = options.get("delete_if_exists", False) path = options.get("path") - track_type = options.get("track_type", None) + library = options.get("library", None) allowed_extensions = options.get("allowed_extensions") importer = Importer(url, auth_key, delete_after_upload, delete_if_exists) - importer.import_dir(Path(path).resolve(), track_type, allowed_extensions) + importer.import_dir(Path(path).resolve(), library, allowed_extensions) class Importer: @@ -85,7 +85,7 @@ class Importer: return File.objects.filter(md5=file_md5).exists() - def _upload_file(self, filepath: Path, track_type: Optional[str]) -> None: + def _upload_file(self, filepath: Path, library: Optional[str]) -> None: try: resp = requests.post( f"{self.url}/rest/media", @@ -94,7 +94,7 @@ class Importer: ("file", (filepath.name, filepath.open("rb"))), ], timeout=30, - cookies={"tt_upload": track_type} if track_type is not None else {}, + cookies={"tt_upload": library} if library is not None else {}, ) resp.raise_for_status() @@ -105,7 +105,7 @@ class Importer: logger.info(f"deleting {filepath}") filepath.unlink() - def _handle_file(self, filepath: Path, track_type: Optional[str]) -> None: + def _handle_file(self, filepath: Path, library: Optional[str]) -> None: logger.debug(f"handling file {filepath}") if not filepath.is_file(): @@ -117,7 +117,7 @@ class Importer: self._delete_file(filepath) return - self._upload_file(filepath, track_type) + self._upload_file(filepath, library) if self.delete_after_upload: self._delete_file(filepath) @@ -125,7 +125,7 @@ class Importer: def _walk_dir( self, path: Path, - track_type: Optional[str], + library: Optional[str], allowed_extensions: List[str], ) -> None: if not path.is_dir(): @@ -133,28 +133,28 @@ class Importer: for sub_path in path.iterdir(): if sub_path.is_dir(): - self._walk_dir(sub_path, track_type, allowed_extensions) + self._walk_dir(sub_path, library, allowed_extensions) continue if sub_path.suffix.lower() not in allowed_extensions: continue - self._handle_file(sub_path.resolve(), track_type) + self._handle_file(sub_path.resolve(), library) - def _check_track_type(self, track_type: str) -> bool: - return TrackType.objects.filter(code=track_type).exists() + def _check_library(self, library: str) -> bool: + return Library.objects.filter(code=library).exists() def import_dir( self, path: Path, - track_type: Optional[str], + library: Optional[str], allowed_extensions: List[str], ) -> None: - if track_type is not None and not self._check_track_type(track_type): - raise ValueError(f"provided track type {track_type} does not exist") + if library is not None and not self._check_library(library): + raise ValueError(f"provided library {library} does not exist") allowed_extensions = [ (x if x.startswith(".") else "." + x) for x in allowed_extensions ] - self._walk_dir(path, track_type, allowed_extensions) + self._walk_dir(path, library, allowed_extensions) diff --git a/api/libretime_api/storage/models/__init__.py b/api/libretime_api/storage/models/__init__.py index aabfb9d53..fa9af85e7 100644 --- a/api/libretime_api/storage/models/__init__.py +++ b/api/libretime_api/storage/models/__init__.py @@ -1,3 +1,3 @@ from .cloud_file import CloudFile from .file import File -from .track_type import TrackType +from .library import Library diff --git a/api/libretime_api/storage/models/file.py b/api/libretime_api/storage/models/file.py index c9679edb2..05b83b19f 100644 --- a/api/libretime_api/storage/models/file.py +++ b/api/libretime_api/storage/models/file.py @@ -2,6 +2,13 @@ from django.db import models class File(models.Model): + library = models.CharField( + max_length=16, + blank=True, + null=True, + db_column="track_type", + ) + name = models.CharField(max_length=255) mime = models.CharField(max_length=255) ftype = models.CharField(max_length=128) @@ -82,7 +89,6 @@ class File(models.Model): filesize = models.IntegerField() description = models.CharField(max_length=512, blank=True, null=True) artwork = models.CharField(max_length=512, blank=True, null=True) - track_type = models.CharField(max_length=16, blank=True, null=True) def get_owner(self): return self.owner diff --git a/api/libretime_api/storage/models/library.py b/api/libretime_api/storage/models/library.py new file mode 100644 index 000000000..cf8b3825e --- /dev/null +++ b/api/libretime_api/storage/models/library.py @@ -0,0 +1,21 @@ +from django.db import models + + +class Library(models.Model): + name = models.CharField( + max_length=255, + blank=True, + null=True, + db_column="type_name", + ) + code = models.CharField(max_length=16, unique=True) + description = models.CharField(max_length=255, blank=True, null=True) + visible = models.BooleanField( + blank=True, + default=True, + db_column="visibility", + ) + + class Meta: + managed = False + db_table = "cc_track_types" diff --git a/api/libretime_api/storage/models/track_type.py b/api/libretime_api/storage/models/track_type.py deleted file mode 100644 index f42b8293c..000000000 --- a/api/libretime_api/storage/models/track_type.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.db import models - - -class TrackType(models.Model): - code = models.CharField(max_length=16, unique=True) - type_name = models.CharField(max_length=255, blank=True, null=True) - description = models.CharField(max_length=255, blank=True, null=True) - visibility = models.BooleanField(blank=True, default=True) - - class Meta: - managed = False - db_table = "cc_track_types" diff --git a/api/libretime_api/storage/router.py b/api/libretime_api/storage/router.py index 675ca3e93..6cca258c8 100644 --- a/api/libretime_api/storage/router.py +++ b/api/libretime_api/storage/router.py @@ -1,8 +1,8 @@ from rest_framework import routers -from .views import CloudFileViewSet, FileViewSet, TrackTypeViewSet +from .views import CloudFileViewSet, FileViewSet, LibraryViewSet router = routers.DefaultRouter() router.register("files", FileViewSet) router.register("cloud-files", CloudFileViewSet) -router.register("track-types", TrackTypeViewSet) +router.register("libraries", LibraryViewSet) diff --git a/api/libretime_api/storage/serializers/__init__.py b/api/libretime_api/storage/serializers/__init__.py index 279d99f94..52ab3212e 100644 --- a/api/libretime_api/storage/serializers/__init__.py +++ b/api/libretime_api/storage/serializers/__init__.py @@ -1,3 +1,3 @@ from .cloud_file import CloudFileSerializer from .file import FileSerializer -from .track_type import TrackTypeSerializer +from .library import LibrarySerializer diff --git a/api/libretime_api/storage/serializers/library.py b/api/libretime_api/storage/serializers/library.py new file mode 100644 index 000000000..374898273 --- /dev/null +++ b/api/libretime_api/storage/serializers/library.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from ..models import Library + + +class LibrarySerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Library + fields = "__all__" diff --git a/api/libretime_api/storage/serializers/track_type.py b/api/libretime_api/storage/serializers/track_type.py deleted file mode 100644 index c87e382a9..000000000 --- a/api/libretime_api/storage/serializers/track_type.py +++ /dev/null @@ -1,9 +0,0 @@ -from rest_framework import serializers - -from ..models import TrackType - - -class TrackTypeSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = TrackType - fields = "__all__" diff --git a/api/libretime_api/storage/tests/management/commands/test_bulk_import.py b/api/libretime_api/storage/tests/management/commands/test_bulk_import.py index 7a257c95e..5bd54a1ec 100644 --- a/api/libretime_api/storage/tests/management/commands/test_bulk_import.py +++ b/api/libretime_api/storage/tests/management/commands/test_bulk_import.py @@ -26,13 +26,13 @@ def _import_paths(tmp_path: Path): return (tmp_path, test_file) -@pytest.fixture(name="track_type") -def _track_type(): +@pytest.fixture(name="library") +def _library(): return baker.make( - "storage.TrackType", + "storage.Library", code="MUS", - type_name="Music", - description="Description", + name="Music", + description="Some music", ) @@ -58,12 +58,12 @@ def _importer(requests_mock: Mocker): def test_importer( import_paths: Tuple[Path, Path], importer: MockImporter, - track_type, + library, ): - importer.import_dir(import_paths[0], track_type.code, [".mp3"]) + importer.import_dir(import_paths[0], library.code, [".mp3"]) - importer._handle_file.assert_called_with(import_paths[1], track_type.code) - importer._upload_file.assert_called_with(import_paths[1], track_type.code) + importer._handle_file.assert_called_with(import_paths[1], library.code) + importer._upload_file.assert_called_with(import_paths[1], library.code) importer._delete_file.assert_not_called() @@ -71,13 +71,13 @@ def test_importer( def test_importer_and_delete( import_paths: Tuple[Path, Path], importer: MockImporter, - track_type, + library, ): importer.delete_after_upload = True - importer.import_dir(import_paths[0], track_type.code, [".mp3"]) + importer.import_dir(import_paths[0], library.code, [".mp3"]) - importer._handle_file.assert_called_with(import_paths[1], track_type.code) - importer._upload_file.assert_called_with(import_paths[1], track_type.code) + importer._handle_file.assert_called_with(import_paths[1], library.code) + importer._upload_file.assert_called_with(import_paths[1], library.code) importer._delete_file.assert_called_with(import_paths[1]) @@ -85,13 +85,13 @@ def test_importer_and_delete( def test_importer_existing_file( import_paths: Tuple[Path, Path], importer: MockImporter, - track_type, + library, ): baker.make("storage.File", md5="46305a7cf42ee53976c88d337e47e940") - importer.import_dir(import_paths[0], track_type.code, [".mp3"]) + importer.import_dir(import_paths[0], library.code, [".mp3"]) - importer._handle_file.assert_called_with(import_paths[1], track_type.code) + importer._handle_file.assert_called_with(import_paths[1], library.code) importer._upload_file.assert_not_called() importer._delete_file.assert_not_called() @@ -100,25 +100,25 @@ def test_importer_existing_file( def test_importer_existing_file_and_delete( import_paths: Tuple[Path, Path], importer: MockImporter, - track_type, + library, ): baker.make("storage.File", md5="46305a7cf42ee53976c88d337e47e940") importer.delete_if_exists = True - importer.import_dir(import_paths[0], track_type.code, [".mp3"]) + importer.import_dir(import_paths[0], library.code, [".mp3"]) - importer._handle_file.assert_called_with(import_paths[1], track_type.code) + importer._handle_file.assert_called_with(import_paths[1], library.code) importer._upload_file.assert_not_called() importer._delete_file.assert_called_with(import_paths[1]) @pytest.mark.django_db -def test_importer_missing_track_type( +def test_importer_missing_library( import_paths: Tuple[Path, Path], importer: MockImporter, ): with pytest.raises( ValueError, - match="provided track type MISSING does not exist", + match="provided library MISSING does not exist", ): importer.import_dir(import_paths[0], "MISSING", [".mp3"]) diff --git a/api/libretime_api/storage/views/__init__.py b/api/libretime_api/storage/views/__init__.py index 0c88b6c69..28f64238d 100644 --- a/api/libretime_api/storage/views/__init__.py +++ b/api/libretime_api/storage/views/__init__.py @@ -1,3 +1,3 @@ from .cloud_file import CloudFileViewSet from .file import FileViewSet -from .track_type import TrackTypeViewSet +from .library import LibraryViewSet diff --git a/api/libretime_api/storage/views/library.py b/api/libretime_api/storage/views/library.py new file mode 100644 index 000000000..7d23ac115 --- /dev/null +++ b/api/libretime_api/storage/views/library.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets + +from ..models import Library +from ..serializers import LibrarySerializer + + +class LibraryViewSet(viewsets.ModelViewSet): + queryset = Library.objects.all() + serializer_class = LibrarySerializer + model_permission_name = "library" diff --git a/api/libretime_api/storage/views/track_type.py b/api/libretime_api/storage/views/track_type.py deleted file mode 100644 index 4464e1983..000000000 --- a/api/libretime_api/storage/views/track_type.py +++ /dev/null @@ -1,10 +0,0 @@ -from rest_framework import viewsets - -from ..models import TrackType -from ..serializers import TrackTypeSerializer - - -class TrackTypeViewSet(viewsets.ModelViewSet): - queryset = TrackType.objects.all() - serializer_class = TrackTypeSerializer - model_permission_name = "tracktype" diff --git a/api/schema.yml b/api/schema.yml index 7eb0315dd..608f31812 100644 --- a/api/schema.yml +++ b/api/schema.yml @@ -761,6 +761,153 @@ paths: responses: "204": description: No response body + /api/v2/libraries/: + get: + operationId: libraries_list + tags: + - libraries + security: + - cookieAuth: [] + - basicAuth: [] + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Library" + description: "" + post: + operationId: libraries_create + tags: + - libraries + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Library" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Library" + multipart/form-data: + schema: + $ref: "#/components/schemas/Library" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/Library" + description: "" + /api/v2/libraries/{id}/: + get: + operationId: libraries_retrieve + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this library. + required: true + tags: + - libraries + security: + - cookieAuth: [] + - basicAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Library" + description: "" + put: + operationId: libraries_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this library. + required: true + tags: + - libraries + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Library" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Library" + multipart/form-data: + schema: + $ref: "#/components/schemas/Library" + required: true + security: + - cookieAuth: [] + - basicAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Library" + description: "" + patch: + operationId: libraries_partial_update + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this library. + required: true + tags: + - libraries + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedLibrary" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedLibrary" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedLibrary" + security: + - cookieAuth: [] + - basicAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Library" + description: "" + delete: + operationId: libraries_destroy + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this library. + required: true + tags: + - libraries + security: + - cookieAuth: [] + - basicAuth: [] + responses: + "204": + description: No response body /api/v2/listener-counts/: get: operationId: listener_counts_list @@ -5013,153 +5160,6 @@ paths: responses: "204": description: No response body - /api/v2/track-types/: - get: - operationId: track_types_list - tags: - - track-types - security: - - cookieAuth: [] - - basicAuth: [] - responses: - "200": - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/TrackType" - description: "" - post: - operationId: track_types_create - tags: - - track-types - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/TrackType" - application/x-www-form-urlencoded: - schema: - $ref: "#/components/schemas/TrackType" - multipart/form-data: - schema: - $ref: "#/components/schemas/TrackType" - required: true - security: - - cookieAuth: [] - - basicAuth: [] - responses: - "201": - content: - application/json: - schema: - $ref: "#/components/schemas/TrackType" - description: "" - /api/v2/track-types/{id}/: - get: - operationId: track_types_retrieve - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this track type. - required: true - tags: - - track-types - security: - - cookieAuth: [] - - basicAuth: [] - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/TrackType" - description: "" - put: - operationId: track_types_update - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this track type. - required: true - tags: - - track-types - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/TrackType" - application/x-www-form-urlencoded: - schema: - $ref: "#/components/schemas/TrackType" - multipart/form-data: - schema: - $ref: "#/components/schemas/TrackType" - required: true - security: - - cookieAuth: [] - - basicAuth: [] - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/TrackType" - description: "" - patch: - operationId: track_types_partial_update - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this track type. - required: true - tags: - - track-types - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/PatchedTrackType" - application/x-www-form-urlencoded: - schema: - $ref: "#/components/schemas/PatchedTrackType" - multipart/form-data: - schema: - $ref: "#/components/schemas/PatchedTrackType" - security: - - cookieAuth: [] - - basicAuth: [] - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/TrackType" - description: "" - delete: - operationId: track_types_destroy - parameters: - - in: path - name: id - schema: - type: integer - description: A unique integer value identifying this track type. - required: true - tags: - - track-types - security: - - cookieAuth: [] - - basicAuth: [] - responses: - "204": - description: No response body /api/v2/user-tokens/: get: operationId: user_tokens_list @@ -5834,6 +5834,10 @@ components: id: type: integer readOnly: true + library: + type: string + nullable: true + maxLength: 16 name: type: string maxLength: 255 @@ -6082,10 +6086,6 @@ components: type: string nullable: true maxLength: 512 - track_type: - type: string - nullable: true - maxLength: 16 edited_by: type: string format: uri @@ -6126,6 +6126,29 @@ components: - item_url - override_album - podcast + Library: + type: object + properties: + item_url: + type: string + format: uri + readOnly: true + name: + type: string + nullable: true + maxLength: 255 + code: + type: string + maxLength: 16 + description: + type: string + nullable: true + maxLength: 255 + visible: + type: boolean + required: + - code + - item_url ListenerCount: type: object properties: @@ -6256,6 +6279,10 @@ components: id: type: integer readOnly: true + library: + type: string + nullable: true + maxLength: 16 name: type: string maxLength: 255 @@ -6504,10 +6531,6 @@ components: type: string nullable: true maxLength: 512 - track_type: - type: string - nullable: true - maxLength: 16 edited_by: type: string format: uri @@ -6534,6 +6557,26 @@ components: podcast: type: string format: uri + PatchedLibrary: + type: object + properties: + item_url: + type: string + format: uri + readOnly: true + name: + type: string + nullable: true + maxLength: 255 + code: + type: string + maxLength: 16 + description: + type: string + nullable: true + maxLength: 255 + visible: + type: boolean PatchedListenerCount: type: object properties: @@ -7277,26 +7320,6 @@ components: timestamp: type: string format: date-time - PatchedTrackType: - type: object - properties: - item_url: - type: string - format: uri - readOnly: true - code: - type: string - maxLength: 16 - type_name: - type: string - nullable: true - maxLength: 255 - description: - type: string - nullable: true - maxLength: 255 - visibility: - type: boolean PatchedUser: type: object properties: @@ -8232,29 +8255,6 @@ components: required: - item_url - timestamp - TrackType: - type: object - properties: - item_url: - type: string - format: uri - readOnly: true - code: - type: string - maxLength: 16 - type_name: - type: string - nullable: true - maxLength: 255 - description: - type: string - nullable: true - maxLength: 255 - visibility: - type: boolean - required: - - code - - item_url User: type: object properties: