Merge branch 'tag-history-date' into 'develop'

Trends: fill in empty tag histories

Closes #29

See merge request soapbox-pub/ditto!3
This commit is contained in:
Alex Gleason 2023-07-26 22:31:28 +00:00
commit 89e7abec9a
4 changed files with 71 additions and 5 deletions

View File

@ -3,6 +3,7 @@ import { Conf } from '@/config.ts';
import { z } from '@/deps.ts';
import { trends } from '@/trends.ts';
import { Time } from '@/utils.ts';
import { stripTime } from '@/utils/time.ts';
const limitSchema = z.coerce.number().catch(10).transform((value) => Math.min(Math.max(value, 0), 20));
@ -14,6 +15,7 @@ const trendingTagsController: AppController = (c) => {
const yesterday = new Date(now.getTime() - Time.days(1));
const lastWeek = new Date(now.getTime() - Time.days(7));
/** Most used hashtags within the past 24h. */
const tags = trends.getTrendingTags({
since: yesterday,
until: now,
@ -24,8 +26,10 @@ const trendingTagsController: AppController = (c) => {
name,
url: Conf.local(`/tags/${name}`),
history: [
// Use the full 24h query for the current day. Then use `offset: 1` to adjust for this below.
// This result is more accurate than what Mastodon returns.
{
day: String(Math.floor(now.getTime() / 1000)),
day: String(Math.floor(stripTime(now).getTime() / 1000)),
accounts: String(accounts),
uses: String(uses),
},
@ -36,6 +40,7 @@ const trendingTagsController: AppController = (c) => {
limit: 6,
offset: 1,
}).map((history) => ({
// For some reason, Mastodon wants these to be strings... oh well.
day: String(Math.floor(history.day.getTime() / 1000)),
accounts: String(history.accounts),
uses: String(history.uses),

View File

@ -1,6 +1,7 @@
import { Sqlite } from '@/deps.ts';
import { hashtagSchema, hexIdSchema } from '@/schema.ts';
import { Time } from './utils.ts';
import { Time } from '@/utils.ts';
import { generateDateRange } from '@/utils/time.ts';
interface GetTrendingTagsOpts {
since: Date;
@ -43,6 +44,7 @@ class TrendsDB {
cleanup();
}
/** Gets the most used hashtags between the date range. */
getTrendingTags({ since, until, limit = 10, threshold = 3 }: GetTrendingTagsOpts) {
return this.#db.query<string[]>(
`
@ -62,10 +64,14 @@ class TrendsDB {
}));
}
/**
* Gets the tag usage count for a specific tag.
* It returns an array with counts for each date between the range.
*/
getTagHistory({ tag, since, until, limit = 7, offset = 0 }: GetTagHistoryOpts) {
return this.#db.query<string[]>(
const result = this.#db.query<string[]>(
`
SELECT inserted_at, COUNT(DISTINCT pubkey8), COUNT(*)
SELECT date(inserted_at), COUNT(DISTINCT pubkey8), COUNT(*)
FROM tag_usages
WHERE tag = ? AND inserted_at >= ? AND inserted_at < ?
GROUP BY date(inserted_at)
@ -79,6 +85,18 @@ class TrendsDB {
accounts: Number(row[1]),
uses: Number(row[2]),
}));
/** Full date range between `since` and `until`. */
const dateRange = generateDateRange(
new Date(since.getTime() + Time.days(1)),
new Date(until.getTime() - Time.days(offset)),
).reverse();
// Fill in missing dates with 0 usages.
return dateRange.map((day) => {
const data = result.find((item) => item.day.getTime() === day.getTime());
return data || { day, accounts: 0, uses: 0 };
});
}
addTagUsages(pubkey: string, hashtags: string[], date = new Date()): void {

23
src/utils/time.test.ts Normal file
View File

@ -0,0 +1,23 @@
import { assertEquals } from '@/deps-test.ts';
import { generateDateRange } from './time.ts';
Deno.test('generateDateRange', () => {
const since = new Date('2023-07-03T16:30:00.000Z');
const until = new Date('2023-07-07T09:01:00.000Z');
const expected = [
new Date('2023-07-03T00:00:00.000Z'),
new Date('2023-07-04T00:00:00.000Z'),
new Date('2023-07-05T00:00:00.000Z'),
new Date('2023-07-06T00:00:00.000Z'),
new Date('2023-07-07T00:00:00.000Z'),
];
const result = generateDateRange(since, until);
assertEquals(
result.map((d) => d.getTime()),
expected.map((d) => d.getTime()),
);
});

View File

@ -9,4 +9,24 @@ const Time = {
years: (y: number) => y * Time.days(365),
};
export { Time };
/** Strips the time off the date, giving 12am UTC. */
function stripTime(date: Date): Date {
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
}
/** Strips times off the dates and generates all 24h intervals between them, inclusive of both inputs. */
function generateDateRange(since: Date, until: Date): Date[] {
const dates = [];
const sinceDate = stripTime(since);
const untilDate = stripTime(until);
while (sinceDate <= untilDate) {
dates.push(new Date(sinceDate));
sinceDate.setUTCDate(sinceDate.getUTCDate() + 1);
}
return dates;
}
export { generateDateRange, stripTime, Time };