Skip to content

Commit d92d8c3

Browse files
committed
Merge release into embed
2 parents e460a11 + 59972d1 commit d92d8c3

3 files changed

Lines changed: 105 additions & 25 deletions

File tree

src/components/statistics/ComprehensionQuestionsReport.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,8 @@ export const ComprehensionQuestionsReport: React.FunctionComponent<IStatsPagePro
2121
props
2222
) => {
2323
const l10n = useIntl();
24-
const stats1 = useGetBookComprehensionEventStats(props);
25-
useProvideDataForExport(stats1, props);
26-
const stats = stats1
27-
? stats1.filter((bookStatInfo) => {
28-
// Filter out non-null values
29-
// We'll just look at 2 of them. It'd also be fine to check all the relevant fields too, if desired.
30-
return bookStatInfo.quizzesTaken && bookStatInfo.questions;
31-
})
32-
: undefined;
24+
const stats = useGetBookComprehensionEventStats(props);
25+
useProvideDataForExport(stats, props);
3326

3427
const columns: IGridColumn[] = [
3528
{ name: "title", title: "Book Title", l10nId: "bookTitle" },

src/components/statistics/useGetBookStats.ts

Lines changed: 102 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,120 @@ import { IStatsPageProps, IBookStat } from "./StatsInterfaces";
33
import {
44
useCollectionStats,
55
extractBookStatFromRawData,
6+
IBasicBookInfo,
7+
useSearchBooks,
68
} from "../../connection/LibraryQueryHooks";
9+
import { getFilterForCollectionAndChildren } from "../../model/Collections";
10+
import { BookOrderingScheme } from "../../model/ContentInterfaces";
711

8-
export function useGetBookStats(
9-
props: IStatsPageProps
12+
// Prefer the first Parse language pointer as a lightweight fallback language tag.
13+
function getLanguageTagFromBook(book: IBasicBookInfo): string | undefined {
14+
return book.languages?.[0]?.isoCode;
15+
}
16+
17+
// Build a bookInstanceId-to-language map, keeping the first tag we see for duplicate ids.
18+
// This is intentionally only graceful: if bookInstanceId is duplicated, any one matching
19+
// language tag is good enough for repairing missing stats rows.
20+
function buildLanguageMap(books: IBasicBookInfo[]): Map<string, string> {
21+
const result = new Map<string, string>();
22+
23+
books.forEach((book) => {
24+
if (!book.bookInstanceId) {
25+
return;
26+
}
27+
28+
const languageTag = getLanguageTagFromBook(book);
29+
if (!languageTag) {
30+
return;
31+
}
32+
33+
if (!result.has(book.bookInstanceId)) {
34+
result.set(book.bookInstanceId, languageTag);
35+
}
36+
});
37+
38+
return result;
39+
}
40+
41+
// Fill missing stats languages from Parse book language pointers.
42+
function useRawBookStatsWithParseLanguageFallback(
43+
props: IStatsPageProps,
44+
urlSuffix: string
1045
): IBookStat[] | undefined {
11-
const { response } = useCollectionStats(props, "reading/per-book");
46+
const { response } = useCollectionStats(props, urlSuffix);
47+
const collectionFilter = props.collection.filter
48+
? props.collection.filter
49+
: getFilterForCollectionAndChildren(props.collection);
50+
51+
// The stats API sometimes returns rows without a language tag
52+
// (specifically when we only have download info, not read info),
53+
// so we query matching books and repair missing stats.language from langPointers.
54+
// See BL-16000.
55+
const { books: parseBooks } = useSearchBooks(
56+
{
57+
include: "langPointers",
58+
keys: "bookInstanceId,langPointers",
59+
limit: 10000000,
60+
},
61+
collectionFilter || {},
62+
BookOrderingScheme.None,
63+
undefined,
64+
!collectionFilter
65+
);
66+
67+
const parseLanguageByInstanceId = useMemo(() => {
68+
return buildLanguageMap(parseBooks);
69+
}, [parseBooks]);
1270

1371
return useMemo(() => {
1472
if (response && response["data"] && response["data"]["stats"]) {
1573
return response["data"]["stats"].map((s: any) => {
16-
return extractBookStatFromRawData(s);
74+
const stats = extractBookStatFromRawData(s);
75+
if (stats.language) {
76+
return stats;
77+
} else {
78+
const fallbackLanguage = parseLanguageByInstanceId.get(
79+
s.bookinstanceid
80+
);
81+
82+
return fallbackLanguage
83+
? { ...stats, language: fallbackLanguage }
84+
: stats;
85+
}
1786
});
87+
} else {
88+
return undefined;
1889
}
19-
return undefined;
20-
}, [response]);
90+
}, [response, parseLanguageByInstanceId]);
2191
}
2292

23-
export function useGetBookComprehensionEventStats(
24-
props: IStatsPageProps
93+
// Apply an optional client-side filter to repaired per-book stats.
94+
function useFilteredBookStats(
95+
props: IStatsPageProps,
96+
urlSuffix: string,
97+
predicate?: (bookStatInfo: IBookStat) => boolean
2598
): IBookStat[] | undefined {
26-
const { response } = useCollectionStats(props, "reading/per-book");
99+
const stats = useRawBookStatsWithParseLanguageFallback(props, urlSuffix);
27100

28101
return useMemo(() => {
29-
if (response && response["data"] && response["data"]["stats"]) {
30-
return response["data"]["stats"].map((s: any) => {
31-
return extractBookStatFromRawData(s);
32-
});
33-
}
34-
return undefined;
35-
}, [response]);
102+
return predicate ? stats?.filter(predicate) : stats;
103+
}, [predicate, stats]);
104+
}
105+
106+
export function useGetBookStats(
107+
props: IStatsPageProps
108+
): IBookStat[] | undefined {
109+
return useFilteredBookStats(props, "reading/per-book");
110+
}
111+
112+
const comprehensionStatsPredicate = (bookStatInfo: IBookStat) =>
113+
bookStatInfo.quizzesTaken > 0 && bookStatInfo.questions > 0;
114+
export function useGetBookComprehensionEventStats(
115+
props: IStatsPageProps
116+
): IBookStat[] | undefined {
117+
return useFilteredBookStats(
118+
props,
119+
"reading/per-book",
120+
comprehensionStatsPredicate
121+
);
36122
}

src/connection/LibraryQueryHooks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ function makeBookQueryAxiosParams(
655655
// around the raw REST result, used to quickly make book cards & grid rows.
656656
export interface IBasicBookInfo {
657657
objectId: string;
658+
bookInstanceId?: string;
658659
baseUrl: string;
659660
harvestState?: string;
660661
//note, here in a "BasicBookInfo", this is just JSON, intentionally not parsed yet,

0 commit comments

Comments
 (0)