mirror of
https://github.com/Horizontal42/Sort_YM.git
synced 2026-11-10 15:42:02 +03:00
Раньше жанр брался только с альбома (одна строка, часто общая: rock/alternative/pop) - разные по звучанию артисты сваливались в одну крупную корзину. Теперь classify_track сводит жанр трека и жанры артиста (client.artists(), несколько тегов на артиста) на уровне под-жанра: при согласии сигналов или отсутствии одного из них берём имеющийся; при конфликте - если альбомный тег зонтичный (GENERIC_SLUGS) доверяем артисту, если точный - треку (вероятный осознанный эксперимент артиста в другом жанре). Один и тот же артист с разными по духу треками теперь раскладывается по разным плейлистам. Под-жанровые группы меньше config.small_group_min схлопываются в родительскую крупную корзину (report.build_rows, двухпроходно, без циклов - coarse_of детерминирована от fine). Попутно: artist_ids трека может быть None (артист без привязки к каталогу Яндекса, встречается у стороннего/пиратского аплоада) - раньше это улетало в client.artists() как строка "None" и валилось BadRequestError на батче; теперь такие id отфильтровываются на записи и дополнительно на чтении кэша.
97 lines
4.2 KiB
Python
97 lines
4.2 KiB
Python
import pytest
|
||
|
||
from sort_ym import genres
|
||
|
||
|
||
@pytest.fixture
|
||
def catalog():
|
||
# Форма реального дерева client.genres(): "rock"/"rusrock"/"hardrock"/"dnb" - подгенры,
|
||
# "allrock"/"alternative"/"indie"/"punk"/"rap"/"metal" - настоящие корни (root_id == сам слаг).
|
||
# Проверено на живом каталоге (cache/genre_catalog.json).
|
||
return {
|
||
"allrock": {"title": "Рок", "root_id": "allrock"},
|
||
"rock": {"title": "Иностранный рок", "root_id": "allrock"},
|
||
"rusrock": {"title": "Русский рок", "root_id": "allrock"},
|
||
"hardrock": {"title": "Хард-рок", "root_id": "allrock"},
|
||
"alternative": {"title": "Альтернатива", "root_id": "alternative"},
|
||
"indie": {"title": "Инди", "root_id": "indie"},
|
||
"punk": {"title": "Панк", "root_id": "punk"},
|
||
"metal": {"title": "Метал", "root_id": "metal"},
|
||
"numetal": {"title": "Ню-метал", "root_id": "metal"},
|
||
"rap": {"title": "Рэп и хип-хоп", "root_id": "rap"},
|
||
"trap": {"title": "Трэп", "root_id": "rap"},
|
||
"electronics": {"title": "Электроника", "root_id": "electronics"},
|
||
"dnb": {"title": "Драм-н-бейс", "root_id": "electronics"},
|
||
}
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"genre_raw,expected",
|
||
[
|
||
("allrock", "rock"),
|
||
("rock", "rock"),
|
||
("rusrock", "rock"),
|
||
("alternative", "indie-alt"),
|
||
("rap", "rap"),
|
||
("trap", "rap"),
|
||
(None, "other"),
|
||
("", "other"),
|
||
("totally-unknown-slug", "other"),
|
||
],
|
||
)
|
||
def test_bucket_for(catalog, genre_raw, expected):
|
||
assert genres.bucket_for(genre_raw, catalog) == expected
|
||
|
||
|
||
def test_flatten_catalog_walks_sub_genres():
|
||
class FakeGenre:
|
||
def __init__(self, id_, title, sub_genres=None):
|
||
self.id = id_
|
||
self.title = title
|
||
self.sub_genres = sub_genres or []
|
||
|
||
root = FakeGenre("rock", "Рок", [FakeGenre("rusrock", "Русский рок")])
|
||
|
||
flat = genres.flatten_catalog([root])
|
||
|
||
assert flat["rock"] == {"title": "Рок", "root_id": "rock"}
|
||
assert flat["rusrock"] == {"title": "Русский рок", "root_id": "rock"}
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"fines,expected",
|
||
[
|
||
([], None),
|
||
(["indie"], "indie"),
|
||
(["indie", "metal", "indie"], "indie"),
|
||
(["indie", "metal"], "indie"), # равенство частот -> первый по порядку
|
||
],
|
||
)
|
||
def test_dominant(fines, expected):
|
||
assert genres.dominant(fines) == expected
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"genre_raw,artist_genre_lists,expected",
|
||
[
|
||
# согласие трека и артиста -> берём фактический (гранулярно по треку)
|
||
("punk", [["rock", "punk"]], "punk"),
|
||
# конфликт: зонтичный альбомный тег ("rock" в GENERIC_SLUGS) -> доверяем артисту
|
||
# (кейс New Politics: альбом стоит "rock", у артиста только "indie")
|
||
("rock", [["indie"]], "indie"),
|
||
# конфликт: точный альбомный тег (не в GENERIC_SLUGS) -> доверяем треку, даже если
|
||
# у артиста другой основной жанр (настоящий эксперимент артиста в ином жанре)
|
||
("dnb", [["indie"]], "electronics"),
|
||
# пустой genre_raw -> едем по артисту
|
||
(None, [["indie"]], "indie"),
|
||
("", [["indie"]], "indie"),
|
||
# у артиста нет тегов -> едем по треку (genre_raw "rock" резолвится в корень "allrock")
|
||
("rock", [[]], "allrock"),
|
||
# несколько артистов на треке -> голосование dominant по объединённым тегам
|
||
("rock", [["indie"], ["indie", "metal"]], "indie"),
|
||
# ничего не помогло
|
||
(None, [[]], "other"),
|
||
],
|
||
)
|
||
def test_classify_track(catalog, genre_raw, artist_genre_lists, expected):
|
||
assert genres.classify_track(genre_raw, artist_genre_lists, catalog) == expected
|