feat: fixed bugs

This commit is contained in:
Hellow 2023-12-19 22:11:46 +01:00
parent 22b32b0c50
commit a2a2297354
13 changed files with 174 additions and 485 deletions

View File

@ -1,5 +1,6 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<settings> <settings>
<option name="PROJECT_PROFILE" value="Default" />
<option name="USE_PROJECT_PROFILE" value="false" /> <option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" /> <version value="1.0" />
</settings> </settings>

View File

@ -4,4 +4,7 @@
<option name="sdkName" value="Python 3.10 (music-downloader)" /> <option name="sdkName" value="Python 3.10 (music-downloader)" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (music-downloader)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (music-downloader)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>
</project> </project>

View File

@ -25,7 +25,6 @@ print(other_song.__dict__)
print(song) print(song)
"""
only_smile = Artist( only_smile = Artist(
name="Only Smile", name="Only Smile",
source_list=[Source(SourcePages.BANDCAMP, "https://onlysmile.bandcamp.com/")], source_list=[Source(SourcePages.BANDCAMP, "https://onlysmile.bandcamp.com/")],
@ -126,7 +125,6 @@ for _id, _object in objects_by_id.items():
print(only_smile) print(only_smile)
""" """
"""
c = Collection([Song(title="hi"), Song(title="hi2"), Song(title="hi3")]) c = Collection([Song(title="hi"), Song(title="hi2"), Song(title="hi3")])
c1 = Collection([Song(title="he"), Song(title="hi5")]) c1 = Collection([Song(title="he"), Song(title="hi5")])
c11 = Collection([Song(title="wow how ultra subby", isrc="hiii")]) c11 = Collection([Song(title="wow how ultra subby", isrc="hiii")])

View File

@ -1,5 +1,5 @@
from typing_extensions import TypeVar
from .option import Options from .option import Options
from .parents import DatabaseObject
from .metadata import Metadata, Mapping as ID3Mapping, ID3Timestamp from .metadata import Metadata, Mapping as ID3Mapping, ID3Timestamp
@ -19,3 +19,5 @@ from .collection import Collection
from .country import Country from .country import Country
from .contact import Contact from .contact import Contact
from .parents import OuterProxy as DatabaseObject

View File

@ -54,6 +54,7 @@ class Collection(Generic[T]):
self._indexed_values[name].add(value) self._indexed_values[name].add(value)
self._indexed_to_objects[value].append(__object) self._indexed_to_objects[value].append(__object)
print(from_map)
if not from_map: if not from_map:
for attribute, new_object in self.contain_given_in_attribute.items(): for attribute, new_object in self.contain_given_in_attribute.items():
__object.__getattribute__(attribute).contain_collection_inside(new_object) __object.__getattribute__(attribute).contain_collection_inside(new_object)
@ -143,6 +144,7 @@ class Collection(Generic[T]):
2. merge into existing object 2. merge into existing object
3. remap existing object 3. remap existing object
""" """
self = self.__self__
if __object.id in self._contains_ids: if __object.id in self._contains_ids:
return return
@ -233,7 +235,7 @@ class Collection(Generic[T]):
# now the ugly part # now the ugly part
# replace all refs of the other element with this one # 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"): def contain_collection_inside(self, sub_collection: "Collection"):
""" """
@ -253,6 +255,10 @@ class Collection(Generic[T]):
def __len__(self) -> int: def __len__(self) -> int:
return len(self._data) + sum(len(collection) for collection in self.children) 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]: def __iter__(self) -> Iterator[T]:
for element in self._data: for element in self._data:
yield element yield element

View File

