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}