from marshmallow import Schema, fields, EXCLUDE, validate, post_load, pre_dump from marshmallow.validate import Range, Length from server.core.schemas import BaseSchema from server.dna.category.errors import NAME_IS_HOMEPAGE_CATEGORY_ERR from server.dna.constants import ( ARTIST, TRACK, LIKES, FOLLOWERS, VIEWS, CHART_TYPES, SUBSCRIBERS, LISTENERS, POPULARITY, STREAMS, MONTHLY_LISTENERS, PLAYS, COMMENTS, ) from marshmallow.exceptions import ValidationError class CategoryCounterSchema(Schema): artist = fields.Integer() track = fields.Integer() class CategorySchema: class ResponseSchema(Schema): class Meta: ordered = True id = fields.Integer() name = fields.String() created_at = fields.DateTime() updated_at = fields.DateTime() is_homepage = fields.Boolean() cover_image = fields.String() counters = fields.Nested(CategoryCounterSchema) @pre_dump def update_cover_image(self, data, **kwargs): if isinstance(data, dict): if data["cover_image"]: data["cover_image"] += "?resolution=large" return data class CreateRequestSchema(Schema): class Meta: unknown = EXCLUDE name = fields.String(required=True, validate=Length(max=255)) is_homepage = fields.Boolean(default=False) class UpdateRequestSchema(Schema): class Meta: unknown = EXCLUDE strict = True name = fields.String(validate=Length(max=255)) is_homepage = fields.Boolean(validate=validate.OneOf([True])) @post_load() def fields_validator(self, data, **kwargs): if self.fields.keys() == data.keys(): raise ValidationError(message=NAME_IS_HOMEPAGE_CATEGORY_ERR, field_name="error") return data class CreatedUpdatedResponseSchema(Schema): id = fields.Integer(required=True) class LimitOffsetSchema(BaseSchema): limit = fields.Integer(validate=Range(min=1, max=60), load_default=60) offset = fields.Integer(validate=Range(min=0), load_default=0) class CategoryEntitySchema: class QuerystringSchema(LimitOffsetSchema): entity_type = fields.String(validate=validate.OneOf([ARTIST, TRACK])) class ResponseSchema(Schema): id = fields.Integer() entity_type = fields.String(validate=validate.OneOf([ARTIST, TRACK])) created_at = fields.DateTime() data = fields.Dict() @pre_dump def entity_id_to_id(self, data, **kwargs): data.id = data.entity_id return data class CreateRequestSchema(Schema): class Meta: unknown = EXCLUDE entity_id = fields.Integer(required=True) entity_type = fields.String(validate=validate.OneOf([ARTIST, TRACK]), required=True) chart_type = fields.String(validate=validate.OneOf(CHART_TYPES), required=True) @post_load() def fields_validator(self, data, **kwargs): if data.get("entity_type") == ARTIST: if data.get("chart_type") not in [ FOLLOWERS, SUBSCRIBERS, LIKES, VIEWS, LISTENERS, POPULARITY, PLAYS, COMMENTS, ]: raise ValidationError(message=f"Wrong chart_type for entity_type {ARTIST}", field_name="error") elif data.get("entity_type") == TRACK: if data.get("chart_type") not in [LIKES, VIEWS, STREAMS, MONTHLY_LISTENERS, PLAYS, FOLLOWERS, COMMENTS]: raise ValidationError(message=f"Wrong chart_type for entity_type {TRACK}", field_name="error") return data class DeleteRequestSchema(Schema): id = fields.Integer(required=True) entity_id = fields.Integer(required=True) class EntityCategoriesQuerystringSchema(BaseSchema): ids = fields.String(required=True) @post_load() def ids_validator(self, data, **kwargs): if not all(x.isdigit() for x in data["ids"].split(",")): raise ValidationError(message="Wrong ids", field_name="error") return data class EntityTypeURLSchema(BaseSchema): entity_type = fields.String(validate=validate.OneOf([ARTIST, TRACK])) class CategoryEntityResponseSchema(Schema): id = fields.Integer() categories = fields.List(fields.Nested(CategorySchema.ResponseSchema(exclude=["cover_image"])))