@ -1,10 +1,10 @@
from typing import Optional, List, Tuple from typing import Optional, List, Tuple
from ..utils.enums.contact import ContactMethod from ..utils.enums.contact import ContactMethod
from .parents import DatabaseObject from .parents import OuterProxy
class Contact(DatabaseObject): class Contact(OuterProxy):
COLLECTION_STRING_ATTRIBUTES = tuple() COLLECTION_STRING_ATTRIBUTES = tuple()
SIMPLE_STRING_ATTRIBUTES = { SIMPLE_STRING_ATTRIBUTES = {
"contact_method": None, "contact_method": None,
@ -18,7 +18,8 @@ class Contact(DatabaseObject):
('value', self.value), ('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.contact_method: ContactMethod = contact_method
self.value: str = value self.value: str = value

View File

@ -72,7 +72,6 @@ class Language:
alpha_2: str alpha_2: str
alpha_3: str alpha_3: str
name: str name: str
numeric: int
@classmethod @classmethod
def by_pycountry(cls, language) -> Language: def by_pycountry(cls, language) -> Language:
@ -82,12 +81,11 @@ class Language:
alpha_2=alpha_2, alpha_2=alpha_2,
alpha_3=language.alpha_3, alpha_3=language.alpha_3,
name=language.name, name=language.name,
numeric=language.numeric,
) )
@classmethod @classmethod
def by_alpha_2(cls, alpha_2: str) -> Language: 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 @classmethod
def by_alpha_3(cls, alpha_3: str) -> Language: def by_alpha_3(cls, alpha_3: str) -> Language:

View File

@ -2,12 +2,12 @@ from typing import List
from collections import defaultdict from collections import defaultdict
import pycountry import pycountry
from .parents import DatabaseObject from .parents import OuterProxy
from .source import Source, SourceCollection from .source import Source, SourceCollection
from .formatted_text import FormattedText from .formatted_text import FormattedText
class Lyrics(DatabaseObject): class Lyrics(OuterProxy):
COLLECTION_STRING_ATTRIBUTES = ("source_collection",) COLLECTION_STRING_ATTRIBUTES = ("source_collection",)
SIMPLE_STRING_ATTRIBUTES = { SIMPLE_STRING_ATTRIBUTES = {
"text": FormattedText(), "text": FormattedText(),
@ -23,7 +23,7 @@ class Lyrics(DatabaseObject):
source_list: List[Source] = None, source_list: List[Source] = None,
**kwargs **kwargs
) -> None: ) -> None:
DatabaseObject.__init__(self, _id=_id, dynamic=dynamic, **kwargs) super().__init__(_id=_id, dynamic=dynamic, **kwargs)
self.text: FormattedText = text or FormattedText() self.text: FormattedText = text or FormattedText()
self.language: pycountry.Languages = language self.language: pycountry.Languages = language

View File

@ -1,31 +1,17 @@
from __future__ import annotations from __future__ import annotations
import random import random
from collections import defaultdict from collections import defaultdict
from typing import Optional, Dict, Tuple, List, Type, Generic, TypeVar, Any from typing import Optional, Dict, Tuple, List, Type, Generic, Any, TypeVar
from dataclasses import dataclass
from .metadata import Metadata from .metadata import Metadata
from .option import Options from ..utils.config import logging_settings
from ..utils.shared import HIGHEST_ID from ..utils.shared import HIGHEST_ID
from ..utils.config import main_settings, logging_settings
from ..utils.support_classes.hacking import MetaClass from ..utils.support_classes.hacking import MetaClass
from ..utils.exception.objects import IsDynamicException
LOGGER = logging_settings["object_logger"] LOGGER = logging_settings["object_logger"]
P = TypeVar('P') P = TypeVar("P", bound="OuterProxy")
@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
class InnerData: class InnerData:
@ -68,13 +54,43 @@ class InnerData:
self.__setattr__(key, value) 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. 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): def __init__(self, _id: int = None, dynamic: bool = False, **kwargs):
_automatic_id: bool = False _automatic_id: bool = False
@ -91,6 +107,17 @@ class OuterProxy:
kwargs["id"] = _id kwargs["id"] = _id
kwargs["dynamic"] = dynamic 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(): for name, factory in type(self)._default_factories.items():
if name not in kwargs: if name not in kwargs:
kwargs[name] = factory() kwargs[name] = factory()
@ -160,222 +187,32 @@ class OuterProxy:
self._inner.__merge__(__other._inner, override=override) self._inner.__merge__(__other._inner, override=override)
__other._inner = self._inner __other._inner = self._inner
@property
class Attribute(Generic[P]): def metadata(self) -> Metadata:
def __init__(self, database_object: "DatabaseObject", static_attribute: StaticAttribute) -> None: """
self.database_object: DatabaseObject = database_object This is an interface.
self.static_attribute: StaticAttribute = static_attribute :return:
"""
return Metadata()
@property @property
def name(self) -> str: def options(self) -> List[P]:
return self.static_attribute.name return [self]
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
@property @property
def indexing_values(self) -> List[Tuple[str, object]]: def indexing_values(self) -> List[Tuple[str, object]]:
""" """
returns a map of the name and values of the attributes. This is an interface.
This helps in comparing classes for equal data (eg. being the same song but different attributes) 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: Returns:
List[Tuple[str, object]]: the first element in the tuple is the name of the attribute, the second the value. List[Tuple[str, object]]: the first element in the tuple is the name of the attribute, the second the value.
""" """
return list() return []
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

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import random import random
from collections import defaultdict from collections import defaultdict
from typing import List, Optional, Dict, Tuple, Type from typing import List, Optional, Dict, Tuple, Type, Union
import pycountry import pycountry
@ -17,7 +17,7 @@ from .metadata import (
Metadata Metadata
) )
from .option import Options from .option import Options
from .parents import DatabaseObject, StaticAttribute from .parents import OuterProxy, P
from .source import Source, SourceCollection from .source import Source, SourceCollection
from .target import Target from .target import Target
from .country import Language, Country from .country import Language, Country
@ -59,7 +59,7 @@ class Song(Base):
"main_artist_collection": Collection, "main_artist_collection": Collection,
"album_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"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)})" 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 = self.main_artist_collection.shallow_list
options.extend(self.feature_artist_collection) options.extend(self.feature_artist_collection)
options.extend(self.album_collection) options.extend(self.album_collection)
@ -226,6 +222,11 @@ class Album(Base):
"language": lambda: Language.by_alpha_2("en"), "language": lambda: Language.by_alpha_2("en"),
"date": ID3Timestamp, "date": ID3Timestamp,
"notes": FormattedText, "notes": FormattedText,
"source_collection": SourceCollection,
"artist_collection": Collection,
"song_collection": Collection,
"label_collection": Collection,
} }
DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("song_collection", ) DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("song_collection", )
@ -236,42 +237,6 @@ class Album(Base):
"main_artist_collection": self.artist_collection "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 @property
def indexing_values(self) -> List[Tuple[str, object]]: def indexing_values(self) -> List[Tuple[str, object]]:
return [ return [
@ -311,7 +276,7 @@ class Album(Base):
f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})"
@property @property
def options(self) -> List[DatabaseObject]: def options(self) -> List[P]:
options = self.artist_collection.shallow_list options = self.artist_collection.shallow_list
options.append(self) options.append(self)
options.extend(self.song_collection) options.extend(self.song_collection)
@ -434,122 +399,50 @@ class Artist(Base):
"unformated_location": None, "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") DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = ("feature_song_collection", "main_album_collection")
UPWARDS_COLLECTION_STRING_ATTRIBUTES = ("label_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 = [ self.main_album_collection.append_object_to_attribute = {
StaticAttribute(name="name", weight=.5), "artist_collection": self
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), self.label_collection.append_object_to_attribute = {
StaticAttribute(name="contact_collection", is_collection=True), "current_artist_collection": self
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: 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)
def update_albumsort(self): def update_albumsort(self):
""" """
@ -564,7 +457,7 @@ class Artist(Base):
if len(self.main_album_collection) <= 0: if len(self.main_album_collection) <= 0:
return 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.OTHER: 0, # if I don't know it, I add it to the first section
AlbumType.STUDIO_ALBUM: 0, AlbumType.STUDIO_ALBUM: 0,
AlbumType.EP: 0, AlbumType.EP: 0,
@ -608,27 +501,6 @@ class Artist(Base):
# replace the old collection with the new one # replace the old collection with the new one
self.main_album_collection: Collection = Collection(data=album_list, element_type=Album) 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 @property
def indexing_values(self) -> List[Tuple[str, object]]: def indexing_values(self) -> List[Tuple[str, object]]:
return [ return [
@ -664,16 +536,12 @@ class Artist(Base):
f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})" f"under Label({OPTION_STRING_DELIMITER.join([label.name for label in self.label_collection])})"
@property @property
def options(self) -> List[DatabaseObject]: def options(self) -> List[P]:
options = [self] options = [self]
options.extend(self.main_album_collection) options.extend(self.main_album_collection)
options.extend(self.feature_song_collection) options.extend(self.feature_song_collection)
return options return options
@property
def country_string(self):
return self.country.alpha_3
@property @property
def feature_album(self) -> Album: def feature_album(self) -> Album:
return Album( return Album(
@ -712,22 +580,27 @@ Label
class Label(Base): class Label(Base):
COLLECTION_STRING_ATTRIBUTES = ("album_collection", "current_artist_collection") 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 DOWNWARDS_COLLECTION_STRING_ATTRIBUTES = COLLECTION_STRING_ATTRIBUTES
STATIC_ATTRIBUTES = [ name: str
StaticAttribute(name="name", weight=.5), unified_name: str
StaticAttribute(name="unified_name", weight=.3), notes: FormattedText
StaticAttribute(name="notes", default_value=FormattedText()),
StaticAttribute(name="album_collection", is_collection=True, is_downwards_collection=True), source_collection: SourceCollection
StaticAttribute(name="current_artist_collection", is_collection=True, is_downwards_collection=True), 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__( def __init__(
self, self,
@ -753,33 +626,6 @@ class Label(Base):
self.album_collection: Collection[Album] = Collection(data=album_list, element_type=Album) 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) 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 @property
def indexing_values(self) -> List[Tuple[str, object]]: def indexing_values(self) -> List[Tuple[str, object]]:
return [ return [
@ -789,7 +635,7 @@ class Label(Base):
] ]
@property @property
def options(self) -> List[DatabaseObject]: def options(self) -> List[P]:
options = [self] options = [self]
options.extend(self.current_artist_collection.shallow_list) options.extend(self.current_artist_collection.shallow_list)
options.extend(self.album_collection.shallow_list) options.extend(self.album_collection.shallow_list)

View File

@ -7,11 +7,11 @@ from ..utils.enums.source import SourcePages, SourceTypes
from ..utils.config import youtube_settings from ..utils.config import youtube_settings
from .metadata import Mapping, Metadata from .metadata import Mapping, Metadata
from .parents import DatabaseObject from .parents import OuterProxy
from .collection import Collection from .collection import Collection
class Source(DatabaseObject): class Source(OuterProxy):
""" """
create somehow like that create somehow like that
```python ```python
@ -19,6 +19,13 @@ class Source(DatabaseObject):
Source(src="youtube", url="https://youtu.be/dfnsdajlhkjhsd") Source(src="youtube", url="https://youtu.be/dfnsdajlhkjhsd")
``` ```
""" """
page_enum: SourcePages
referer_page: SourcePages
url: str
audio_url: str
COLLECTION_STRING_ATTRIBUTES = tuple() COLLECTION_STRING_ATTRIBUTES = tuple()
SIMPLE_STRING_ATTRIBUTES = { SIMPLE_STRING_ATTRIBUTES = {
"page_enum": None, "page_enum": None,
@ -27,21 +34,11 @@ class Source(DatabaseObject):
"audio_url": None "audio_url": None
} }
def __init__( def __init__(self, page_enum: SourcePages, referer_page: SourcePages = None, **kwargs) -> None:
self, if referer_page is None:
page_enum: SourcePages, referer_page = page_enum
url: str = None,
id_: str = None,
referer_page: SourcePages = None,
adio_url: str = None
) -> None:
DatabaseObject.__init__(self, id_=id_)
self.page_enum = page_enum super().__init__(page_enum=page_enum, referer_page=referer_page, **kwargs)
self.referer_page = page_enum if referer_page is None else referer_page
self.url = url
self.audio_url = adio_url
@classmethod @classmethod
def match_url(cls, url: str, referer_page: SourcePages) -> Optional["Source"]: def match_url(cls, url: str, referer_page: SourcePages) -> Optional["Source"]:

View File

@ -5,7 +5,7 @@ import logging
import requests import requests
from tqdm import tqdm from tqdm import tqdm
from .parents import DatabaseObject from .parents import OuterProxy
from ..utils.config import main_settings, logging_settings from ..utils.config import main_settings, logging_settings
from ..utils.string_processing import fit_to_file_system 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") LOGGER = logging.getLogger("target")
class Target(DatabaseObject): class Target(OuterProxy):
""" """
create somehow like that create somehow like that
```python ```python

View File

@ -1,6 +1,6 @@
from typing import Optional, List from typing import Optional, List
from ...objects import DatabaseObject, Artist, Album, Song from ...objects import Artist, Album, Song, DatabaseObject
class Query: class Query:
def __init__( def __init__(