From a2a229735405a3cc8ae5229bf834a8d3e98b6d3c Mon Sep 17 00:00:00 2001 From: Hellow <74311245+HeIIow2@users.noreply.github.com> Date: Tue, 19 Dec 2023 22:11:46 +0100 Subject: [PATCH] feat: fixed bugs --- .../inspectionProfiles/profiles_settings.xml | 1 + .idea/misc.xml | 3 + src/create_custom_objects.py | 2 - src/music_kraken/objects/__init__.py | 4 +- src/music_kraken/objects/collection.py | 8 +- src/music_kraken/objects/contact.py | 7 +- src/music_kraken/objects/country.py | 4 +- src/music_kraken/objects/lyrics.py | 6 +- src/music_kraken/objects/parents.py | 293 ++++------------- src/music_kraken/objects/song.py | 296 +++++------------- src/music_kraken/objects/source.py | 29 +- src/music_kraken/objects/target.py | 4 +- .../utils/support_classes/query.py | 2 +- 13 files changed, 174 insertions(+), 485 deletions(-) diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 105ce2d..dd4c951 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,5 +1,6 @@ + diff --git a/.idea/misc.xml b/.idea/misc.xml index df5fc58..878f755 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,4 +4,7 @@ + + \ No newline at end of file diff --git a/src/create_custom_objects.py b/src/create_custom_objects.py index 49d58a9..78adc96 100644 --- a/src/create_custom_objects.py +++ b/src/create_custom_objects.py @@ -25,7 +25,6 @@ print(other_song.__dict__) print(song) -""" only_smile = Artist( name="Only Smile", source_list=[Source(SourcePages.BANDCAMP, "https://onlysmile.bandcamp.com/")], @@ -126,7 +125,6 @@ for _id, _object in objects_by_id.items(): print(only_smile) """ -""" c = Collection([Song(title="hi"), Song(title="hi2"), Song(title="hi3")]) c1 = Collection([Song(title="he"), Song(title="hi5")]) c11 = Collection([Song(title="wow how ultra subby", isrc="hiii")]) diff --git a/src/music_kraken/objects/__init__.py b/src/music_kraken/objects/__init__.py index bdca840..5596a2c 100644 --- a/src/music_kraken/objects/__init__.py +++ b/src/music_kraken/objects/__init__.py @@ -1,5 +1,5 @@ +from typing_extensions import TypeVar from .option import Options -from .parents import DatabaseObject from .metadata import Metadata, Mapping as ID3Mapping, ID3Timestamp @@ -19,3 +19,5 @@ from .collection import Collection from .country import Country from .contact import Contact + +from .parents import OuterProxy as DatabaseObject diff --git a/src/music_kraken/objects/collection.py b/src/music_kraken/objects/collection.py index de98f47..a16d64f 100644 --- a/src/music_kraken/objects/collection.py +++ b/src/music_kraken/objects/collection.py @@ -54,6 +54,7 @@ class Collection(Generic[T]): self._indexed_values[name].add(value) self._indexed_to_objects[value].append(__object) + print(from_map) if not from_map: for attribute, new_object in self.contain_given_in_attribute.items(): __object.__getattribute__(attribute).contain_collection_inside(new_object) @@ -143,6 +144,7 @@ class Collection(Generic[T]): 2. merge into existing object 3. remap existing object """ + self = self.__self__ if __object.id in self._contains_ids: return @@ -233,7 +235,7 @@ class Collection(Generic[T]): # now the ugly part # replace all refs of the other element with this one - self._risky_merge(equal_collection) + self = self._risky_merge(equal_collection) def contain_collection_inside(self, sub_collection: "Collection"): """ @@ -253,6 +255,10 @@ class Collection(Generic[T]): def __len__(self) -> int: return len(self._data) + sum(len(collection) for collection in self.children) + @property + def empty(self) -> bool: + return self.__len__() == 0 + def __iter__(self) -> Iterator[T]: for element in self._data: yield element diff --git a/src/music_kraken/objects/contact.py b/src/music_kraken/objects/contact.py index 2041297..e2e024f 100644 --- a/src/music_kraken/objects/contact.py +++ b/src/music_kraken/objects/contact.py @@ -1,10 +1,10 @@ from typing import Optional, List, Tuple from ..utils.enums.contact import ContactMethod -from .parents import DatabaseObject +from .parents import OuterProxy -class Contact(DatabaseObject): +class Contact(OuterProxy): COLLECTION_STRING_ATTRIBUTES = tuple() SIMPLE_STRING_ATTRIBUTES = { "contact_method": None, @@ -18,7 +18,8 @@ class Contact(DatabaseObject): ('value', self.value), ] - def __init__(self, contact_method: ContactMethod, value: str) -> None: + def __init__(self, contact_method: ContactMethod, value: str, **kwargs) -> None: + super().__init__(**kwargs) self.contact_method: ContactMethod = contact_method self.value: str = value diff --git a/src/music_kraken/objects/country.py b/src/music_kraken/objects/country.py index c9aeaa3..3b60bf5 100644 --- a/src/music_kraken/objects/country.py +++ b/src/music_kraken/objects/country.py @@ -72,7 +72,6 @@ class Language: alpha_2: str alpha_3: str name: str - numeric: int @classmethod def by_pycountry(cls, language) -> Language: @@ -82,12 +81,11 @@ class Language: alpha_2=alpha_2, alpha_3=language.alpha_3, name=language.name, - numeric=language.numeric, ) @classmethod def by_alpha_2(cls, alpha_2: str) -> Language: - return cls.by_pycountry(pycountry.languages.get(alpha_2=alpha_2)) + return cls.by_pycountry(pycountry.languages.get(alpha_2=alpha_2.upper())) @classmethod def by_alpha_3(cls, alpha_3: str) -> Language: diff --git a/src/music_kraken/objects/lyrics.py b/src/music_kraken/objects/lyrics.py index bcd1c1e..f35e186 100644 --- a/src/music_kraken/objects/lyrics.py +++ b/src/music_kraken/objects/lyrics.py @@ -2,12 +2,12 @@ from typing import List from collections import defaultdict import pycountry -from .parents import DatabaseObject +from .parents import OuterProxy from .source import Source, SourceCollection from .formatted_text import FormattedText -class Lyrics(DatabaseObject): +class Lyrics(OuterProxy): COLLECTION_STRING_ATTRIBUTES = ("source_collection",) SIMPLE_STRING_ATTRIBUTES = { "text": FormattedText(), @@ -23,7 +23,7 @@ class Lyrics(DatabaseObject): source_list: List[Source] = None, **kwargs ) -> None: - DatabaseObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) + super().__init__(_id=_id, dynamic=dynamic, **kwargs) self.text: FormattedText = text or FormattedText() self.language: pycountry.Languages = language diff --git a/src/music_kraken/objects/parents.py b/src/music_kraken/objects/parents.py index 13a90f6..1ed5379 100644 --- a/src/music_kraken/objects/parents.py +++ b/src/music_kraken/objects/parents.py @@ -1,31 +1,17 @@ from __future__ import annotations + import random from collections import defaultdict -from typing import Optional, Dict, Tuple, List, Type, Generic, TypeVar, Any -from dataclasses import dataclass +from typing import Optional, Dict, Tuple, List, Type, Generic, Any, TypeVar from .metadata import Metadata -from .option import Options +from ..utils.config import logging_settings from ..utils.shared import HIGHEST_ID -from ..utils.config import main_settings, logging_settings from ..utils.support_classes.hacking import MetaClass -from ..utils.exception.objects import IsDynamicException LOGGER = logging_settings["object_logger"] -P = TypeVar('P') - - -@dataclass -class StaticAttribute(Generic[P]): - name: str - - default_value: Any = None - weight: float = 0 - - is_collection: bool = False - is_downwards_collection: bool = False - is_upwards_collection: bool = False +P = TypeVar("P", bound="OuterProxy") class InnerData: @@ -68,13 +54,43 @@ class InnerData: self.__setattr__(key, value) +class Meta(type): + def __new__(meta, classname, bases, classDict): + for key, value in classDict.items(): + if (not key.islower()) or key.startswith("_") or (key.startswith("__") and key.endswith("__")): + continue -class OuterProxy: + if hasattr(value, "__call__") or isinstance(value, property) or isinstance(value, classmethod): + continue + + print("hi", type(value)) + print(key, value) + + new_instance = type.__new__(meta, classname, bases, classDict) + + return new_instance + + + +class OuterProxy(metaclass=Meta): """ Wraps the inner data, and provides apis, to naturally access those values. """ - _default_factories: dict + _default_factories: dict = {} + + def __new__(cls, *args, **kwargs): + for key, value in cls.__dict__["__annotations__"].items(): + if (not key.islower()) or key.startswith("_") or (key.startswith("__") and key.endswith("__")): + continue + + if key in cls._default_factories: + continue + + cls._default_factories[key] = lambda: None + + return super().__new__(cls) + def __init__(self, _id: int = None, dynamic: bool = False, **kwargs): _automatic_id: bool = False @@ -91,6 +107,17 @@ class OuterProxy: kwargs["id"] = _id kwargs["dynamic"] = dynamic + key: str + for key, value in super().__getattribute__("__dict__").items(): + if (not key.islower()) or key.startswith("_") or (key.startswith("__") and key.endswith("__")): + continue + + if hasattr(value, "__call__") or isinstance(value, property) or isinstance(value, classmethod): + continue + + print(type(value)) + print(key, value) + for name, factory in type(self)._default_factories.items(): if name not in kwargs: kwargs[name] = factory() @@ -160,222 +187,32 @@ class OuterProxy: self._inner.__merge__(__other._inner, override=override) __other._inner = self._inner - -class Attribute(Generic[P]): - def __init__(self, database_object: "DatabaseObject", static_attribute: StaticAttribute) -> None: - self.database_object: DatabaseObject = database_object - self.static_attribute: StaticAttribute = static_attribute + @property + def metadata(self) -> Metadata: + """ + This is an interface. + :return: + """ + return Metadata() @property - def name(self) -> str: - return self.static_attribute.name - - def get(self) -> P: - return self.database_object.__getattribute__(self.name) - - def set(self, value: P): - self.database_object.__setattr__(self.name, value) - - -class DatabaseObject(metaclass=MetaClass): - COLLECTION_STRING_ATTRIBUTES: tuple = tuple() - SIMPLE_STRING_ATTRIBUTES: dict = dict() - - # contains all collection attributes, which describe something "smaller" - # e.g. album has songs, but not artist. - DOWNWARDS_COLLECTION_STRING_ATTRIBUTES: tuple = tuple() - UPWARDS_COLLECTION_STRING_ATTRIBUTES: tuple = tuple() - - STATIC_ATTRIBUTES: List[StaticAttribute] = list() - - def __init__(self, _id: int = None, dynamic: bool = False, **kwargs) -> None: - self.automatic_id: bool = False - - if _id is None and not dynamic: - """ - generates a random integer id - 64 bit integer, but this is defined in shared.py in ID_BITS - the range is defined in the Tuple ID_RANGE - """ - _id = random.randint(0, HIGHEST_ID) - self.automatic_id = True - # LOGGER.debug(f"Id for {type(self).__name__} isn't set. Setting to {_id}") - - self._attributes: List[Attribute] = [] - self._simple_attribute_list: List[Attribute] = [] - self._collection_attributes: List[Attribute] = [] - self._downwards_collection_attributes: List[Attribute] = [] - self._upwards_collection_attributes: List[Attribute] = [] - - for static_attribute in self.STATIC_ATTRIBUTES: - attribute: Attribute = Attribute(self, static_attribute) - self._attributes.append(attribute) - - if static_attribute.is_collection: - if static_attribute.is_collection: - self._collection_attributes.append(attribute) - if static_attribute.is_upwards_collection: - self._upwards_collection_attributes.append(attribute) - if static_attribute.is_downwards_collection: - self._downwards_collection_attributes.append(attribute) - else: - self._simple_attribute_list.append(attribute) - - # The id can only be None, if the object is dynamic (self.dynamic = True) - self.id: Optional[int] = _id - - self.dynamic = dynamic - self.build_version = -1 - - super().__init__() - - @property - def upwards_collection(self) -> Collection: - for attribute in self._upwards_collection_attributes: - yield attribute.get() - - @property - def downwards_collection(self) -> Collection: - for attribute in self._downwards_collection_attributes: - yield attribute.get() - - @property - def all_collections(self) -> Collection: - for attribute in self._collection_attributes: - yield attribute.get() - - def __hash__(self): - if self.dynamic: - raise TypeError("Dynamic DatabaseObjects are unhashable.") - return self.id - - def __deep_eq__(self, other) -> bool: - if not isinstance(other, type(self)): - return False - - return super().__eq__(other) - - def __eq__(self, other) -> bool: - if not isinstance(other, type(self)): - return False - - if super().__eq__(other): - return True - - # add the checks for dynamic, to not throw an exception - if not self.dynamic and not other.dynamic and self.id == other.id: - return True - - temp_attribute_map: Dict[str, set] = defaultdict(set) - - # building map with sets - for name, value in self.indexing_values: - temp_attribute_map[name].add(value) - - # check against the attributes of the other object - for name, other_value in other.indexing_values: - if other_value in temp_attribute_map[name]: - return True - - return False + def options(self) -> List[P]: + return [self] @property def indexing_values(self) -> List[Tuple[str, object]]: """ - returns a map of the name and values of the attributes. - This helps in comparing classes for equal data (eg. being the same song but different attributes) + This is an interface. + It is supposed to return a map of the name and values for all important attributes. + This helps in comparing classes for equal data (e.g. being the same song but different attributes) + + TODO + Rewrite this approach into a approach, that is centered around statistics, and not binaries. + Instead of: one of this matches, it is the same + This: If enough attributes are similar enough, they are the same Returns: List[Tuple[str, object]]: the first element in the tuple is the name of the attribute, the second the value. """ - return list() - - def merge(self, other, override: bool = False, replace_all_refs: bool = False): - print("merge") - - if other is None: - return - - if self.__deep_eq__(other): - return - - if not isinstance(other, type(self)): - LOGGER.warning(f"can't merge \"{type(other)}\" into \"{type(self)}\"") - return - - for collection in self._collection_attributes: - if hasattr(self, collection.name) and hasattr(other, collection.name): - if collection.get() is not getattr(other, collection.name): - pass - collection.get().extend(getattr(other, collection.name)) - - for simple_attribute, default_value in type(self).SIMPLE_STRING_ATTRIBUTES.items(): - if getattr(other, simple_attribute) == default_value: - continue - - if override or getattr(self, simple_attribute) == default_value: - setattr(self, simple_attribute, getattr(other, simple_attribute)) - - if replace_all_refs: - self._risky_merge(other) - - def strip_details(self): - for collection in type(self).DOWNWARDS_COLLECTION_STRING_ATTRIBUTES: - getattr(self, collection).clear() - - @property - def metadata(self) -> Metadata: - return Metadata() - - @property - def options(self) -> List["DatabaseObject"]: - return [self] - - @property - def option_string(self) -> str: - return self.__repr__() - - def _build_recursive_structures(self, build_version: int, merge: False): - pass - - def compile(self, merge_into: bool = False): - """ - compiles the recursive structures, - and does depending on the object some other stuff. - - no need to override if only the recursive structure should be build. - override self.build_recursive_structures() instead - """ - - self._build_recursive_structures(build_version=random.randint(0, 99999), merge=merge_into) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - pass - - def add_list_of_other_objects(self, object_list: List["DatabaseObject"]): - d: Dict[Type[DatabaseObject], List[DatabaseObject]] = defaultdict(list) - - for db_object in object_list: - d[type(db_object)].append(db_object) - - for key, value in d.items(): - self._add_other_db_objects(key, value) - - -class MainObject(DatabaseObject): - """ - This is the parent class for all "main" data objects: - - Song - - Album - - Artist - - Label - - It has all the functionality of the "DatabaseObject" (it inherits from said class) - but also some added functions as well. - """ - - def __init__(self, _id: int = None, dynamic: bool = False, **kwargs): - DatabaseObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.additional_arguments: dict = kwargs + return [] diff --git a/src/music_kraken/objects/song.py b/src/music_kraken/objects/song.py index a74e261..2067648 100644 --- a/src/music_kraken/objects/song.py +++ b/src/music_kraken/objects/song.py @@ -2,7 +2,7 @@ from __future__ import annotations import random from collections import defaultdict -from typing import List, Optional, Dict, Tuple, Type +from typing import List, Optional, Dict, Tuple, Type, Union import pycountry @@ -17,7 +17,7 @@ from .metadata import ( Metadata ) from .option import Options -from .parents import DatabaseObject, StaticAttribute +from .parents import OuterProxy, P from .source import Source, SourceCollection from .target import Target from .country import Language, Country @@ -59,7 +59,7 @@ class Song(Base): "main_artist_collection": Collection, "album_collection": Collection, - "feature_artist_collection": Collection + "feature_artist_collection": Collection, } """ @@ -161,14 +161,10 @@ class Song(Base): f"by Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.main_artist_collection)}) " \ f"feat. Artist({OPTION_STRING_DELIMITER.join(artist.name for artist in self.feature_artist_collection)})" - @property - def options(self) -> List[DatabaseObject]: - """ - Return a list of related objects including the song object, album object, main artist objects, and - feature artist objects. - :return: a list of objects that are related to the Song object - """ + + @property + def options(self) -> List[P]: options = self.main_artist_collection.shallow_list options.extend(self.feature_artist_collection) options.extend(self.album_collection) @@ -226,6 +222,11 @@ class Album(Base): "language": lambda: Language.by_alpha_2("en"), "date": ID3Timestamp, "notes": FormattedText, + + "source_collection": SourceCollection, + "artist_collection": Collection, + "song_collection": Collection, + "label_collection": Collection, } DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("song_collection", ) @@ -236,42 +237,6 @@ class Album(Base): "main_artist_collection": self.artist_collection } - def _build_recursive_structures(self, build_version: int, merge: bool): - if build_version == self.build_version: - return - self.build_version = build_version - - song: Song - for song in self.song_collection: - song.album_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - song._build_recursive_structures(build_version=build_version, merge=merge) - - artist: Artist - for artist in self.artist_collection: - artist.main_album_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - artist._build_recursive_structures(build_version=build_version, merge=merge) - - label: Label - for label in self.label_collection: - label.album_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - label._build_recursive_structures(build_version=build_version, merge=merge) - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - self.song_collection.extend(object_list) - return - - if object_type is Artist: - self.artist_collection.extend(object_list) - return - - if object_type is Album: - return - - if object_type is Label: - self.label_collection.extend(object_list) - return - @property def indexing_values(self) -> List[Tuple[str, object]]: return [ @@ -311,7 +276,7 @@ class Album(Base): f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" @property - def options(self) -> List[DatabaseObject]: + def options(self) -> List[P]: options = self.artist_collection.shallow_list options.append(self) options.extend(self.song_collection) @@ -434,122 +399,50 @@ class Artist(Base): "unformated_location": None, } + name: str + unified_name: str + country: Country + formed_in: ID3Timestamp + notes: FormattedText + lyrical_themes: List[str] + + general_genre: str + unformated_location: str + + source_collection: SourceCollection + contact_collection: Collection[Contact] + + feature_song_collection: Collection[Song] + main_album_collection: Collection[Album] + label_collection: Collection[Label] + + _default_factories = { + "formed_in": ID3Timestamp, + "notes": FormattedText, + "lyrical_themes": list, + "general_genre": lambda: "", + "source_collection": SourceCollection, + "feature_song_collection": Collection, + "main_album_collection": Collection, + "contact_collection": Collection, + "label_collection": Collection, + } + DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("feature_song_collection", "main_album_collection") UPWARDS_COLLECTION_STRING_ATTRIBUTES = ("label_collection", ) + def __init_collections__(self): + self.feature_song_collection.append_object_to_attribute = { + "feature_artist_collection": self + } - STATIC_ATTRIBUTES = [ - StaticAttribute(name="name", weight=.5), - StaticAttribute(name="unified_name", weight=.3), - StaticAttribute(name="country"), - StaticAttribute(name="formed_in", default_value=ID3Timestamp()), - StaticAttribute(name="lyrical_themes", default_value=[]), - StaticAttribute(name="general_genre", default_value=""), - StaticAttribute(name="notes", default_value=FormattedText()), - StaticAttribute(name="unformated_location"), - - StaticAttribute(name="source_collection", is_collection=True), - StaticAttribute(name="contact_collection", is_collection=True), - StaticAttribute(name="feature_song_collection", is_collection=True, is_downwards_collection=True), - StaticAttribute(name="main_album_collection", is_collection=True, is_downwards_collection=True), - StaticAttribute(name="label_collection", is_collection=True, is_upwards_collection=True), - ] - - def __init__( - self, - _id: int = None, - dynamic: bool = False, - name: str = None, - unified_name: str = None, - source_list: List[Source] = None, - feature_song_list: List[Song] = None, - main_album_list: List[Album] = None, - contact_list: List[Contact] = None, - notes: FormattedText = None, - lyrical_themes: List[str] = None, - general_genre: str = "", - country: CountryTyping = None, - formed_in: ID3Timestamp = None, - label_list: List['Label'] = None, - unformated_location: str = None, - **kwargs - ): - Base.__init__(self, _id=_id, dynamic=dynamic, **kwargs) - - self.name: str = name - self.unified_name: str = unified_name - if unified_name is None and name is not None: - self.unified_name = unify(name) - - """ - TODO implement album type and notes - """ - self.country: CountryTyping = country - self.formed_in: ID3Timestamp = formed_in - """ - notes, general genre, lyrics themes are attributes - which are meant to only use in outputs to describe the object - i mean do as you want but there is no strict rule about em so good luck - """ - self.notes: FormattedText = notes or FormattedText() - - self.lyrical_themes: List[str] = lyrical_themes or [] - self.general_genre = general_genre - self.unformated_location: Optional[str] = unformated_location - - self.source_collection: SourceCollection = SourceCollection(source_list) - self.contact_collection: Collection[Label] = Collection(data=contact_list) - - self.feature_song_collection: Collection[Song] = Collection( - data=feature_song_list, - append_object_to_attribute={ - "feature_artist_collection": self - } - ) + self.main_album_collection.append_object_to_attribute = { + "artist_collection": self + } - self.main_album_collection: Collection[Album] = Collection( - data=main_album_list, - append_object_to_attribute={ - "artist_collection": self - } - ) - - self.label_collection: Collection[Label] = Collection( - data=label_list, - append_object_to_attribute={ - "current_artist_collection": self - } - ) - - - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - # this doesn't really make sense - # self.feature_song_collection.extend(object_list) - return - - if object_type is Artist: - return - - if object_type is Album: - self.main_album_collection.extend(object_list) - return - - if object_type is Label: - self.label_collection.extend(object_list) - return - - def compile(self, merge_into: bool = False): - """ - compiles the recursive structures, - and does depending on the object some other stuff. - - no need to override if only the recursive structure should be built. - override self.build_recursive_structures() instead - """ - - self.update_albumsort() - self._build_recursive_structures(build_version=random.randint(0, 99999), merge=merge_into) + self.label_collection.append_object_to_attribute = { + "current_artist_collection": self + } def update_albumsort(self): """ @@ -564,7 +457,7 @@ class Artist(Base): if len(self.main_album_collection) <= 0: return - type_section: Dict[AlbumType] = defaultdict(lambda: 2, { + type_section: Dict[AlbumType, int] = defaultdict(lambda: 2, { AlbumType.OTHER: 0, # if I don't know it, I add it to the first section AlbumType.STUDIO_ALBUM: 0, AlbumType.EP: 0, @@ -608,27 +501,6 @@ class Artist(Base): # replace the old collection with the new one self.main_album_collection: Collection = Collection(data=album_list, element_type=Album) - - def _build_recursive_structures(self, build_version: int, merge: False): - if build_version == self.build_version: - return - self.build_version = build_version - - song: Song - for song in self.feature_song_collection: - song.feature_artist_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - song._build_recursive_structures(build_version=build_version, merge=merge) - - album: Album - for album in self.main_album_collection: - album.artist_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - album._build_recursive_structures(build_version=build_version, merge=merge) - - label: Label - for label in self.label_collection: - label.current_artist_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - label._build_recursive_structures(build_version=build_version, merge=merge) - @property def indexing_values(self) -> List[Tuple[str, object]]: return [ @@ -664,16 +536,12 @@ class Artist(Base): f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" @property - def options(self) -> List[DatabaseObject]: + def options(self) -> List[P]: options = [self] options.extend(self.main_album_collection) options.extend(self.feature_song_collection) return options - @property - def country_string(self): - return self.country.alpha_3 - @property def feature_album(self) -> Album: return Album( @@ -712,22 +580,27 @@ Label class Label(Base): COLLECTION_STRING_ATTRIBUTES = ("album_collection", "current_artist_collection") - SIMPLE_STRING_ATTRIBUTES = { - "name": None, - "unified_name": None, - "notes": FormattedText() - } + DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = COLLECTION_STRING_ATTRIBUTES - STATIC_ATTRIBUTES = [ - StaticAttribute(name="name", weight=.5), - StaticAttribute(name="unified_name", weight=.3), - StaticAttribute(name="notes", default_value=FormattedText()), + name: str + unified_name: str + notes: FormattedText - StaticAttribute(name="album_collection", is_collection=True, is_downwards_collection=True), - StaticAttribute(name="current_artist_collection", is_collection=True, is_downwards_collection=True), - ] + source_collection: SourceCollection + contact_collection: Collection[Contact] + + album_collection: Collection[Album] + current_artist_collection: Collection[Artist] + + _default_factories = { + "notes": FormattedText, + "album_collection": Collection, + "current_artist_collection": Collection, + "source_collection": SourceCollection, + "contact_collection": Collection + } def __init__( self, @@ -753,33 +626,6 @@ class Label(Base): self.album_collection: Collection[Album] = Collection(data=album_list, element_type=Album) self.current_artist_collection: Collection[Artist] = Collection(data=current_artist_list, element_type=Artist) - def _add_other_db_objects(self, object_type: Type["DatabaseObject"], object_list: List["DatabaseObject"]): - if object_type is Song: - return - - if object_type is Artist: - self.current_artist_collection.extend(object_list) - return - - if object_type is Album: - self.album_collection.extend(object_list) - return - - def _build_recursive_structures(self, build_version: int, merge: False): - if build_version == self.build_version: - return - self.build_version = build_version - - album: Album - for album in self.album_collection: - album.label_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - album._build_recursive_structures(build_version=build_version, merge=merge) - - artist: Artist - for artist in self.current_artist_collection: - artist.label_collection.append(self, merge_on_conflict=merge, merge_into_existing=False) - artist._build_recursive_structures(build_version=build_version, merge=merge) - @property def indexing_values(self) -> List[Tuple[str, object]]: return [ @@ -789,7 +635,7 @@ class Label(Base): ] @property - def options(self) -> List[DatabaseObject]: + def options(self) -> List[P]: options = [self] options.extend(self.current_artist_collection.shallow_list) options.extend(self.album_collection.shallow_list) diff --git a/src/music_kraken/objects/source.py b/src/music_kraken/objects/source.py index 4fb1e40..58d4b53 100644 --- a/src/music_kraken/objects/source.py +++ b/src/music_kraken/objects/source.py @@ -7,11 +7,11 @@ from ..utils.enums.source import SourcePages, SourceTypes from ..utils.config import youtube_settings from .metadata import Mapping, Metadata -from .parents import DatabaseObject +from .parents import OuterProxy from .collection import Collection -class Source(DatabaseObject): +class Source(OuterProxy): """ create somehow like that ```python @@ -19,6 +19,13 @@ class Source(DatabaseObject): Source(src="youtube", url="https://youtu.be/dfnsdajlhkjhsd") ``` """ + + page_enum: SourcePages + referer_page: SourcePages + + url: str + audio_url: str + COLLECTION_STRING_ATTRIBUTES = tuple() SIMPLE_STRING_ATTRIBUTES = { "page_enum": None, @@ -27,21 +34,11 @@ class Source(DatabaseObject): "audio_url": None } - def __init__( - self, - page_enum: SourcePages, - url: str = None, - id_: str = None, - referer_page: SourcePages = None, - adio_url: str = None - ) -> None: - DatabaseObject.__init__(self, id_=id_) + def __init__(self, page_enum: SourcePages, referer_page: SourcePages = None, **kwargs) -> None: + if referer_page is None: + referer_page = page_enum - self.page_enum = page_enum - self.referer_page = page_enum if referer_page is None else referer_page - - self.url = url - self.audio_url = adio_url + super().__init__(page_enum=page_enum, referer_page=referer_page, **kwargs) @classmethod def match_url(cls, url: str, referer_page: SourcePages) -> Optional["Source"]: diff --git a/src/music_kraken/objects/target.py b/src/music_kraken/objects/target.py index 4faee32..a5cc0dc 100644 --- a/src/music_kraken/objects/target.py +++ b/src/music_kraken/objects/target.py @@ -5,7 +5,7 @@ import logging import requests from tqdm import tqdm -from .parents import DatabaseObject +from .parents import OuterProxy from ..utils.config import main_settings, logging_settings from ..utils.string_processing import fit_to_file_system @@ -13,7 +13,7 @@ from ..utils.string_processing import fit_to_file_system LOGGER = logging.getLogger("target") -class Target(DatabaseObject): +class Target(OuterProxy): """ create somehow like that ```python diff --git a/src/music_kraken/utils/support_classes/query.py b/src/music_kraken/utils/support_classes/query.py index 239096b..d6d70c9 100644 --- a/src/music_kraken/utils/support_classes/query.py +++ b/src/music_kraken/utils/support_classes/query.py @@ -1,6 +1,6 @@ from typing import Optional, List -from ...objects import DatabaseObject, Artist, Album, Song +from ...objects import Artist, Album, Song, DatabaseObject class Query: def __init__(