Skip to content

Commit

Permalink
channels can be a series in timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
mlomb committed Mar 2, 2023
1 parent af75698 commit b791d14
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 22 deletions.
12 changes: 7 additions & 5 deletions pipeline/aggregate/blocks/timeline/ActiveAuthors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@ import { Index } from "@pipeline/Types";
import { BlockDescription, BlockFn } from "@pipeline/aggregate/Blocks";
import { DateItem } from "@pipeline/aggregate/Common";
import { filterMessages } from "@pipeline/aggregate/Helpers";
import { TimelineSeriesDefinition, generateSeries } from "@pipeline/aggregate/blocks/timeline/Series";
import { MessageView } from "@pipeline/serialization/MessageView";

export interface ActiveAuthorsTimeline {
perGuildPerMonth: DateItem[][];
perSeriesPerMonth: DateItem[][];
}

const fn: BlockFn<ActiveAuthorsTimeline> = (database, filters, common, args) => {
const { keyToTimestamp } = common;
const { monthKeys, dateToMonthIndex } = common.timeKeys;

const computeForGuild = (guildIndex: Index) => {
const computeForSeries = (def: TimelineSeriesDefinition) => {
let foundAtLeastOneMessage = false;

// TODO: optimize this with a bitset or something, using a Set may use too much memory if there are many authors
const authorsPresentInMonth: Set<Index>[] = [];
for (const _ of monthKeys) authorsPresentInMonth.push(new Set());

const processMessage = (msg: MessageView) => {
if (msg.guildIndex === guildIndex) {
if (msg.guildIndex === def.guildIndex || msg.channelIndex === def.channelIndex) {
authorsPresentInMonth[dateToMonthIndex[msg.dayIndex]].add(msg.authorIndex);
foundAtLeastOneMessage = true;
}
Expand All @@ -33,7 +35,7 @@ const fn: BlockFn<ActiveAuthorsTimeline> = (database, filters, common, args) =>
if (foundAtLeastOneMessage) {
for (let i = 0; i < monthKeys.length; i++) {
items.push({
ts: Day.fromKey(monthKeys[i]).toTimestamp(),
ts: keyToTimestamp.month[i],
v: authorsPresentInMonth[i].size,
});
}
Expand All @@ -43,7 +45,7 @@ const fn: BlockFn<ActiveAuthorsTimeline> = (database, filters, common, args) =>
};

const res: ActiveAuthorsTimeline = {
perGuildPerMonth: database.guilds.map((_, guildIndex) => computeForGuild(guildIndex)),
perSeriesPerMonth: generateSeries(database).map(computeForSeries),
};

return res;
Expand Down
19 changes: 9 additions & 10 deletions pipeline/aggregate/blocks/timeline/Growth.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import { Day } from "@pipeline/Time";
import { Index } from "@pipeline/Types";
import { BlockDescription, BlockFn } from "@pipeline/aggregate/Blocks";
import { DateItem } from "@pipeline/aggregate/Common";
import { filterMessages } from "@pipeline/aggregate/Helpers";
import { TimelineSeriesDefinition, generateSeries } from "@pipeline/aggregate/blocks/timeline/Series";
import { MessageView } from "@pipeline/serialization/MessageView";

export interface GrowthTimeline {
perGuildPerDay: DateItem[][];
perSeriesPerMonth: DateItem[][];
}

const fn: BlockFn<GrowthTimeline> = (database, filters, common, args) => {
const { keyToTimestamp } = common;
const { dateKeys } = common.timeKeys;

const computeForGuild = (guildIndex: Index) => {
const computeForSeries = (def: TimelineSeriesDefinition) => {
let foundAtLeastOneMessage = false;

// the day each author posted their first message
const firstMessageDay: number[] = new Array(database.authors.length).fill(-1);

const processMessage = (msg: MessageView) => {
if (msg.guildIndex !== guildIndex) return;
if (msg.guildIndex === def.guildIndex || msg.channelIndex === def.channelIndex) {
if (firstMessageDay[msg.authorIndex] === -1 || msg.dayIndex < firstMessageDay[msg.authorIndex])
firstMessageDay[msg.authorIndex] = msg.dayIndex;

if (firstMessageDay[msg.authorIndex] === -1 || msg.dayIndex < firstMessageDay[msg.authorIndex])
firstMessageDay[msg.authorIndex] = msg.dayIndex;

foundAtLeastOneMessage = true;
foundAtLeastOneMessage = true;
}
};

filterMessages(processMessage, database, filters, { channels: true, authors: true, time: false });
Expand Down Expand Up @@ -60,7 +59,7 @@ const fn: BlockFn<GrowthTimeline> = (database, filters, common, args) => {
};

const res: GrowthTimeline = {
perGuildPerDay: database.guilds.map((_, guildIndex) => computeForGuild(guildIndex)),
perSeriesPerMonth: generateSeries(database).map(computeForSeries),
};

return res;
Expand Down
31 changes: 31 additions & 0 deletions pipeline/aggregate/blocks/timeline/Series.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// WARNING: Don't import anything from the pipeline here,
// this file is meant to be used in the report app as well
import type { Database } from "@pipeline/process/Types";

export interface TimelineSeriesDefinition {
title: string;
guildIndex?: number;
channelIndex?: number;
}

/** Generates the series that should be displayed */
export const generateSeries = (db: Database): TimelineSeriesDefinition[] => {
return [
// keep guilds that have a channel that is NOT a DM/group
...db.guilds
.map((guild, guildIndex) => ({ guild, guildIndex }))
.filter(({ guildIndex }) => db.channels.some((c) => c.guildIndex === guildIndex && c.type === "text"))
.map(({ guild, guildIndex }) => ({
title: guild.name,
guildIndex,
})),
// add all groups as series
...db.channels
.map((channel, channelIndex) => ({ channel, channelIndex }))
.filter(({ channel }) => channel.type === "group")
.map(({ channel, channelIndex }) => ({
title: channel.name,
channelIndex,
})),
];
};
2 changes: 1 addition & 1 deletion report/components/cards/timeline/ActiveAuthorsOverTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const createChart: CreateFn<DateItem[][]> = (c: Container) => {
const ActiveAuthorsOverTime = () => (
<AmCharts5Chart
create={createChart}
data={useBlockData("timeline/active-authors")?.perGuildPerMonth}
data={useBlockData("timeline/active-authors")?.perSeriesPerMonth}
style={{
minHeight: 500,
marginLeft: 5,
Expand Down
2 changes: 1 addition & 1 deletion report/components/cards/timeline/GrowthOverTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const createChart: CreateFn<DateItem[][]> = (c: Container) => {
const GrowthOverTime = () => (
<AmCharts5Chart
create={createChart}
data={useBlockData("timeline/growth")?.perGuildPerDay}
data={useBlockData("timeline/growth")?.perSeriesPerMonth}
style={{
minHeight: 500,
marginLeft: 5,
Expand Down
9 changes: 4 additions & 5 deletions report/components/viz/amcharts/Timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
ValueAxis,
XYChart,
XYCursor,
XYSeries,
} from "@amcharts/amcharts5/xy";
import { DateItem } from "@pipeline/aggregate/Common";
import { Guild } from "@pipeline/process/Types";
import { TimelineSeriesDefinition, generateSeries } from "@pipeline/aggregate/blocks/timeline/Series";
import { getDatabase } from "@report/WorkerWrapper";
import { syncAxisWithTimeFilter } from "@report/components/viz/amcharts/AmCharts5";

Expand Down Expand Up @@ -57,11 +56,11 @@ export const createTimeline = (c: Container, timeUnit: TimeUnit, seriesChart: "s
})
);

const createSeries = (guild: Guild) => {
const createSeries = (def: TimelineSeriesDefinition) => {
let series: LineSeries;

const settings: IXYSeriesSettings = {
name: guild.name,
name: def.title,
valueXField: "ts",
valueYField: "v",
xAxis: xAxis,
Expand Down Expand Up @@ -109,7 +108,7 @@ export const createTimeline = (c: Container, timeUnit: TimeUnit, seriesChart: "s
return series;
};

const series = db.guilds.map(createSeries);
const series = generateSeries(db).map(createSeries);

const legend = chart.children.unshift(
Legend.new(root, {
Expand Down

0 comments on commit b791d14

Please sign in to comment.