mirror of
https://github.com/BreizhHardware/Jellystat.git
synced 2026-01-18 16:27:20 +01:00
Initial changes for total time view
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
exports.up = async function (knex) {
|
||||
try {
|
||||
await knex.schema.raw(`
|
||||
DROP FUNCTION IF EXISTS public.fs_watch_stats_over_time(integer);
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.fs_watch_stats_over_time(
|
||||
days integer)
|
||||
RETURNS TABLE("Date" date, "Count" bigint, "TotalTime" bigint, "Library" text, "LibraryID" text)
|
||||
LANGUAGE 'plpgsql'
|
||||
COST 100
|
||||
VOLATILE PARALLEL UNSAFE
|
||||
ROWS 1000
|
||||
|
||||
AS $BODY$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
dates."Date",
|
||||
COALESCE(counts."Count", 0) AS "Count",
|
||||
COALESCE(counts."TotalTime", 0) AS "TotalTime",
|
||||
l."Name" as "Library",
|
||||
l."Id" as "LibraryID"
|
||||
FROM
|
||||
(SELECT generate_series(
|
||||
DATE_TRUNC('day', NOW() - CAST(days || ' days' as INTERVAL)),
|
||||
DATE_TRUNC('day', NOW()),
|
||||
'1 day')::DATE AS "Date"
|
||||
) dates
|
||||
CROSS JOIN jf_libraries l
|
||||
|
||||
LEFT JOIN
|
||||
(SELECT
|
||||
DATE_TRUNC('day', a."ActivityDateInserted")::DATE AS "Date",
|
||||
COUNT(*) AS "Count",
|
||||
(SUM(a."PlaybackDuration") / 60)::bigint AS "TotalTime",
|
||||
l."Name" as "Library"
|
||||
FROM
|
||||
jf_playback_activity a
|
||||
JOIN jf_library_items i ON i."Id" = a."NowPlayingItemId"
|
||||
JOIN jf_libraries l ON i."ParentId" = l."Id"
|
||||
WHERE
|
||||
a."ActivityDateInserted" BETWEEN NOW() - CAST(days || ' days' as INTERVAL) AND NOW()
|
||||
|
||||
GROUP BY
|
||||
l."Name", DATE_TRUNC('day', a."ActivityDateInserted")
|
||||
) counts
|
||||
ON counts."Date" = dates."Date" AND counts."Library" = l."Name"
|
||||
where l.archived=false
|
||||
|
||||
ORDER BY
|
||||
"Date", "Library";
|
||||
END;
|
||||
|
||||
$BODY$;
|
||||
|
||||
ALTER FUNCTION public.fs_watch_stats_over_time(integer)
|
||||
OWNER TO "${process.env.POSTGRES_ROLE}";
|
||||
`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
exports.down = async function (knex) {
|
||||
// try {
|
||||
// await knex.schema.raw(`
|
||||
// DROP FUNCTION IF EXISTS public.fs_watch_stats_over_time(integer);
|
||||
|
||||
// CREATE OR REPLACE FUNCTION fs_watch_stats_over_time(
|
||||
// days integer
|
||||
// )
|
||||
// RETURNS TABLE(
|
||||
// "Date" date,
|
||||
// "Count" bigint,
|
||||
// "Library" text
|
||||
// )
|
||||
// LANGUAGE 'plpgsql'
|
||||
// COST 100
|
||||
// VOLATILE PARALLEL UNSAFE
|
||||
// ROWS 1000
|
||||
|
||||
// AS $BODY$
|
||||
// BEGIN
|
||||
// RETURN QUERY
|
||||
// SELECT
|
||||
// dates."Date",
|
||||
// COALESCE(counts."Count", 0) AS "Count",
|
||||
// l."Name" as "Library"
|
||||
// FROM
|
||||
// (SELECT generate_series(
|
||||
// DATE_TRUNC('day', NOW() - CAST(days || ' days' as INTERVAL)),
|
||||
// DATE_TRUNC('day', NOW()),
|
||||
// '1 day')::DATE AS "Date"
|
||||
// ) dates
|
||||
// CROSS JOIN jf_libraries l
|
||||
// LEFT JOIN
|
||||
// (SELECT
|
||||
// DATE_TRUNC('day', a."ActivityDateInserted")::DATE AS "Date",
|
||||
// COUNT(*) AS "Count",
|
||||
// l."Name" as "Library"
|
||||
// FROM
|
||||
// jf_playback_activity a
|
||||
// JOIN jf_library_items i ON i."Id" = a."NowPlayingItemId"
|
||||
// JOIN jf_libraries l ON i."ParentId" = l."Id"
|
||||
// WHERE
|
||||
// a."ActivityDateInserted" BETWEEN NOW() - CAST(days || ' days' as INTERVAL) AND NOW()
|
||||
// GROUP BY
|
||||
// l."Name", DATE_TRUNC('day', a."ActivityDateInserted")
|
||||
// ) counts
|
||||
// ON counts."Date" = dates."Date" AND counts."Library" = l."Name"
|
||||
// ORDER BY
|
||||
// "Date", "Library";
|
||||
// END;
|
||||
// $BODY$;
|
||||
|
||||
// ALTER FUNCTION fs_watch_stats_over_time(integer)
|
||||
// OWNER TO "${process.env.POSTGRES_ROLE}";`);
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
};
|
||||
@@ -167,7 +167,9 @@
|
||||
"STAT_PAGE": {
|
||||
"STATISTICS": "Statistics",
|
||||
"DAILY_PLAY_PER_LIBRARY": "Daily Play Count Per Library",
|
||||
"PLAY_COUNT_BY": "Play Count By"
|
||||
"PLAY_COUNT_BY": "Play Count By",
|
||||
"COUNT_VIEW": "Total Count",
|
||||
"TIME_VIEW": "Total Time"
|
||||
},
|
||||
"SETTINGS_PAGE": {
|
||||
"SETTINGS": "Settings",
|
||||
|
||||
@@ -143,3 +143,12 @@ input[type="number"] {
|
||||
.item-name :hover {
|
||||
color: var(--secondary-color) !important;
|
||||
}
|
||||
|
||||
.pill-wrapper {
|
||||
color: white;
|
||||
display: flex;
|
||||
border-radius: 8px;
|
||||
font-size: 1.2em;
|
||||
align-self: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Tabs, Tab } from "react-bootstrap";
|
||||
import { useState } from "react";
|
||||
|
||||
import "./css/stats.css";
|
||||
@@ -20,6 +21,13 @@ function Statistics() {
|
||||
localStorage.setItem("PREF_STATISTICS_STAT_DAYS_INPUT", event.target.value);
|
||||
};
|
||||
|
||||
const [activeTab, setActiveTab] = useState(localStorage.getItem(`PREF_STATISTICS_LAST_SELECTED_TAB`) ?? "tabCount");
|
||||
|
||||
function setTab(tabName) {
|
||||
setActiveTab(tabName);
|
||||
localStorage.setItem(`PREF_STATISTICS_LAST_SELECTED_TAB`, tabName);
|
||||
}
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
if (input < 1) {
|
||||
@@ -43,6 +51,17 @@ function Statistics() {
|
||||
<h1>
|
||||
<Trans i18nKey={"STAT_PAGE.STATISTICS"} />
|
||||
</h1>
|
||||
<div className="pill-wrapper">
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onSelect={setTab}
|
||||
variant="pills"
|
||||
className="custom-tabs"
|
||||
>
|
||||
<Tab eventKey="tabCount" title={<Trans i18nKey="STAT_PAGE.COUNT_VIEW" />} />
|
||||
<Tab eventKey="tabTime" title={<Trans i18nKey="STAT_PAGE.TIME_VIEW" />} />
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="date-range">
|
||||
<div className="header">
|
||||
<Trans i18nKey={"LAST"} />
|
||||
@@ -55,14 +74,26 @@ function Statistics() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<DailyPlayStats days={days} />
|
||||
|
||||
<div className="statistics-graphs">
|
||||
<PlayStatsByDay days={days} />
|
||||
<PlayStatsByHour days={days} />
|
||||
</div>
|
||||
</div>
|
||||
{activeTab === "tabCount" && (
|
||||
<>
|
||||
<DailyPlayStats days={days} />
|
||||
<div className="statistics-graphs">
|
||||
<PlayStatsByDay days={days} />
|
||||
<PlayStatsByHour days={days} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === "tabTime" && (
|
||||
<>
|
||||
<DailyPlayStats days={days} />
|
||||
<div className="statistics-graphs">
|
||||
<PlayStatsByDay days={days} />
|
||||
<PlayStatsByHour days={days} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user