feat(api): rename track type to library

Related to #1922

- rename library model fields
This commit is contained in:
jo 2022-06-28 09:20:11 +02:00 committed by Kyle Robbertze
parent e5cb21c0e2
commit 05ca410453
14 changed files with 289 additions and 274 deletions

View File

@ -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)

View File

@ -1,3 +1,3 @@
from .cloud_file import CloudFile
from .file import File
from .track_type import TrackType
from .library import Library

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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)

View File

@ -1,3 +1,3 @@
from .cloud_file import CloudFileSerializer
from .file import FileSerializer
from .track_type import TrackTypeSerializer
from .library import LibrarySerializer

View File

@ -0,0 +1,9 @@
from rest_framework import serializers
from ..models import Library
class LibrarySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Library
fields = "__all__"

View File

@ -1,9 +0,0 @@
from rest_framework import serializers
from ..models import TrackType
class TrackTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TrackType
fields = "__all__"

View File

@ -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"])

View File

@ -1,3 +1,3 @@
from .cloud_file import CloudFileViewSet
from .file import FileViewSet
from .track_type import TrackTypeViewSet
from .library import LibraryViewSet

View File

@ -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"

View File

@ -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"

View File

@ -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: