From fd2c39435dd4b07b5352010d55cfcef62b75f0b5 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sat, 23 Apr 2022 23:44:38 +0400 Subject: [PATCH] refactor: prisma everywhere, drop unused columns and tables --- .../migration.sql | 12 ++ .../migration.sql | 8 + prisma/schema.prisma | 48 ++++-- src/backend/database/pg-wrapper.ts | 163 ------------------ src/backend/flow/history/updateHistory.ts | 9 +- src/backend/manual/manualDownload.ts | 4 +- src/backend/migrate/noSchemaMigrate.ts | 92 ---------- src/backend/migrate/prepareForPrisma.ts | 92 ---------- src/backend/platforms/_example.ts | 18 +- src/backend/platforms/betfair.ts | 5 +- src/backend/platforms/fantasyscotus.ts | 7 +- src/backend/platforms/foretold.ts | 11 +- src/backend/platforms/givewellopenphil.ts | 12 +- src/backend/platforms/goodjudgment.ts | 15 +- src/backend/platforms/goodjudgmentopen.ts | 1 - src/backend/platforms/index.ts | 93 ++++------ src/backend/platforms/infer.ts | 7 +- src/backend/platforms/kalshi.ts | 15 +- src/backend/platforms/manifold.ts | 11 +- src/backend/platforms/metaculus.ts | 11 +- src/backend/platforms/polymarket.ts | 7 +- src/backend/platforms/predictit.ts | 15 +- src/backend/platforms/rootclaim.ts | 7 +- src/backend/platforms/smarkets.ts | 6 +- src/backend/platforms/wildeford.ts | 14 +- .../pullForecastsToCSVForRating.ts | 4 +- .../pullMetaculusForecastsToCSVForRating.ts | 4 +- ...ess-forecasts-from-old-givewellopenphil.ts | 2 +- .../misc/process-forecasts-from-old-xrisk.ts | 2 +- .../misc/process-forecasts-from-xrisk.ts | 2 +- .../misc/process-forecasts-into-elicit.ts | 19 +- src/web/worker/searchGuesstimate.ts | 3 +- src/web/worker/searchWithAlgolia.ts | 3 - 33 files changed, 181 insertions(+), 541 deletions(-) create mode 100644 prisma/migrations/20220423191830_drop_stars_column/migration.sql create mode 100644 prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql delete mode 100644 src/backend/database/pg-wrapper.ts delete mode 100644 src/backend/migrate/noSchemaMigrate.ts delete mode 100644 src/backend/migrate/prepareForPrisma.ts diff --git a/prisma/migrations/20220423191830_drop_stars_column/migration.sql b/prisma/migrations/20220423191830_drop_stars_column/migration.sql new file mode 100644 index 0000000..8ad6b6d --- /dev/null +++ b/prisma/migrations/20220423191830_drop_stars_column/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to drop the column `stars` on the `history` table. All the data in the column will be lost. + - You are about to drop the column `stars` on the `questions` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "history" DROP COLUMN "stars"; + +-- AlterTable +ALTER TABLE "questions" DROP COLUMN "stars"; diff --git a/prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql b/prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql new file mode 100644 index 0000000..4dee470 --- /dev/null +++ b/prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the `frontpage` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +DROP TABLE "frontpage"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 49baa57..9b5a035 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -23,14 +23,6 @@ model Dashboard { @@map("dashboards") } -model Frontpage { - id Int @id @default(autoincrement()) - frontpage_full Json - frontpage_sliced Json - - @@map("frontpage") -} - model History { id String title String @@ -39,7 +31,6 @@ model History { description String options Json timestamp DateTime @db.Timestamp(6) - stars Int qualityindicators Json extra Json pk Int @id @default(autoincrement()) @@ -49,14 +40,37 @@ model History { } model Question { - id String @id - title String - url String - platform String - description String - options Json - timestamp DateTime @db.Timestamp(6) - stars Int + /// E.g. "fantasyscotus-580" + id String @id + /// E.g. "In Wooden v. U.S., the SCOTUS will affirm the lower court's decision" + title String + /// E.g. "https://fantasyscotus.net/user-predictions/case/wooden-v-us/" + url String + /// E.g. "fantasyscotus" + platform String + /// E.g. "62.50% (75 out of 120) of FantasySCOTUS players predict that the lower court's decision will be affirmed. FantasySCOTUS overall predicts an outcome of Affirm 6-3. Historically, FantasySCOTUS has chosen the correct side 50.00% of the time." + description String + + // E.g.: + // [ + // { + // "name": "Yes", + // "probability": 0.625, + // "type": "PROBABILITY" + // }, + // { + // "name": "No", + // "probability": 0.375, + // "type": "PROBABILITY" + // } + // ] + options Json + timestamp DateTime @db.Timestamp(6) + + // { + // "numforecasts": 120, + // "stars": 2 + // } qualityindicators Json extra Json diff --git a/src/backend/database/pg-wrapper.ts b/src/backend/database/pg-wrapper.ts deleted file mode 100644 index f7bc03f..0000000 --- a/src/backend/database/pg-wrapper.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { Pool, PoolClient } from "pg"; - -import { Question } from "../platforms"; -import { measureTime } from "../utils/measureTime"; -import { roughSizeOfObject } from "../utils/roughSize"; - -const questionTableNames = ["questions", "history"]; - -const allTableNames = [...questionTableNames, "dashboards", "frontpage"]; - -/* Postgres database connection code */ -const databaseURL = process.env.DIGITALOCEAN_POSTGRES; -export const pool = new Pool({ - connectionString: databaseURL, - ssl: process.env.POSTGRES_NO_SSL - ? false - : { - rejectUnauthorized: false, - }, -}); - -// Read -export async function pgRead({ tableName }: { tableName: string }) { - if (!allTableNames.includes(tableName)) { - throw Error( - `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` - ); - } - let command = `SELECT * from ${tableName}`; - return (await pool.query(command)).rows; -} - -export async function pgBulkInsert({ - data, - tableName, - client, -}: { - data: Question[]; - tableName: string; - client: PoolClient; -}) { - if (!questionTableNames.includes(tableName)) { - throw Error( - `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` - ); - } - - const generateQuery = (rows: number) => { - let text = `INSERT INTO ${tableName} VALUES`; - const cols = 10; - const parts: string[] = []; - for (let r = 0; r < rows; r++) { - const bits = []; - for (let c = 1; c <= cols; c++) { - bits.push(`$${cols * r + c}`); - } - parts.push("(" + bits.join(", ") + ")"); - } - - text += parts.join(", "); - return text; - }; - - let from = 0; - const chunkSize = 20; - while (from < data.length - 1) { - const take = Math.min(chunkSize, data.length - from); - const query = generateQuery(take); - - const chunk = []; - for (let i = from; i < from + take; i++) { - const datum = data[i]; - let timestamp = - datum.timestamp && - !!datum.timestamp.slice && - !isNaN(Date.parse(datum.timestamp)) - ? datum.timestamp - : new Date().toISOString(); - timestamp = timestamp.slice(0, 19).replace("T", " "); - const values = [ - datum.id, - datum.title, - datum.url, - datum.platform, - datum.description || "", - JSON.stringify(datum.options || []), - timestamp, // fix - datum.stars || - (datum.qualityindicators ? datum.qualityindicators.stars : 2), - JSON.stringify(datum.qualityindicators || []), - JSON.stringify(datum.extra || []), - ]; - chunk.push(...values); - } - - console.log(`Inserting ${from + 1}..${from + take}`); - from += take; - await client.query(query, chunk); - } -} - -export async function pgUpsert({ - contents, - tableName, - replacePlatform, -}: { - contents: Question[]; - tableName: string; - replacePlatform?: string; -}) { - if (!questionTableNames.includes(tableName)) { - throw Error( - `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` - ); - } - - await measureTime(async () => { - const client = await pool.connect(); - try { - await client.query("BEGIN"); - if (replacePlatform) { - await client.query(`DELETE FROM ${tableName} WHERE platform = $1`, [ - replacePlatform, - ]); - } - console.log( - `Upserting ${contents.length} rows into postgres table ${tableName}.` - ); - - await pgBulkInsert({ data: contents, tableName, client }); - console.log( - `Inserted ${ - contents.length - } rows with approximate cummulative size ${roughSizeOfObject( - contents - )} MB into ${tableName}.` - ); - - console.log("Sample: "); - console.log( - JSON.stringify( - // only show the first three options - contents.slice(0, 1).map((question) => ({ - ...question, - options: question.options - ? question.options.length > 3 - ? question.options.slice(0, 3).concat("...") - : question.options - : null, - })), - null, - 4 - ) - ); - await client.query("COMMIT"); - } catch (e) { - await client.query("ROLLBACK"); - throw e; - } finally { - client.release(); - } - }); -} diff --git a/src/backend/flow/history/updateHistory.ts b/src/backend/flow/history/updateHistory.ts index d3e704e..88aea20 100644 --- a/src/backend/flow/history/updateHistory.ts +++ b/src/backend/flow/history/updateHistory.ts @@ -1,9 +1,8 @@ -import { pgRead, pgUpsert } from "../../database/pg-wrapper"; +import { prisma } from "../../database/prisma"; export async function updateHistory() { - let latest = await pgRead({ tableName: "questions" }); - await pgUpsert({ - contents: latest, - tableName: "history", + const questions = await prisma.question.findMany({}); + await prisma.history.createMany({ + data: questions, }); } diff --git a/src/backend/manual/manualDownload.ts b/src/backend/manual/manualDownload.ts index 88472ef..8550904 100644 --- a/src/backend/manual/manualDownload.ts +++ b/src/backend/manual/manualDownload.ts @@ -2,10 +2,10 @@ import "dotenv/config"; import fs from "fs"; -import { pgRead } from "../database/pg-wrapper"; +import { prisma } from "../database/prisma"; let main = async () => { - let json = await pgRead({ tableName: "questions" }); + let json = await prisma.question.findMany({}); let string = JSON.stringify(json, null, 2); let filename = "metaforecasts.json"; fs.writeFileSync(filename, string); diff --git a/src/backend/migrate/noSchemaMigrate.ts b/src/backend/migrate/noSchemaMigrate.ts deleted file mode 100644 index 43bbf20..0000000 --- a/src/backend/migrate/noSchemaMigrate.ts +++ /dev/null @@ -1,92 +0,0 @@ -import "dotenv/config"; - -import { pool } from "../database/pg-wrapper"; - -const migrate = async () => { - const client = await pool.connect(); - - const execQuery = async (q: string) => { - console.log(q); - await client.query(q); - }; - - const platformTitleToName = { - Betfair: "betfair", - FantasySCOTUS: "fantasyscotus", - Foretold: "foretold", - "GiveWell/OpenPhilanthropy": "givewellopenphil", - "Good Judgment": "goodjudgment", - "Good Judgment Open": "goodjudgmentopen", - Infer: "infer", - Kalshi: "kalshi", - "Manifold Markets": "manifold", - Metaculus: "metaculus", - "Peter Wildeford": "wildeford", - PolyMarket: "polymarket", - PredictIt: "predictit", - Rootclaim: "rootclaim", - Smarkets: "smarkets", - "X-risk estimates": "xrisk", - }; - - try { - await client.query("BEGIN"); - const copyTable = async (from: string, to: string) => { - await execQuery(`DROP TABLE IF EXISTS ${to}`); - await execQuery(`CREATE TABLE ${to} (LIKE ${from} INCLUDING ALL)`); - await execQuery(`INSERT INTO ${to} SELECT * FROM ${from}`); - }; - - await copyTable("latest.dashboards", "dashboards"); - await copyTable("latest.combined", "questions"); - await copyTable("latest.frontpage", "frontpage"); - await copyTable("history.h2022", "history"); - - for (const [title, name] of Object.entries(platformTitleToName)) { - console.log(`Updating ${title} -> ${name}`); - for (const table of ["questions", "history"]) { - await client.query( - `UPDATE ${table} SET platform=$1 WHERE platform=$2`, - [name, title] - ); - } - } - - console.log("Fixing GJOpen ids in questions and history"); - for (const table of ["questions", "history"]) { - await client.query( - `UPDATE ${table} SET id=REPLACE(id, 'goodjudmentopen-', 'goodjudgmentopen-') WHERE id LIKE 'goodjudmentopen-%'` - ); - } - - const fixId = (id: string) => - id.replace("goodjudmentopen-", "goodjudgmentopen-"); - - console.log( - "Please rebuild frontpage manually - current version includes invalid GJOpen and xrisk ids" - ); - - const updateDashboards = async () => { - const res = await client.query("SELECT id, contents FROM dashboards"); - for (const row of res.rows) { - let { id, contents } = row; - contents = contents.map(fixId); - await client.query( - "UPDATE dashboards SET contents = $1 WHERE id = $2", - [JSON.stringify(contents), id] - ); - } - }; - console.log("Updating dashboards"); - await updateDashboards(); - - await client.query("COMMIT"); - } catch (e) { - await client.query("ROLLBACK"); - throw e; - } finally { - client.release(); - } -}; - -migrate(); diff --git a/src/backend/migrate/prepareForPrisma.ts b/src/backend/migrate/prepareForPrisma.ts deleted file mode 100644 index cee8905..0000000 --- a/src/backend/migrate/prepareForPrisma.ts +++ /dev/null @@ -1,92 +0,0 @@ -import "dotenv/config"; - -import { pool } from "../database/pg-wrapper"; - -const migrate = async () => { - const client = await pool.connect(); - - const execQuery = async (q: string) => { - console.log(q); - await client.query(q); - }; - - try { - await client.query("BEGIN"); - - const notNullColumn = async (table: string, column: string) => { - await execQuery( - `ALTER TABLE ${table} ALTER COLUMN ${column} SET NOT NULL` - ); - }; - - const jsonbColumn = async (table: string, column: string) => { - await execQuery( - `ALTER TABLE ${table} ALTER COLUMN ${column} SET DATA TYPE jsonb USING ${column}::jsonb` - ); - }; - - const t2c = { - dashboards: [ - "id", - "title", - "description", - "contents", - "timestamp", - "creator", - "extra", - ], - frontpage: ["frontpage_sliced", "frontpage_full"], - history: [ - "id", - "title", - "url", - "platform", - "description", - "options", - "timestamp", - "stars", - "qualityindicators", - "extra", - ], - questions: [ - "id", - "title", - "url", - "platform", - "description", - "options", - "timestamp", - "stars", - "qualityindicators", - "extra", - ], - }; - for (const [table, columns] of Object.entries(t2c)) { - for (const column of columns) { - await notNullColumn(table, column); - } - } - - await execQuery("ALTER TABLE history ADD COLUMN pk SERIAL PRIMARY KEY"); - await execQuery("ALTER TABLE dashboards ADD PRIMARY KEY (id)"); - await execQuery("ALTER TABLE questions ADD PRIMARY KEY (id)"); - - await jsonbColumn("dashboards", "contents"); - await jsonbColumn("dashboards", "extra"); - - for (const table of ["history", "questions"]) { - await jsonbColumn(table, "options"); - await jsonbColumn(table, "qualityindicators"); - await jsonbColumn(table, "extra"); - } - - await client.query("COMMIT"); - } catch (e) { - await client.query("ROLLBACK"); - throw e; - } finally { - client.release(); - } -}; - -migrate(); diff --git a/src/backend/platforms/_example.ts b/src/backend/platforms/_example.ts index 92119b9..3c5c3fe 100644 --- a/src/backend/platforms/_example.ts +++ b/src/backend/platforms/_example.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "example"; @@ -24,9 +24,9 @@ async function fetchData() { async function processPredictions(predictions) { let results = await predictions.map((prediction) => { - let id = `${platformName}-${prediction.id}`; - let probability = prediction.probability; - let options = [ + const id = `${platformName}-${prediction.id}`; + const probability = prediction.probability; + const options = [ { name: "Yes", probability: probability, @@ -38,19 +38,19 @@ async function processPredictions(predictions) { type: "PROBABILITY", }, ]; - let result = { + const result: FetchedQuestion = { + id, title: prediction.title, url: `https://example.com`, platform: platformName, description: prediction.description, - options: options, - timestamp: new Date().toISOString(), + options, qualityindicators: { stars: calculateStars(platformName, { /* some: somex, factors: factors */ }), - other: prediction.otherx, - indicators: prediction.indicatorx, + // other: prediction.otherx, + // indicators: prediction.indicatorx, }, }; return result; diff --git a/src/backend/platforms/betfair.ts b/src/backend/platforms/betfair.ts index bbfd061..4af99fc 100644 --- a/src/backend/platforms/betfair.ts +++ b/src/backend/platforms/betfair.ts @@ -3,7 +3,7 @@ import axios from "axios"; import https from "https"; import { calculateStars } from "../utils/stars"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; const platformName = "betfair"; @@ -80,7 +80,7 @@ async function whipIntoShape(data) { async function processPredictions(data) { let predictions = await whipIntoShape(data); // console.log(JSON.stringify(predictions, null, 4)) - let results: Question[] = predictions.map((prediction) => { + let results: FetchedQuestion[] = predictions.map((prediction) => { /* if(Math.floor(Math.random() * 10) % 20 ==0){ console.log(JSON.stringify(prediction, null, 4)) } */ @@ -126,7 +126,6 @@ async function processPredictions(data) { platform: platformName, description: description, options: options, - timestamp: new Date().toISOString(), qualityindicators: { stars: calculateStars(platformName, { volume: prediction.totalMatched, diff --git a/src/backend/platforms/fantasyscotus.ts b/src/backend/platforms/fantasyscotus.ts index 6feb947..2787373 100644 --- a/src/backend/platforms/fantasyscotus.ts +++ b/src/backend/platforms/fantasyscotus.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; const platformName = "fantasyscotus"; @@ -67,7 +67,7 @@ async function processData(data) { let historicalPercentageCorrect = data.stats.pcnt_correct; let historicalProbabilityCorrect = Number(historicalPercentageCorrect.replace("%", "")) / 100; - let results: Question[] = []; + let results: FetchedQuestion[] = []; for (let event of events) { if (event.accuracy == "") { let id = `${platformName}-${event.id}`; @@ -75,7 +75,7 @@ async function processData(data) { let predictionData = await getPredictionsData(event.docket_url); let pAffirm = predictionData.proportionAffirm; //let trackRecord = event.prediction.includes("Affirm") ? historicalProbabilityCorrect : 1-historicalProbabilityCorrect - let eventObject: Question = { + let eventObject: FetchedQuestion = { id: id, title: `In ${event.short_name}, the SCOTUS will affirm the lower court's decision`, url: `https://fantasyscotus.net/user-predictions${event.docket_url}`, @@ -99,7 +99,6 @@ async function processData(data) { type: "PROBABILITY", }, ], - timestamp: new Date().toISOString(), qualityindicators: { numforecasts: Number(predictionData.numForecasts), stars: calculateStars(platformName, {}), diff --git a/src/backend/platforms/foretold.ts b/src/backend/platforms/foretold.ts index 92a27bd..9adce77 100644 --- a/src/backend/platforms/foretold.ts +++ b/src/backend/platforms/foretold.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ @@ -61,7 +61,7 @@ export const foretold: Platform = { label: "Foretold", color: "#62520b", async fetcher() { - let results = []; + let results: FetchedQuestion[] = []; for (let community of highQualityCommunities) { let questions = await fetchAllCommunityQuestions(community); questions = questions.map((question) => question.node); @@ -84,14 +84,13 @@ export const foretold: Platform = { }, ]; } - let result = { - id: id, + let result: FetchedQuestion = { + id, title: question.name, url: `https://www.foretold.io/c/${community}/m/${question.id}`, platform: platformName, description: "", - options: options, - timestamp: new Date().toISOString(), + options, qualityindicators: { numforecasts: Math.floor(Number(question.measurementCount) / 2), stars: calculateStars(platformName, {}), diff --git a/src/backend/platforms/givewellopenphil.ts b/src/backend/platforms/givewellopenphil.ts index e8170ac..22c37b8 100644 --- a/src/backend/platforms/givewellopenphil.ts +++ b/src/backend/platforms/givewellopenphil.ts @@ -47,12 +47,12 @@ async function main1() { ); let description = "

({ ...datum, platform: platformName, - timestamp: "2021-02-23", + timestamp: new Date("2021-02-23"), })); return dataWithDate; }, diff --git a/src/backend/platforms/goodjudgment.ts b/src/backend/platforms/goodjudgment.ts index 17227fa..499e8f0 100644 --- a/src/backend/platforms/goodjudgment.ts +++ b/src/backend/platforms/goodjudgment.ts @@ -5,7 +5,7 @@ import tunnel from "tunnel"; import { hash } from "../utils/hash"; import { calculateStars } from "../utils/stars"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "goodjudgment"; @@ -57,7 +57,7 @@ export const goodjudgment: Platform = { .then((query) => query.data); // Processing - let results = []; + let results: FetchedQuestion[] = []; let jsonTable = Tabletojson.convert(content, { stripHtmlFromCells: false }); jsonTable.shift(); // deletes first element jsonTable.pop(); // deletes last element @@ -100,14 +100,13 @@ export const goodjudgment: Platform = { analysis = analysis ? analysis[0] : ""; analysis = analysis ? analysis[0] : ""; // not a duplicate // console.log(analysis) - let standardObj = { - id: id, - title: title, + let standardObj: FetchedQuestion = { + id, + title, url: endpoint, platform: platformName, - description: description, - options: options, - timestamp: new Date().toISOString(), + description, + options, qualityindicators: { stars: calculateStars(platformName, {}), }, diff --git a/src/backend/platforms/goodjudgmentopen.ts b/src/backend/platforms/goodjudgmentopen.ts index a4e73f0..a4e1087 100644 --- a/src/backend/platforms/goodjudgmentopen.ts +++ b/src/backend/platforms/goodjudgmentopen.ts @@ -114,7 +114,6 @@ async function fetchStats(questionUrl, cookie) { let result = { description: description, options: options, - timestamp: new Date().toISOString(), qualityindicators: { numforecasts: Number(numforecasts), numforecasters: Number(numforecasters), diff --git a/src/backend/platforms/index.ts b/src/backend/platforms/index.ts index bbf87bd..f9539cb 100644 --- a/src/backend/platforms/index.ts +++ b/src/backend/platforms/index.ts @@ -1,4 +1,6 @@ -import { pgUpsert } from "../database/pg-wrapper"; +import { Question } from "@prisma/client"; + +import { prisma } from "../database/prisma"; import { betfair } from "./betfair"; import { fantasyscotus } from "./fantasyscotus"; import { foretold } from "./foretold"; @@ -28,57 +30,23 @@ export interface QualityIndicators { tradevolume?: string; pool?: any; createdTime?: any; + shares_volume?: any; + yes_bid?: any; + yes_ask?: any; + spread?: any; } -export interface Question { - id: string; - // "fantasyscotus-580" - - title: string; - // "In Wooden v. U.S., the SCOTUS will affirm the lower court's decision" - - url: string; - // "https://fantasyscotus.net/user-predictions/case/wooden-v-us/" - - description: string; - // "62.50% (75 out of 120) of FantasySCOTUS players predict that the lower court's decision will be affirmed. FantasySCOTUS overall predicts an outcome of Affirm 6-3. Historically, FantasySCOTUS has chosen the correct side 50.00% of the time." - platform: string; - // "FantasySCOTUS" - - options: any[]; - /* - [ - { - "name": "Yes", - "probability": 0.625, - "type": "PROBABILITY" - }, - { - "name": "No", - "probability": 0.375, - "type": "PROBABILITY" - } - ] - */ - - timestamp: string; - // "2022-02-11T21:42:19.291Z" - - stars?: number; - // 2 - - qualityindicators: QualityIndicators; - /* - { - "numforecasts": 120, - "stars": 2 - } - */ - extra?: any; -} +export type FetchedQuestion = Omit< + Question, + "extra" | "qualityindicators" | "timestamp" +> & { + timestamp?: Date; + extra?: object; // required in DB but annoying to return empty; also this is slightly stricter than Prisma's JsonValue + qualityindicators: QualityIndicators; // slightly stronger type than Prisma's JsonValue +}; // fetcher should return null if platform failed to fetch questions for some reason -export type PlatformFetcher = () => Promise; +export type PlatformFetcher = () => Promise; export interface Platform { name: string; // short name for ids and `platform` db column, e.g. "xrisk" @@ -95,13 +63,6 @@ export interface Platform { // export type PlatformFetcher = (options: FetchOptions) => Promise; -// interface Platform { -// name: string; -// color?: string; -// longName: string; -// fetcher: PlatformFetcher; -// } - export const platforms: Platform[] = [ betfair, fantasyscotus, @@ -126,13 +87,23 @@ export const processPlatform = async (platform: Platform) => { console.log(`Platform ${platform.name} doesn't have a fetcher, skipping`); return; } - let results = await platform.fetcher(); + const results = await platform.fetcher(); if (results && results.length) { - await pgUpsert({ - contents: results, - tableName: "questions", - replacePlatform: platform.name, - }); + await prisma.$transaction([ + prisma.question.deleteMany({ + where: { + platform: platform.name, + }, + }), + prisma.question.createMany({ + data: results.map((q) => ({ + extra: {}, + timestamp: new Date(), + ...q, + qualityindicators: q.qualityindicators as object, // fighting typescript + })), + }), + ]); console.log("Done"); } else { console.log(`Platform ${platform.name} didn't return any results`); diff --git a/src/backend/platforms/infer.ts b/src/backend/platforms/infer.ts index 23a78d6..cf62fd0 100644 --- a/src/backend/platforms/infer.ts +++ b/src/backend/platforms/infer.ts @@ -5,7 +5,7 @@ import { applyIfSecretExists } from "../utils/getSecrets"; import { measureTime } from "../utils/measureTime"; import { calculateStars } from "../utils/stars"; import toMarkdown from "../utils/toMarkdown"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "infer"; @@ -105,7 +105,6 @@ async function fetchStats(questionUrl, cookie) { let result = { description: description, options: options, - timestamp: new Date().toISOString(), qualityindicators: { numforecasts: Number(numforecasts), numforecasters: Number(numforecasters), @@ -147,7 +146,7 @@ function sleep(ms) { async function infer_inner(cookie: string) { let i = 1; let response = await fetchPage(i, cookie); - let results: Question[] = []; + let results: FetchedQuestion[] = []; await measureTime(async () => { // console.log("Downloading... This might take a couple of minutes. Results will be shown.") @@ -178,7 +177,7 @@ async function infer_inner(cookie: string) { let questionNumRegex = new RegExp("questions/([0-9]+)"); let questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0]; let id = `${platformName}-${questionNum}`; - let question: Question = { + let question: FetchedQuestion = { id: id, title: title, description: moreinfo.description, diff --git a/src/backend/platforms/kalshi.ts b/src/backend/platforms/kalshi.ts index c785a6a..6330298 100644 --- a/src/backend/platforms/kalshi.ts +++ b/src/backend/platforms/kalshi.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "kalshi"; @@ -22,8 +22,8 @@ async function processMarkets(markets) { // console.log(markets) markets = markets.filter((market) => market.close_date > dateNow); let results = await markets.map((market) => { - let probability = market.last_price / 100; - let options = [ + const probability = market.last_price / 100; + const options = [ { name: "Yes", probability: probability, @@ -35,15 +35,14 @@ async function processMarkets(markets) { type: "PROBABILITY", }, ]; - let id = `${platformName}-${market.id}`; - let result = { - id: id, + const id = `${platformName}-${market.id}`; + const result: FetchedQuestion = { + id, title: market.title.replaceAll("*", ""), url: `https://kalshi.com/markets/${market.ticker_name}`, platform: platformName, description: `${market.settle_details}. The resolution source is: ${market.ranged_group_name} (${market.settle_source_url})`, - options: options, - timestamp: new Date().toISOString(), + options, qualityindicators: { stars: calculateStars(platformName, { shares_volume: market.volume, diff --git a/src/backend/platforms/manifold.ts b/src/backend/platforms/manifold.ts index 8304c61..ab09aa2 100644 --- a/src/backend/platforms/manifold.ts +++ b/src/backend/platforms/manifold.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "manifold"; @@ -23,7 +23,7 @@ async function fetchData() { return response; } -function showStatistics(results: Question[]) { +function showStatistics(results: FetchedQuestion[]) { console.log(`Num unresolved markets: ${results.length}`); let sum = (arr) => arr.reduce((tally, a) => tally + a, 0); let num2StarsOrMore = results.filter( @@ -44,7 +44,7 @@ function showStatistics(results: Question[]) { } async function processPredictions(predictions) { - let results: Question[] = await predictions.map((prediction) => { + let results: FetchedQuestion[] = await predictions.map((prediction) => { let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name let probability = prediction.probability; let options = [ @@ -59,14 +59,13 @@ async function processPredictions(predictions) { type: "PROBABILITY", }, ]; - const result: Question = { + const result: FetchedQuestion = { id: id, title: prediction.question, url: prediction.url, platform: platformName, description: prediction.description, options: options, - timestamp: new Date().toISOString(), qualityindicators: { stars: calculateStars(platformName, { volume7Days: prediction.volume7Days, @@ -86,7 +85,7 @@ async function processPredictions(predictions) { }); const unresolvedResults = results.filter( - (result) => !result.extra.isResolved + (result) => !(result.extra as any).isResolved ); return unresolvedResults; } diff --git a/src/backend/platforms/metaculus.ts b/src/backend/platforms/metaculus.ts index 76ac37f..a567510 100644 --- a/src/backend/platforms/metaculus.ts +++ b/src/backend/platforms/metaculus.ts @@ -3,7 +3,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; import toMarkdown from "../utils/toMarkdown"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "metaculus"; @@ -148,14 +148,13 @@ export const metaculus: Platform = { ]; } let id = `${platformName}-${result.id}`; - let interestingInfo = { - id: id, + let interestingInfo: FetchedQuestion = { + id, title: result.title, url: "https://www.metaculus.com" + result.page_url, platform: platformName, - description: description, - options: options, - timestamp: new Date().toISOString(), + description, + options, qualityindicators: { numforecasts: Number(result.number_of_predictions), stars: calculateStars(platformName, { diff --git a/src/backend/platforms/polymarket.ts b/src/backend/platforms/polymarket.ts index 72c53f1..a91c42c 100644 --- a/src/backend/platforms/polymarket.ts +++ b/src/backend/platforms/polymarket.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "polymarket"; @@ -68,7 +68,7 @@ export const polymarket: Platform = { label: "PolyMarket", color: "#00314e", async fetcher() { - let results: Question[] = []; + let results: FetchedQuestion[] = []; let webpageEndpointData = await fetchAllContractInfo(); for (let marketInfo of webpageEndpointData) { let address = marketInfo.marketMakerAddress; @@ -102,14 +102,13 @@ export const polymarket: Platform = { }); } - let result: Question = { + let result: FetchedQuestion = { id: id, title: marketInfo.question, url: "https://polymarket.com/market/" + marketInfo.slug, platform: platformName, description: marketInfo.description, options: options, - timestamp: new Date().toISOString(), qualityindicators: { numforecasts: numforecasts.toFixed(0), liquidity: liquidity.toFixed(2), diff --git a/src/backend/platforms/predictit.ts b/src/backend/platforms/predictit.ts index 55e44cc..0233ec1 100644 --- a/src/backend/platforms/predictit.ts +++ b/src/backend/platforms/predictit.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; import toMarkdown from "../utils/toMarkdown"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; const platformName = "predictit"; @@ -53,7 +53,7 @@ export const predictit: Platform = { })); // console.log(markets) - let results = []; + let results: FetchedQuestion[] = []; for (let market of markets) { // console.log(market.name) let id = `${platformName}-${market.id}`; @@ -96,17 +96,16 @@ export const predictit: Platform = { ]; } - let obj = { - id: id, + const obj: FetchedQuestion = { + id, title: market["name"], url: market.url, platform: platformName, - description: description, - options: options, - timestamp: new Date().toISOString(), + description, + options, qualityindicators: { stars: calculateStars(platformName, {}), - shares_volume: shares_volume, + shares_volume, }, }; // console.log(obj) diff --git a/src/backend/platforms/rootclaim.ts b/src/backend/platforms/rootclaim.ts index d195cd7..1a1feff 100644 --- a/src/backend/platforms/rootclaim.ts +++ b/src/backend/platforms/rootclaim.ts @@ -3,7 +3,7 @@ import { JSDOM } from "jsdom"; import { calculateStars } from "../utils/stars"; import toMarkdown from "../utils/toMarkdown"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; const platformName = "rootclaim"; const jsonEndpoint = @@ -50,7 +50,7 @@ export const rootclaim: Platform = { color: "#0d1624", async fetcher() { const claims = await fetchAllRootclaims(); - const results: Question[] = []; + const results: FetchedQuestion[] = []; for (const claim of claims) { const id = `${platformName}-${claim.slug.toLowerCase()}`; @@ -71,14 +71,13 @@ export const rootclaim: Platform = { const description = await fetchDescription(url, claim.isclaim); - let obj: Question = { + let obj: FetchedQuestion = { id, title: toMarkdown(claim.question).replace("\n", ""), url, platform: platformName, description: toMarkdown(description).replace("'", "'"), options: options, - timestamp: new Date().toISOString(), qualityindicators: { numforecasts: 1, stars: calculateStars(platformName, {}), diff --git a/src/backend/platforms/smarkets.ts b/src/backend/platforms/smarkets.ts index 845caea..1b95af8 100644 --- a/src/backend/platforms/smarkets.ts +++ b/src/backend/platforms/smarkets.ts @@ -1,7 +1,7 @@ import axios from "axios"; import { calculateStars } from "../utils/stars"; -import { Platform, Question } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "smarkets"; @@ -159,14 +159,14 @@ export const smarkets: Platform = { name = name+ (contractName=="Yes"?'':` (${contracts["contracts"][0].name})`) } */ - let result: Question = { + let result: FetchedQuestion = { id: id, title: name, url: "https://smarkets.com/event/" + market.event_id + market.slug, platform: platformName, description: market.description, options: options, - timestamp: new Date().toISOString(), + timestamp: new Date(), qualityindicators: { stars: calculateStars(platformName, {}), }, diff --git a/src/backend/platforms/wildeford.ts b/src/backend/platforms/wildeford.ts index e628516..7778b04 100644 --- a/src/backend/platforms/wildeford.ts +++ b/src/backend/platforms/wildeford.ts @@ -4,7 +4,7 @@ import { GoogleSpreadsheet } from "google-spreadsheet"; import { applyIfSecretExists } from "../utils/getSecrets"; import { hash } from "../utils/hash"; import { calculateStars } from "../utils/stars"; -import { Platform } from "./"; +import { FetchedQuestion, Platform } from "./"; /* Definitions */ const platformName = "wildeford"; @@ -88,16 +88,14 @@ async function processPredictions(predictions) { type: "PROBABILITY", }, ]; - let result = { - id: id, - title: title, + let result: FetchedQuestion = { + id, + title, url: prediction["url"], platform: platformName, description: prediction["Notes"] || "", - options: options, - timestamp: new Date( - Date.parse(prediction["Prediction Date"] + "Z") - ).toISOString(), + options, + timestamp: new Date(Date.parse(prediction["Prediction Date"] + "Z")), qualityindicators: { stars: calculateStars(platformName, null), }, diff --git a/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts b/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts index 1c044cf..c02315e 100644 --- a/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts +++ b/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts @@ -1,7 +1,7 @@ /* Imports */ import fs from "fs"; -import { pgRead } from "../../database/pg-wrapper"; +import { prisma } from "../../database/prisma"; /* Definitions */ @@ -24,7 +24,7 @@ const main = async () => { "PredictIt", "Rootclaim", ]; - const json = await pgRead({ tableName: "questions" }); + const json = await prisma.question.findMany({}); console.log(json.length); //let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))] //console.log(uniquePlatforms) diff --git a/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts b/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts index 6cc2231..cbc048d 100644 --- a/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts +++ b/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts @@ -2,7 +2,7 @@ import fs from "fs"; import { shuffleArray } from "../../../utils"; -import { pgRead } from "../../database/pg-wrapper"; +import { prisma } from "../../database/prisma"; /* Definitions */ @@ -18,7 +18,7 @@ let getQualityIndicators = (question) => let main = async () => { let highQualityPlatforms = ["Metaculus"]; // ['CSET-foretell', 'Foretold', 'Good Judgment Open', 'Metaculus', 'PredictIt', 'Rootclaim'] - let json = await pgRead({ tableName: "questions" }); + let json = await prisma.question.findMany({}); console.log(json.length); //let uniquePlatforms = [...new Set(json.map(question => question.platform))] //console.log(uniquePlatforms) diff --git a/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts b/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts index 40ddd9e..bf73046 100644 --- a/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts +++ b/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts @@ -38,7 +38,7 @@ for (let datum of data) { */ timestamp: "2021-02-23T15∶21∶37.005Z", //new Date().toISOString(), qualityindicators: { - stars: datum.qualityindicators.stars, //datum["stars"], + stars: datum.qualityindicators.stars, }, }; results.push(result); diff --git a/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts b/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts index aa53194..1c1f327 100644 --- a/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts +++ b/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts @@ -40,7 +40,7 @@ ${datum["description"]}` ], timestamp: new Date().toISOString(), qualityindicators: { - stars: 2, //datum["stars"] + stars: 2, }, }; results.push(result); diff --git a/src/backend/utils/misc/process-forecasts-from-xrisk.ts b/src/backend/utils/misc/process-forecasts-from-xrisk.ts index 5ea0c97..dc5c847 100644 --- a/src/backend/utils/misc/process-forecasts-from-xrisk.ts +++ b/src/backend/utils/misc/process-forecasts-from-xrisk.ts @@ -23,7 +23,7 @@ for (let datum of data) { options: datum.options, timestamp: datum.timestamps, qualityindicators: { - stars: 2, //datum["stars"] + stars: 2, }, }; results.push(result); diff --git a/src/backend/utils/misc/process-forecasts-into-elicit.ts b/src/backend/utils/misc/process-forecasts-into-elicit.ts index c6ce4fb..8cf28b7 100644 --- a/src/backend/utils/misc/process-forecasts-into-elicit.ts +++ b/src/backend/utils/misc/process-forecasts-into-elicit.ts @@ -1,7 +1,8 @@ /* Imports */ import fs from "fs"; -import { pgRead } from "../../database/pg-wrapper"; +import { prisma } from "../../database/prisma"; +import { QualityIndicators } from "../../platforms"; /* Definitions */ let locationData = "./data/"; @@ -9,8 +10,8 @@ let locationData = "./data/"; /* Body */ // let rawdata = fs.readFileSync("./data/merged-questions.json") // run from topmost folder, not from src async function main() { - let data = await pgRead({ tableName: "questions" }); //JSON.parse(rawdata) - let processDescription = (description) => { + const data = await prisma.question.findMany({}); + const processDescription = (description) => { if (description == null || description == undefined || description == "") { return ""; } else { @@ -32,14 +33,14 @@ async function main() { }; let results = []; - for (let datum of data) { + for (const datum of data) { // do something - let description = processDescription(datum["description"]); - let forecasts = datum["qualityindicators"] - ? datum["qualityindicators"].numforecasts + const description = processDescription(datum["description"]); + const forecasts = datum["qualityindicators"] + ? (datum["qualityindicators"] as object as QualityIndicators).numforecasts : "unknown"; - let stars = datum["qualityindicators"] - ? datum["qualityindicators"].stars + const stars = datum["qualityindicators"] + ? (datum["qualityindicators"] as object as QualityIndicators).stars : 2; results.push("Title: " + datum["title"]); results.push("URL: " + datum["url"]); diff --git a/src/web/worker/searchGuesstimate.ts b/src/web/worker/searchGuesstimate.ts index 8af4a4c..51b20d3 100644 --- a/src/web/worker/searchGuesstimate.ts +++ b/src/web/worker/searchGuesstimate.ts @@ -43,11 +43,10 @@ export default async function searchGuesstimate( description, options: [], qualityindicators: { - stars: stars, + stars, numforecasts: 1, numforecasters: 1, }, - stars, extra: { visualization: model.big_screenshot, }, diff --git a/src/web/worker/searchWithAlgolia.ts b/src/web/worker/searchWithAlgolia.ts index ce89420..9eba5b4 100644 --- a/src/web/worker/searchWithAlgolia.ts +++ b/src/web/worker/searchWithAlgolia.ts @@ -116,7 +116,6 @@ export default async function searchWithAlgolia({ }, ], timestamp: `${new Date().toISOString().slice(0, 10)}`, - stars: 5, // legacy qualityindicators: { numforecasts: 1, numforecasters: 1, @@ -148,7 +147,6 @@ export default async function searchWithAlgolia({ }, ], timestamp: `${new Date().toISOString().slice(0, 10)}`, - stars: 5, // legacy qualityindicators: { numforecasts: 1, numforecasters: 1, @@ -183,7 +181,6 @@ export default async function searchWithAlgolia({ }, ], timestamp: `${new Date().toISOString().slice(0, 10)}`, - stars: 1, // legacy qualityindicators: { numforecasts: 1, numforecasters: 1,