0.1.3: basic top tags

This commit is contained in:
Mark Joshwel 2024-05-25 00:44:17 +08:00
parent d3916aa641
commit dfd9e342bb
3 changed files with 93 additions and 22 deletions

View file

@ -32,6 +32,7 @@ For more information, please refer to <http://unlicense.org/>
from argparse import ArgumentParser from argparse import ArgumentParser
from bisect import insort from bisect import insort
from collections import Counter
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import Enum from enum import Enum
from functools import wraps from functools import wraps
@ -239,6 +240,7 @@ class ListeningReport(NamedTuple):
albums_top_new: ThingWithScrobbles albums_top_new: ThingWithScrobbles
tracks_top_new: ThingWithScrobbles tracks_top_new: ThingWithScrobbles
listening_time_hours: int listening_time_hours: int
tags: dict[str, tuple[int, ...]]
def to_str( def to_str(
self, self,
@ -262,7 +264,8 @@ class ListeningReport(NamedTuple):
+ f"{self.user} — Σ{self.listening_time_hours}h; {self.scrobbles_daily_avg}s/d " + f"{self.user} — Σ{self.listening_time_hours}h; {self.scrobbles_daily_avg}s/d "
) )
basket.append( basket.append(
indent(f"<{self.url}>", prefix=(prefix := " " * len(_prefix))) + "\n" indent(f"<{self.url}>", prefix=(prefix := " " * len(_prefix)))
+ "\n"
) )
rmax = len(f"#{leaderboard_n}") rmax = len(f"#{leaderboard_n}")
@ -320,6 +323,29 @@ class ListeningReport(NamedTuple):
) )
basket.append(d4_l + d4_r + d4_url) basket.append(d4_l + d4_r + d4_url)
# detail 5: top five tags
if len(self.tags) > 0:
tag_counter = Counter[str]()
for tag_name, tag_values in self.tags.items():
tag_counter.update({tag_name: sum(tag_values)})
basket.append(
indent(
(
"Top tags".ljust(len(d4_l) - 6)
+ " : "
+ ", ".join(
[
f"{tn} ({tc})"
for tn, tc in tag_counter.most_common(5)
]
)
),
prefix=prefix,
)
)
if not behaviour.lowercase: if not behaviour.lowercase:
text = "\n".join(basket) text = "\n".join(basket)
@ -340,7 +366,7 @@ class ListeningReport(NamedTuple):
# detail 2: total period artist count # detail 2: total period artist count
basket.append( basket.append(
f"{prefix}{self.artists_count} artists (#{leaderboard_artists_pos}): " f"\n{prefix}{self.artists_count} artists (#{leaderboard_artists_pos}): "
+ ( + (
f"[{self.artists[0].name}]({self.artists[0].url})" f"[{self.artists[0].name}]({self.artists[0].url})"
if behaviour.all_the_links if behaviour.all_the_links
@ -370,6 +396,21 @@ class ListeningReport(NamedTuple):
) )
) )
# detail 5: top tags
if len(self.tags) > 0:
tag_counter = Counter[str]()
for tag_name, tag_values in self.tags.items():
tag_counter.update({tag_name: sum(tag_values)})
basket.append(
f"\n{prefix}Top tags"
+ ": "
+ ", ".join(
[f"{tn} ({tc})" for tn, tc in tag_counter.most_common(5)]
)
)
if not behaviour.lowercase: if not behaviour.lowercase:
text = "\n".join(basket) text = "\n".join(basket)
@ -392,7 +433,6 @@ def get_listening_report(
limiter: Limiter, limiter: Limiter,
behaviour: Behaviour, behaviour: Behaviour,
) -> ListeningReport: ) -> ListeningReport:
target_url: str = f"https://www.last.fm/user/{target}/listening-report/week" target_url: str = f"https://www.last.fm/user/{target}/listening-report/week"
page_res: Response = limiter.limit(get)( page_res: Response = limiter.limit(get)(
@ -422,6 +462,7 @@ def get_listening_report(
albums_top_new=_get_albums_top_new(page), albums_top_new=_get_albums_top_new(page),
tracks_top_new=_get_tracks_top_new(page), tracks_top_new=_get_tracks_top_new(page),
listening_time_hours=_get_listening_time_hours(page), listening_time_hours=_get_listening_time_hours(page),
tags=_get_tags(page),
) )
@ -520,7 +561,9 @@ def _get_top_overview(
if len(top.select(select_needle)) == 0: if len(top.select(select_needle)) == 0:
continue continue
assert (_n := top.select(".listening-report-secondary-top-item-name")) is not None assert (
_n := top.select(".listening-report-secondary-top-item-name")
) is not None
assert ( assert (
_v := top.select(".listening-report-secondary-top-item-value") _v := top.select(".listening-report-secondary-top-item-value")
) is not None ) is not None
@ -597,6 +640,34 @@ def _get_tracks_top_new(page: BeautifulSoup) -> ThingWithScrobbles:
return _get_top_new_thing(page=page, select_needle=".top-new-item-type__track") return _get_top_new_thing(page=page, select_needle=".top-new-item-type__track")
def _get_tags(page: BeautifulSoup) -> dict[str, tuple[int, ...]]:
tags: dict[str, tuple[int, ...]] = {}
assert (_1 := page.select_one("#top-tags-over-time")) is not None
assert (_2 := _1.select_one(".js-top-tags-over-time-table")) is not None
assert (_3 := _2.select_one("tbody")) is not None
for tr in _3.select("tr"):
tag_name: str = ""
tag_counts: list[int] = []
for i, td in enumerate(tr.select("td")):
if i == 0:
tag_name = td.text.strip()
else:
assert (
num := td.text.strip()
).isnumeric(), (
"second tag table td onwards should be numeric, but isn't"
)
tag_counts.append(int(num))
tags.update({tag_name: tuple(tag_counts)})
return tags
def _rank( def _rank(
r: ListeningReport, rs: list[ListeningReport], k: Callable[[ListeningReport], int] r: ListeningReport, rs: list[ListeningReport], k: Callable[[ListeningReport], int]
): ):

34
poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]] [[package]]
name = "beautifulsoup4" name = "beautifulsoup4"
@ -330,13 +330,13 @@ files = [
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
version = "4.2.1" version = "4.2.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
{file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
] ]
[package.extras] [package.extras]
@ -346,13 +346,13 @@ type = ["mypy (>=1.8)"]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.31.0" version = "2.32.2"
description = "Python HTTP for Humans." description = "Python HTTP for Humans."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"},
] ]
[package.dependencies] [package.dependencies]
@ -400,13 +400,13 @@ files = [
[[package]] [[package]]
name = "types-beautifulsoup4" name = "types-beautifulsoup4"
version = "4.12.0.20240504" version = "4.12.0.20240511"
description = "Typing stubs for beautifulsoup4" description = "Typing stubs for beautifulsoup4"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "types-beautifulsoup4-4.12.0.20240504.tar.gz", hash = "sha256:d7b7af4ccc52fc22784d33a529695e34329a9bdd5db6a649c9b25cb2c3a148d5"}, {file = "types-beautifulsoup4-4.12.0.20240511.tar.gz", hash = "sha256:004f6096fdd83b19cdbf6cb10e4eae57b10205eccc365d0a69d77da836012e28"},
{file = "types_beautifulsoup4-4.12.0.20240504-py3-none-any.whl", hash = "sha256:84e04e4473b3c79da04d9d1f89d20a38e5cc92f9c080a8e1f9d36a287b350465"}, {file = "types_beautifulsoup4-4.12.0.20240511-py3-none-any.whl", hash = "sha256:7ceda66a93ba28d759d5046d7fec9f4cad2f563a77b3a789efc90bcadafeefd1"},
] ]
[package.dependencies] [package.dependencies]
@ -425,13 +425,13 @@ files = [
[[package]] [[package]]
name = "types-requests" name = "types-requests"
version = "2.31.0.20240406" version = "2.32.0.20240523"
description = "Typing stubs for requests" description = "Typing stubs for requests"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, {file = "types-requests-2.32.0.20240523.tar.gz", hash = "sha256:26b8a6de32d9f561192b9942b41c0ab2d8010df5677ca8aa146289d11d505f57"},
{file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, {file = "types_requests-2.32.0.20240523-py3-none-any.whl", hash = "sha256:f19ed0e2daa74302069bbbbf9e82902854ffa780bc790742a810a9aaa52f65ec"},
] ]
[package.dependencies] [package.dependencies]
@ -439,13 +439,13 @@ urllib3 = ">=2"
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.11.0" version = "4.12.0"
description = "Backported and Experimental Type Hints for Python 3.8+" description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"},
] ]
[[package]] [[package]]

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "lfcircle" name = "lfcircle"
version = "0.1.2" version = "0.1.3"
description = "last.fm statistics generator for your friend circle!" description = "last.fm statistics generator for your friend circle!"
authors = ["Mark Joshwel <mark@joshwel.co>"] authors = ["Mark Joshwel <mark@joshwel.co>"]
license = "Unlicense" license = "Unlicense"