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 bisect import insort
from collections import Counter
from datetime import datetime, timedelta
from enum import Enum
from functools import wraps
@ -239,6 +240,7 @@ class ListeningReport(NamedTuple):
albums_top_new: ThingWithScrobbles
tracks_top_new: ThingWithScrobbles
listening_time_hours: int
tags: dict[str, tuple[int, ...]]
def to_str(
self,
@ -262,7 +264,8 @@ class ListeningReport(NamedTuple):
+ f"{self.user} — Σ{self.listening_time_hours}h; {self.scrobbles_daily_avg}s/d "
)
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}")
@ -320,6 +323,29 @@ class ListeningReport(NamedTuple):
)
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:
text = "\n".join(basket)
@ -340,7 +366,7 @@ class ListeningReport(NamedTuple):
# detail 2: total period artist count
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})"
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:
text = "\n".join(basket)
@ -392,7 +433,6 @@ def get_listening_report(
limiter: Limiter,
behaviour: Behaviour,
) -> ListeningReport:
target_url: str = f"https://www.last.fm/user/{target}/listening-report/week"
page_res: Response = limiter.limit(get)(
@ -422,6 +462,7 @@ def get_listening_report(
albums_top_new=_get_albums_top_new(page),
tracks_top_new=_get_tracks_top_new(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:
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 (
_v := top.select(".listening-report-secondary-top-item-value")
) 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")
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(
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]]
name = "beautifulsoup4"
@ -330,13 +330,13 @@ files = [
[[package]]
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`."
optional = false
python-versions = ">=3.8"
files = [
{file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"},
{file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"},
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
]
[package.extras]
@ -346,13 +346,13 @@ type = ["mypy (>=1.8)"]
[[package]]
name = "requests"
version = "2.31.0"
version = "2.32.2"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
{file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"},
{file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"},
]
[package.dependencies]
@ -400,13 +400,13 @@ files = [
[[package]]
name = "types-beautifulsoup4"
version = "4.12.0.20240504"
version = "4.12.0.20240511"
description = "Typing stubs for beautifulsoup4"
optional = false
python-versions = ">=3.8"
files = [
{file = "types-beautifulsoup4-4.12.0.20240504.tar.gz", hash = "sha256:d7b7af4ccc52fc22784d33a529695e34329a9bdd5db6a649c9b25cb2c3a148d5"},
{file = "types_beautifulsoup4-4.12.0.20240504-py3-none-any.whl", hash = "sha256:84e04e4473b3c79da04d9d1f89d20a38e5cc92f9c080a8e1f9d36a287b350465"},
{file = "types-beautifulsoup4-4.12.0.20240511.tar.gz", hash = "sha256:004f6096fdd83b19cdbf6cb10e4eae57b10205eccc365d0a69d77da836012e28"},
{file = "types_beautifulsoup4-4.12.0.20240511-py3-none-any.whl", hash = "sha256:7ceda66a93ba28d759d5046d7fec9f4cad2f563a77b3a789efc90bcadafeefd1"},
]
[package.dependencies]
@ -425,13 +425,13 @@ files = [
[[package]]
name = "types-requests"
version = "2.31.0.20240406"
version = "2.32.0.20240523"
description = "Typing stubs for requests"
optional = false
python-versions = ">=3.8"
files = [
{file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"},
{file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"},
{file = "types-requests-2.32.0.20240523.tar.gz", hash = "sha256:26b8a6de32d9f561192b9942b41c0ab2d8010df5677ca8aa146289d11d505f57"},
{file = "types_requests-2.32.0.20240523-py3-none-any.whl", hash = "sha256:f19ed0e2daa74302069bbbbf9e82902854ffa780bc790742a810a9aaa52f65ec"},
]
[package.dependencies]
@ -439,13 +439,13 @@ urllib3 = ">=2"
[[package]]
name = "typing-extensions"
version = "4.11.0"
version = "4.12.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
{file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"},
{file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"},
]
[[package]]

View file

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