Add hard zero
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
CREATE TABLE "Settings" (
|
||||
"id" boolean PRIMARY KEY DEFAULT true NOT NULL,
|
||||
"hardZeroAt" timestamp (3),
|
||||
CONSTRAINT "Settings_singleton" CHECK ("id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO "Settings" ("id") VALUES (true);
|
||||
--> statement-breakpoint
|
||||
-- secondsSince counts from the latest of last execution, task creation, and the hard-zero
|
||||
-- baseline. GREATEST ignores NULLs, so a NULL hardZeroAt has no effect.
|
||||
DROP VIEW "TaskPeriod";
|
||||
--> statement-breakpoint
|
||||
CREATE VIEW "TaskPeriod" AS
|
||||
SELECT *, ("secondsSince" / period) AS "urgency" FROM (
|
||||
SELECT
|
||||
"Task".id,
|
||||
(("everyN" * (CASE "Task"."everySpan"
|
||||
WHEN 'HOUR' THEN 3600
|
||||
WHEN 'DAY' THEN 86400
|
||||
WHEN 'WEEK' THEN 604800
|
||||
WHEN 'MONTH' THEN 2629746
|
||||
WHEN 'YEAR' THEN 31556952
|
||||
WHEN 'DECADE' THEN 315569520
|
||||
WHEN 'CENTURY' THEN 3155695200
|
||||
ELSE 31556952
|
||||
END)) / n) AS "period",
|
||||
"Execution".id AS "latestExecutionId",
|
||||
EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - GREATEST(
|
||||
COALESCE("Execution".date, "Task"."createdAt"),
|
||||
(SELECT "hardZeroAt" FROM "Settings")
|
||||
))) AS "secondsSince"
|
||||
FROM
|
||||
"Task"
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
"Execution".id,
|
||||
"Execution"."taskId",
|
||||
"Execution".date,
|
||||
ROW_NUMBER() OVER (PARTITION BY "Execution"."taskId" ORDER BY "Execution".date DESC) AS rn
|
||||
FROM
|
||||
"Execution"
|
||||
) AS "Execution" ON "Task".id = "Execution"."taskId" AND "Execution".rn = 1
|
||||
) sub;
|
||||
@@ -0,0 +1,410 @@
|
||||
{
|
||||
"id": "419b9723-6f92-4a7a-b4a7-1982c6e978f5",
|
||||
"prevId": "87630719-c657-471c-b37a-a81fecf27978",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.Division": {
|
||||
"name": "Division",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"Division_name_key": {
|
||||
"name": "Division_name_key",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": ["name"]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.Execution": {
|
||||
"name": "Execution",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"taskId": {
|
||||
"name": "taskId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"personId": {
|
||||
"name": "personId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"date": {
|
||||
"name": "date",
|
||||
"type": "timestamp (3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"effort": {
|
||||
"name": "effort",
|
||||
"type": "double precision",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Execution_taskId_Task_id_fk": {
|
||||
"name": "Execution_taskId_Task_id_fk",
|
||||
"tableFrom": "Execution",
|
||||
"tableTo": "Task",
|
||||
"columnsFrom": ["taskId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"Execution_personId_Person_id_fk": {
|
||||
"name": "Execution_personId_Person_id_fk",
|
||||
"tableFrom": "Execution",
|
||||
"tableTo": "Person",
|
||||
"columnsFrom": ["personId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.Person": {
|
||||
"name": "Person",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"Person_name_key": {
|
||||
"name": "Person_name_key",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": ["name"]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.Session": {
|
||||
"name": "Session",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expiresAt": {
|
||||
"name": "expiresAt",
|
||||
"type": "timestamp (3) with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Session_userId_User_id_fk": {
|
||||
"name": "Session_userId_User_id_fk",
|
||||
"tableFrom": "Session",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.Settings": {
|
||||
"name": "Settings",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "boolean",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"hardZeroAt": {
|
||||
"name": "hardZeroAt",
|
||||
"type": "timestamp (3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {
|
||||
"Settings_singleton": {
|
||||
"name": "Settings_singleton",
|
||||
"value": "\"id\""
|
||||
}
|
||||
},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.Task": {
|
||||
"name": "Task",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"n": {
|
||||
"name": "n",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"everyN": {
|
||||
"name": "everyN",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"everySpan": {
|
||||
"name": "everySpan",
|
||||
"type": "Span",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp (3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"xp": {
|
||||
"name": "xp",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"divisionId": {
|
||||
"name": "divisionId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Task_divisionId_Division_id_fk": {
|
||||
"name": "Task_divisionId_Division_id_fk",
|
||||
"tableFrom": "Task",
|
||||
"tableTo": "Division",
|
||||
"columnsFrom": ["divisionId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"Task_divisionId_name_key": {
|
||||
"name": "Task_divisionId_name_key",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": ["divisionId", "name"]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.User": {
|
||||
"name": "User",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"passwordHash": {
|
||||
"name": "passwordHash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"personId": {
|
||||
"name": "personId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp (3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"User_personId_Person_id_fk": {
|
||||
"name": "User_personId_Person_id_fk",
|
||||
"tableFrom": "User",
|
||||
"tableTo": "Person",
|
||||
"columnsFrom": ["personId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"User_username_key": {
|
||||
"name": "User_username_key",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": ["username"]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"public.Span": {
|
||||
"name": "Span",
|
||||
"schema": "public",
|
||||
"values": ["HOUR", "DAY", "WEEK", "MONTH", "YEAR", "DECADE", "CENTURY"]
|
||||
}
|
||||
},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {
|
||||
"public.TaskPeriod": {
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"latestExecutionId": {
|
||||
"name": "latestExecutionId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"period": {
|
||||
"name": "period",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"secondsSince": {
|
||||
"name": "secondsSince",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"urgency": {
|
||||
"name": "urgency",
|
||||
"type": "double precision",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"name": "TaskPeriod",
|
||||
"schema": "public",
|
||||
"isExisting": true,
|
||||
"materialized": false
|
||||
}
|
||||
},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
"when": 1781952453886,
|
||||
"tag": "0001_task_period_view",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1782239032665,
|
||||
"tag": "0002_hard_zero",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { runSeed } from './seed';
|
||||
|
||||
const sql = postgres(demoUrl, { max: 1 });
|
||||
await sql`TRUNCATE "Execution", "Task", "Division", "Person", "Session", "User" RESTART IDENTITY CASCADE`;
|
||||
// Settings holds the singleton row, so clear it in place rather than truncating.
|
||||
await sql`UPDATE "Settings" SET "hardZeroAt" = NULL`;
|
||||
await sql.end();
|
||||
|
||||
await runSeed(demoUrl, { demo: true });
|
||||
|
||||
+24
-1
@@ -1,5 +1,16 @@
|
||||
import { relations, sql } from 'drizzle-orm';
|
||||
import { doublePrecision, integer, pgEnum, pgTable, pgView, text, timestamp, unique } from 'drizzle-orm/pg-core';
|
||||
import {
|
||||
boolean,
|
||||
check,
|
||||
doublePrecision,
|
||||
integer,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
pgView,
|
||||
text,
|
||||
timestamp,
|
||||
unique,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { type Span, spans } from '@/lib/span-enum';
|
||||
|
||||
// Physical table/column names stay PascalCase / camelCase (quoted) to match the
|
||||
@@ -47,6 +58,18 @@ export const execution = pgTable('Execution', {
|
||||
effort: doublePrecision('effort').notNull(),
|
||||
});
|
||||
|
||||
// Singleton: the CHECK pins id to true, so the PK admits exactly one row.
|
||||
export const settings = pgTable(
|
||||
'Settings',
|
||||
{
|
||||
id: boolean('id').primaryKey().default(true),
|
||||
// Urgency baseline: any task not executed since this instant counts as urgency 0
|
||||
// (see GREATEST in the TaskPeriod view). NULL applies no baseline.
|
||||
hardZeroAt: timestamp('hardZeroAt', { precision: 3, mode: 'date' }),
|
||||
},
|
||||
() => [check('Settings_singleton', sql`"id"`)],
|
||||
);
|
||||
|
||||
// Auth tables: only ever written/read on the real DB (see src/db/index.ts).
|
||||
export const user = pgTable('User', {
|
||||
id: text('id').primaryKey().default(sql`gen_random_uuid()`),
|
||||
|
||||
Reference in New Issue
Block a user