Added the tasks table to usage

This commit is contained in:
Matt Aitken
2024-06-28 20:18:25 +01:00
parent 5f79a2bc0b
commit eeeb7a002d
3 changed files with 116 additions and 9 deletions
@@ -8,6 +8,15 @@ type Options = {
organizationId: string;
};
export type TaskUsageItem = {
taskIdentifier: string;
runCount: number;
averageDuration: number;
averageCost: number;
totalDuration: number;
totalCost: number;
};
export class UsagePresenter extends BasePresenter {
public async call({ organizationId }: Options) {
//periods
@@ -18,6 +27,7 @@ export class UsagePresenter extends BasePresenter {
endOfToday.setDate(endOfToday.getDate() + 1);
endOfToday.setHours(23, 59, 59, 999);
//usage data from the platform
const past30Days = getUsageSeries(organizationId, {
from: thirtyDaysAgo,
to: endOfToday,
@@ -39,6 +49,40 @@ export class UsagePresenter extends BasePresenter {
}));
});
//usage by task
const tasks = this._replica.$queryRaw<TaskUsageItem[]>`
SELECT
tr."taskIdentifier",
COUNT(*) AS "runCount",
AVG(tr."usageDurationMs") AS "averageDuration",
SUM(tr."usageDurationMs") AS "totalDuration",
AVG(tr."costInCents") / 100.0 AS "averageCost",
SUM(tr."costInCents") / 100.0 AS "totalCost"
FROM
${sqlDatabaseSchema}."TaskRun" tr
JOIN ${sqlDatabaseSchema}."Project" pr ON pr.id = tr."projectId"
JOIN ${sqlDatabaseSchema}."Organization" org ON org.id = pr."organizationId"
WHERE
tr."createdAt" > ${thirtyDaysAgo}
AND tr."createdAt" < ${endOfToday}
AND org.id = ${organizationId}
GROUP BY
tr."taskIdentifier"
ORDER BY
"totalCost" DESC;
`.then((data) => {
return data.map((item) => ({
taskIdentifier: item.taskIdentifier,
runCount: Number(item.runCount),
averageDuration: Number(item.averageDuration),
averageCost: Number(item.averageCost),
totalDuration: Number(item.totalDuration),
totalCost: Number(item.totalCost),
}));
});
//month-to-date usage data with projection
const startOfMonth = new Date();
startOfMonth.setDate(1);
startOfMonth.setHours(0, 0, 0, 0);
@@ -58,6 +102,7 @@ export class UsagePresenter extends BasePresenter {
return {
past30Days,
usage,
tasks,
};
}
}
@@ -11,13 +11,23 @@ import { featuresForRequest } from "~/features.server";
import { getUsage, getUsageSeries } from "~/services/platform.v3.server";
import { requireUserId } from "~/services/session.server";
import { createTimeSeriesData } from "~/utils/graphs";
import { formatCurrency } from "~/utils/numberFormatter";
import { formatCurrency, formatNumber } from "~/utils/numberFormatter";
import { OrganizationParamsSchema, organizationPath } from "~/utils/pathBuilder";
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
import { UsagePresenter } from "~/presenters/v3/UsagePresenter.server";
import { Suspense } from "react";
import { Await } from "@remix-run/react";
import { Spinner } from "~/components/primitives/Spinner";
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "~/components/primitives/Table";
import { formatDurationMilliseconds } from "@trigger.dev/core/v3";
import { Paragraph } from "~/components/primitives/Paragraph";
export async function loader({ params, request }: LoaderFunctionArgs) {
await requireUserId(request);
@@ -37,11 +47,12 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
}
const presenter = new UsagePresenter();
const { past30Days, usage } = await presenter.call({ organizationId: organization.id });
const { past30Days, usage, tasks } = await presenter.call({ organizationId: organization.id });
return typeddefer({
past30Days,
usage,
tasks,
});
}
@@ -63,7 +74,7 @@ const tooltipStyle = {
};
export default function ChoosePlanPage() {
const { usage, past30Days } = useTypedLoaderData<typeof loader>();
const { usage, past30Days, tasks } = useTypedLoaderData<typeof loader>();
const currentPlan = useCurrentPlan();
return (
@@ -177,6 +188,63 @@ export default function ChoosePlanPage() {
</Await>
</Suspense>
</div>
<div className="p-4">
<Header3 spacing>Tasks</Header3>
<Suspense fallback={<Spinner />}>
<Await resolve={tasks}>
{(tasks) => {
return (
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>Task</TableHeaderCell>
<TableHeaderCell alignment="right">Runs</TableHeaderCell>
<TableHeaderCell alignment="right">Average duration</TableHeaderCell>
<TableHeaderCell alignment="right">Average cost</TableHeaderCell>
<TableHeaderCell alignment="right">Total duration</TableHeaderCell>
<TableHeaderCell alignment="right">Total cost</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{tasks.length === 0 ? (
<TableRow>
<TableCell colSpan={6}>
<Paragraph variant="small">No runs to display yet.</Paragraph>
</TableCell>
</TableRow>
) : (
tasks.map((task) => (
<TableRow key={task.taskIdentifier}>
<TableCell>{task.taskIdentifier}</TableCell>
<TableCell alignment="right">
{formatNumber(task.runCount)}
</TableCell>
<TableCell alignment="right">
{formatDurationMilliseconds(task.averageDuration, {
style: "short",
})}
</TableCell>
<TableCell alignment="right">
{formatCurrency(task.averageCost, false)}
</TableCell>
<TableCell alignment="right">
{formatDurationMilliseconds(task.totalDuration, {
style: "short",
})}
</TableCell>
<TableCell alignment="right">
{formatCurrency(task.totalCost, false)}
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
);
}}
</Await>
</Suspense>
</div>
</div>
</div>
</PageBody>
-6
View File
@@ -19,12 +19,6 @@ export function createTimeSeriesData<R>({ startDate, endDate, window = "DAY", da
const foundData = data.find((d) => {
const time = d.date.getTime();
const inRange = time >= periodStart.getTime() && time < periodEnd.getTime();
console.log({
time,
periodStart: periodStart.getTime(),
periodEnd: periodEnd.getTime(),
inRange,
});
return inRange;
});
if (!foundData) {