diff --git a/sitio/src/lib/HeatmapHours.svelte b/sitio/src/lib/HeatmapHours.svelte new file mode 100644 index 0000000..daa77d2 --- /dev/null +++ b/sitio/src/lib/HeatmapHours.svelte @@ -0,0 +1,66 @@ + + +
+
+

{title}

+ Zona horaria: UTC-3 +
+ +
+
+
+ {#each hours as h} +
{h}
+ {/each} + + {#each matrix as row, dow} +
{weekdays[dow]}
+ {#each row as value} +
+ {/each} + {/each} +
+
+ +
+ menos +
+
+
+
+
+
+ más +
+
+ diff --git a/sitio/src/routes/+page.server.ts b/sitio/src/routes/+page.server.ts index b8745b4..1843028 100644 --- a/sitio/src/routes/+page.server.ts +++ b/sitio/src/routes/+page.server.ts @@ -4,7 +4,6 @@ import * as schema from "../schema"; import type { PageServerLoad } from "./$types"; import { dayjs, likesCutoffSql } from "$lib/consts"; import { error, redirect } from "@sveltejs/kit"; -import { getLastWeek } from "$lib/data-processing/weekly"; import { getStatsForDaysInTimePeriod } from "@/data-processing/days"; const tz = "America/Argentina/Buenos_Aires"; @@ -25,7 +24,7 @@ function getStartingFrom(query: string) { } } -export const load: PageServerLoad = async ({ params, url, setHeaders }) => { +export const load = (async ({ url, setHeaders }) => { const query = url.searchParams.get("q") ?? "date:" + dayjs().tz(tz).format("YYYY-MM-DD"); const startingFrom = getStartingFrom(query); @@ -116,6 +115,44 @@ export const load: PageServerLoad = async ({ params, url, setHeaders }) => { const t1 = performance.now(); console.log("queries", t1 - t0); + // Build 7x24 heatmap (UTC-3) over last 60 days using retweets and available likes + const heatmapStart = dayjs().tz(tz).subtract(60, "day").startOf("day"); + const heatmapEnd = dayjs().tz(tz).endOf("day"); + const [heatLikes, heatRetweets]: [ + Array<{ firstSeenAt: Date }>, + Array<{ retweetAt: Date }>, + ] = await Promise.all([ + db.query.likedTweets.findMany({ + columns: { firstSeenAt: true }, + where: and( + gte(schema.likedTweets.firstSeenAt, heatmapStart.toDate()), + lte(schema.likedTweets.firstSeenAt, heatmapEnd.toDate()), + likesCutoffSql, + ), + orderBy: desc(schema.likedTweets.firstSeenAt), + }), + db.query.retweets.findMany({ + columns: { retweetAt: true }, + where: and( + gte(schema.retweets.retweetAt, heatmapStart.toDate()), + lte(schema.retweets.retweetAt, heatmapEnd.toDate()), + ), + orderBy: desc(schema.retweets.retweetAt), + }), + ]); + + const hourHeatmap: number[][] = Array.from({ length: 7 }, () => + Array(24).fill(0), + ); + const addToHeat = (d: Date) => { + const x = dayjs(d).tz(tz); + const dow = x.day(); + const hour = x.hour(); + hourHeatmap[dow][hour]++; + }; + heatLikes.forEach((t: { firstSeenAt: Date }) => addToHeat(t.firstSeenAt)); + heatRetweets.forEach((t: { retweetAt: Date }) => addToHeat(t.retweetAt)); + if ( likedTweets.length === 0 && retweets.length === 0 && @@ -145,5 +182,6 @@ export const load: PageServerLoad = async ({ params, url, setHeaders }) => { monthData, hasNextMonth: !!hasNextMonth, query, + hourHeatmap, }; -}; +}) satisfies PageServerLoad; diff --git a/sitio/src/routes/+page.svelte b/sitio/src/routes/+page.svelte index 1a3c774..9f1c65c 100644 --- a/sitio/src/routes/+page.svelte +++ b/sitio/src/routes/+page.svelte @@ -36,6 +36,7 @@ import { parseDate } from "@internationalized/date"; import StatsCalendar from "@/StatsCalendar.svelte"; import StatsCalendarNavigation from "@/StatsCalendarNavigation.svelte"; + import HeatmapHours from "$lib/HeatmapHours.svelte"; export let data: PageData; @@ -169,6 +170,18 @@ +
+
+

+ ¿Cuándo suele estar activo en Twitter? (UTC-3) +

+

+ Basado en retweets y likes de los últimos 60 días. +

+ +
+
+ {#if dudoso}