Skip to content
Merged

draft #870

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions booklog/exports/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from booklog.exports import authors, reviewed_works, stats, timeline_entries
from booklog.exports import authors, reading_entries, reviewed_works, stats
from booklog.exports.repository_data import RepositoryData
from booklog.repository import api as repository_api

Expand All @@ -26,5 +26,5 @@ def export_data() -> None:

authors.export(repository_data)
reviewed_works.export(repository_data)
timeline_entries.export(repository_data)
reading_entries.export(repository_data)
stats.export(repository_data)
6 changes: 1 addition & 5 deletions booklog/exports/authors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,16 @@ def _build_json_author_reviewed_work(
repository_data: RepositoryData,
) -> JsonReviewedWork:
return JsonReviewedWork(
reviewSequence=repository_data.review_sequence_map.get(work.slug, 0),
reviewSequence=repository_data.review_sequence_map.get(work.slug, review.date.isoformat()),
title=work.title,
reviewed=True,
subtitle=work.subtitle,
workYear=work.year,
workYearSequence=repository_data.work_year_sequence_map.get(work.slug, 0),
authorSequence=repository_data.author_sequence_map.get(work.slug, 0),
titleSequence=repository_data.title_sequence_map.get(work.slug, 0),
kind=work.kind,
slug=work.slug,
sortTitle=work.sort_title,
grade=review.grade,
gradeValue=review.grade_value,
gradeSequence=repository_data.grade_sequence_map.get(work.slug, 0),
reviewDate=review.date,
reviewYear=str(review.date.year),
authors=[
Expand Down
3 changes: 1 addition & 2 deletions booklog/exports/json_reviewed_work.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@


class JsonReviewedWork(JsonWork):
reviewSequence: int
reviewSequence: str
grade: str
gradeValue: int
gradeSequence: int
reviewDate: datetime.date
reviewYear: str
reviewed: Literal[True]
3 changes: 0 additions & 3 deletions booklog/exports/json_work.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ class JsonWork(TypedDict):
subtitle: str | None
sortTitle: str
workYear: str
workYearSequence: int
authorSequence: int
titleSequence: int
authors: list[JsonWorkAuthor]
kind: str
slug: str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,50 @@
from booklog.utils.logging import logger


class JsonTimelineEntry(TypedDict):
timelineSequence: int
class JsonReadingEntry(TypedDict):
readingEntrySequence: int
slug: str
edition: str
timelineDate: datetime.date
readingEntryDate: datetime.date
progress: str
reviewed: bool
workYear: str
workYearSequence: int
authorSequence: int
titleSequence: int
title: str
kind: str
authors: list[JsonAuthor]
includedInSlugs: list[str]


def _build_json_timeline_entry_author(
def _build_json_reading_entry_author(
work_author: repository_api.WorkAuthor, authors: list[repository_api.Author]
) -> JsonAuthor:
author = work_author.author(authors)
return JsonAuthor(name=author.name, sortName=author.sort_name, slug=author.slug)


def _build_json_timeline_entry(
def _build_json_reading_entry(
reading: repository_api.Reading,
timeline_entry: repository_api.TimelineEntry,
reading_entry: repository_api.TimelineEntry,
repository_data: RepositoryData,
) -> JsonTimelineEntry:
) -> JsonReadingEntry:
work = reading.work(repository_data.works)
reviewed = bool(work.review(repository_data.reviews))

# Create the key tuple for looking up the sequence number
timeline_key = (str(timeline_entry.date), str(reading.timeline[-1].date), str(reading.sequence))
entry_key = (str(reading_entry.date), str(reading.timeline[-1].date), str(reading.sequence))

return JsonTimelineEntry(
timelineSequence=repository_data.timeline_sequence_map.get(timeline_key, 0),
return JsonReadingEntry(
readingEntrySequence=repository_data.reading_entry_sequence_map.get(entry_key, 0),
slug=work.slug,
edition=reading.edition,
kind=work.kind,
timelineDate=timeline_entry.date,
progress=timeline_entry.progress,
readingEntryDate=reading_entry.date,
progress=reading_entry.progress,
reviewed=reviewed,
workYear=work.year,
workYearSequence=repository_data.work_year_sequence_map.get(work.slug, 0),
authorSequence=repository_data.author_sequence_map.get(work.slug, 0),
titleSequence=repository_data.title_sequence_map.get(work.slug, 0),
title=work.title,
authors=[
_build_json_timeline_entry_author(work_author, repository_data.authors)
_build_json_reading_entry_author(work_author, repository_data.authors)
for work_author in work.work_authors
],
includedInSlugs=[
Expand All @@ -70,17 +64,17 @@ def _build_json_timeline_entry(
def export(repository_data: RepositoryData) -> None:
logger.log("==== Begin exporting {}...", "timeline-entries")

json_progress = [
_build_json_timeline_entry(
json_entries = [
_build_json_reading_entry(
reading=reading,
timeline_entry=timeline_entry,
reading_entry=reading_entry,
repository_data=repository_data,
)
for reading in repository_data.readings
for timeline_entry in reading.timeline
for reading_entry in reading.timeline
]

exporter.serialize_dicts(
sorted(json_progress, key=lambda progress: progress["timelineSequence"], reverse=True),
"timeline-entries",
sorted(json_entries, key=lambda entry: entry["readingEntrySequence"], reverse=True),
"reading-entries",
)
97 changes: 11 additions & 86 deletions booklog/exports/repository_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,113 +10,38 @@ class RepositoryData:
works: list[repository_api.Work]
readings: list[repository_api.Reading]
reviews: list[repository_api.Review]
work_year_sequence_map: dict[str, int] = field(default_factory=dict, init=False)
author_sequence_map: dict[str, int] = field(default_factory=dict, init=False)
title_sequence_map: dict[str, int] = field(default_factory=dict, init=False)
review_sequence_map: dict[str, int] = field(default_factory=dict, init=False)
grade_sequence_map: dict[str, int] = field(default_factory=dict, init=False)
timeline_sequence_map: dict[tuple[str, str, str], int] = field(default_factory=dict, init=False)
review_sequence_map: dict[str, str] = field(default_factory=dict, init=False)
reading_entry_sequence_map: dict[tuple[str, str, str], int] = field(
default_factory=dict, init=False
)
reading_sequence_map: dict[tuple[str, int], int] = field(default_factory=dict, init=False)

def __post_init__(self) -> None:
"""Calculate sequence maps after initialization."""
self.work_year_sequence_map = self._build_work_year_sequence_map()
self.author_sequence_map = self._build_author_sequence_map()
self.title_sequence_map = self._build_title_sequence_map()
self.review_sequence_map = self._build_review_sequence_map()
self.grade_sequence_map = self._build_grade_sequence_map()
self.timeline_sequence_map = self._build_timeline_sequence_map()
self.reading_entry_sequence_map = self._build_reading_entry_sequence_map()
self.reading_sequence_map = self._build_reading_sequence_map()

def _build_work_year_sequence_map(self) -> dict[str, int]:
"""Build a mapping of work slugs to their numeric position based on year-author-title sort.

Returns a dictionary where keys are work slugs and values are their 1-based
position in the sorted order of "{work.year}-{first_author_sort_name}-{work.sort_title}".
"""

def get_sort_key(work: repository_api.Work) -> str:
first_author = work.work_authors[0].author(self.authors)
first_author_sort_name = first_author.sort_name
return f"{work.year}-{first_author_sort_name}-{work.sort_title}"

sorted_works = sorted(self.works, key=get_sort_key)
return {work.slug: idx + 1 for idx, work in enumerate(sorted_works)}

def _build_author_sequence_map(self) -> dict[str, int]:
"""Build a mapping of work slugs to their numeric position based on author-year-title sort.

Returns a dictionary where keys are work slugs and values are their 1-based
position in the sorted order of "{first_author_sort_name}-{work.year}-{work.sort_title}".
"""

def get_sort_key(work: repository_api.Work) -> str:
first_author = work.work_authors[0].author(self.authors)
first_author_sort_name = first_author.sort_name
return f"{first_author_sort_name}-{work.year}-{work.sort_title}"

sorted_works = sorted(self.works, key=get_sort_key)
return {work.slug: idx + 1 for idx, work in enumerate(sorted_works)}

def _build_title_sequence_map(self) -> dict[str, int]:
"""Build a mapping of work slugs to their numeric position based on title-author-year sort.

Returns a dictionary where keys are work slugs and values are their 1-based
position in the sorted order of "{work.sort_title}-{first_author_sort_name}-{work.year}".
"""

def get_sort_key(work: repository_api.Work) -> str:
first_author = work.work_authors[0].author(self.authors)
first_author_sort_name = first_author.sort_name
return f"{work.sort_title}-{first_author_sort_name}-{work.year}"

sorted_works = sorted(self.works, key=get_sort_key)
return {work.slug: idx + 1 for idx, work in enumerate(sorted_works)}

def _build_review_sequence_map(self) -> dict[str, int]:
def _build_review_sequence_map(self) -> dict[str, str]:
"""Build a mapping of work slugs to their review sequence number.

Returns a dictionary where keys are work slugs and values are their 1-based
position in the sorted order of "{review.date}-{most_recent_reading.sequence}".
"""
review_sequences: list[tuple[str, str]] = [] # (work_slug, sort_key)

for review in self.reviews:
work = review.work(self.works)
readings = list(work.readings(self.readings))

if readings:
most_recent_reading = sorted(readings, key=lambda r: r.sequence, reverse=True)[0]
sort_key = f"{review.date}-{most_recent_reading.sequence}"
review_sequences.append((work.slug, sort_key))

# Sort by the sort_key and create mapping
sorted_sequences = sorted(review_sequences, key=lambda x: x[1])
return {work_slug: idx + 1 for idx, (work_slug, _) in enumerate(sorted_sequences)}

def _build_grade_sequence_map(self) -> dict[str, int]:
"""Build a mapping of work slugs to their grade sequence number.

Returns a dictionary where keys are work slugs and values are their 1-based
position in the sorted order of
"{review.grade_value}-{review.date}-{most_recent_reading.sequence}".
"""
grade_sequences: list[tuple[str, str]] = [] # (work_slug, sort_key)
review_sequences: dict[str, str] = {} # (work_slug, sort_key)

for review in self.reviews:
work = review.work(self.works)
readings = list(work.readings(self.readings))

if readings:
most_recent_reading = sorted(readings, key=lambda r: r.sequence, reverse=True)[0]
sort_key = f"{review.grade_value:02d}-{review.date}-{most_recent_reading.sequence}"
grade_sequences.append((work.slug, sort_key))
sort_key = f"{review.date}-{most_recent_reading.sequence:02}"
review_sequences[work.slug] = sort_key

# Sort by the sort_key and create mapping
sorted_sequences = sorted(grade_sequences, key=lambda x: x[1])
return {work_slug: idx + 1 for idx, (work_slug, _) in enumerate(sorted_sequences)}
return review_sequences

def _build_timeline_sequence_map(self) -> dict[tuple[str, str, str], int]:
def _build_reading_entry_sequence_map(self) -> dict[tuple[str, str, str], int]:
"""Build a mapping of timeline entries to their sequence number.

Returns a dictionary where keys are tuples of
Expand Down
19 changes: 3 additions & 16 deletions booklog/exports/reviewed_works.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,16 @@ def _build_json_more_review(
assert review, f"Expected review for {work.title}"

return JsonReviewedWork(
reviewSequence=repository_data.review_sequence_map.get(work.slug, 0),
reviewSequence=repository_data.review_sequence_map.get(work.slug, review.date.isoformat()),
title=work.title,
subtitle=work.subtitle,
sortTitle=work.sort_title,
kind=work.kind,
workYear=work.year,
reviewed=True,
workYearSequence=repository_data.work_year_sequence_map.get(work.slug, 0),
authorSequence=repository_data.author_sequence_map.get(work.slug, 0),
titleSequence=repository_data.title_sequence_map.get(work.slug, 0),
slug=work.slug,
grade=review.grade,
gradeValue=review.grade_value,
gradeSequence=repository_data.grade_sequence_map.get(work.slug, 0),
reviewDate=review.date,
reviewYear=str(review.date.year),
includedInSlugs=[
Expand Down Expand Up @@ -217,9 +213,6 @@ def _build_json_included_work(
reviewed=bool(review),
kind=included_work.kind,
workYear=included_work.year,
workYearSequence=repository_data.work_year_sequence_map.get(included_work.slug, 0),
authorSequence=repository_data.author_sequence_map.get(included_work.slug, 0),
titleSequence=repository_data.title_sequence_map.get(included_work.slug, 0),
authors=[
json_work_author.build_json_work_author(
work_author=included_work_author, all_authors=repository_data.authors
Expand All @@ -246,19 +239,15 @@ def _build_json_reviewed_work(
)

return JsonReviewedWorkWithDetails(
reviewSequence=repository_data.review_sequence_map.get(work.slug, 0),
reviewSequence=repository_data.review_sequence_map.get(work.slug, review.date.isoformat()),
slug=work.slug,
title=work.title,
subtitle=work.subtitle,
sortTitle=work.sort_title,
workYear=work.year,
reviewed=True,
workYearSequence=repository_data.work_year_sequence_map.get(work.slug, 0),
authorSequence=repository_data.author_sequence_map.get(work.slug, 0),
titleSequence=repository_data.title_sequence_map.get(work.slug, 0),
grade=review.grade,
gradeValue=review.grade_value,
gradeSequence=repository_data.grade_sequence_map.get(work.slug, 0),
kind=work.kind,
reviewDate=review.date,
authors=[
Expand All @@ -267,9 +256,7 @@ def _build_json_reviewed_work(
)
for work_author in work.work_authors
],
readings=[
_build_json_reading(reading, repository_data) for reading in readings_for_work
],
readings=[_build_json_reading(reading, repository_data) for reading in readings_for_work],
includedInSlugs=[
included_in_work.slug
for included_in_work in work.included_in_works(repository_data.works)
Expand Down
6 changes: 0 additions & 6 deletions booklog/exports/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ class JsonMostReadAuthorReading(TypedDict):
kind: str
title: str
workYear: str
workYearSequence: int
authorSequence: int
titleSequence: int
includedInSlugs: list[str]
reviewed: bool

Expand Down Expand Up @@ -142,9 +139,6 @@ def _build_json_most_read_author_reading(
kind=work.kind,
title=work.title,
workYear=work.year,
workYearSequence=repository_data.work_year_sequence_map.get(work.slug, 0),
authorSequence=repository_data.author_sequence_map.get(work.slug, 0),
titleSequence=repository_data.title_sequence_map.get(work.slug, 0),
includedInSlugs=[
included_in_work.slug
for included_in_work in work.included_in_works(repository_data.works)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "Stephen King",
"reviewedWorks": [
{
"authorSequence": 2,
"authors": [
{
"name": "Stephen King",
Expand All @@ -12,21 +11,18 @@
}
],
"grade": "A+",
"gradeSequence": 1,
"gradeValue": 13,
"includedInSlugs": [],
"kind": "Nonfiction",
"reviewDate": "2016-03-10",
"reviewSequence": 1,
"reviewSequence": "2016-03-10-01",
"reviewYear": "2016",
"reviewed": true,
"slug": "on-writing-by-stephen-king",
"sortTitle": "On Writing: A Memoir of the Craft",
"subtitle": "A Memoir of the Craft",
"title": "On Writing",
"titleSequence": 1,
"workYear": "2000",
"workYearSequence": 2
"workYear": "2000"
}
],
"slug": "stephen-king",
Expand Down
Loading