refactor: rename forecast -> question (see #40)
This commit is contained in:
		
							parent
							
								
									2ee82cdd15
								
							
						
					
					
						commit
						6543a729f3
					
				|  | @ -13,7 +13,7 @@ | ||||||
| # React | # React | ||||||
| 
 | 
 | ||||||
| - create one file per one component (tiny helper components in the same file are fine) | - create one file per one component (tiny helper components in the same file are fine) | ||||||
| - name file identically to the component it describes (e.g. `const DisplayForecasts: React.FC<Props> = ...` in `DisplayForecasts.ts`) | - name file identically to the component it describes (e.g. `const DisplayQuestions: React.FC<Props> = ...` in `DisplayQuestions.ts`) | ||||||
| - use named export instead of default export for all React components | - use named export instead of default export for all React components | ||||||
|   - it's better for refactoring |   - it's better for refactoring | ||||||
|   - and it plays well with `React.FC` typing |   - and it plays well with `React.FC` typing | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| import { Pool, PoolClient } from "pg"; | import { Pool, PoolClient } from "pg"; | ||||||
| 
 | 
 | ||||||
| import { Forecast } from "../platforms"; | import { Question } from "../platforms"; | ||||||
| import { hash } from "../utils/hash"; | import { hash } from "../utils/hash"; | ||||||
| import { measureTime } from "../utils/measureTime"; | import { measureTime } from "../utils/measureTime"; | ||||||
| import { roughSizeOfObject } from "../utils/roughSize"; | import { roughSizeOfObject } from "../utils/roughSize"; | ||||||
| 
 | 
 | ||||||
| const forecastTableNames = ["questions", "history"]; | const questionTableNames = ["questions", "history"]; | ||||||
| 
 | 
 | ||||||
| const allTableNames = [...forecastTableNames, "dashboards", "frontpage"]; | const allTableNames = [...questionTableNames, "dashboards", "frontpage"]; | ||||||
| 
 | 
 | ||||||
| /* Postgres database connection code */ | /* Postgres database connection code */ | ||||||
| const databaseURL = process.env.DIGITALOCEAN_POSTGRES; | const databaseURL = process.env.DIGITALOCEAN_POSTGRES; | ||||||
|  | @ -51,11 +51,11 @@ export async function pgBulkInsert({ | ||||||
|   tableName, |   tableName, | ||||||
|   client, |   client, | ||||||
| }: { | }: { | ||||||
|   data: Forecast[]; |   data: Question[]; | ||||||
|   tableName: string; |   tableName: string; | ||||||
|   client: PoolClient; |   client: PoolClient; | ||||||
| }) { | }) { | ||||||
|   if (!forecastTableNames.includes(tableName)) { |   if (!questionTableNames.includes(tableName)) { | ||||||
|     throw Error( |     throw Error( | ||||||
|       `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` |       `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` | ||||||
|     ); |     ); | ||||||
|  | @ -171,11 +171,11 @@ export async function pgUpsert({ | ||||||
|   tableName, |   tableName, | ||||||
|   replacePlatform, |   replacePlatform, | ||||||
| }: { | }: { | ||||||
|   contents: Forecast[]; |   contents: Question[]; | ||||||
|   tableName: string; |   tableName: string; | ||||||
|   replacePlatform?: string; |   replacePlatform?: string; | ||||||
| }) { | }) { | ||||||
|   if (!forecastTableNames.includes(tableName)) { |   if (!questionTableNames.includes(tableName)) { | ||||||
|     throw Error( |     throw Error( | ||||||
|       `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` |       `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections` | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import { pgRead, pool } from "./database/pg-wrapper"; | import { pgRead, pool } from "./database/pg-wrapper"; | ||||||
| import { Forecast } from "./platforms"; | import { Question } from "./platforms"; | ||||||
| 
 | 
 | ||||||
| export async function getFrontpage(): Promise<Forecast[]> { | export async function getFrontpage(): Promise<Question[]> { | ||||||
|   const res = await pool.query( |   const res = await pool.query( | ||||||
|     "SELECT frontpage_sliced FROM frontpage ORDER BY id DESC LIMIT 1" |     "SELECT frontpage_sliced FROM frontpage ORDER BY id DESC LIMIT 1" | ||||||
|   ); |   ); | ||||||
|  | @ -9,7 +9,7 @@ export async function getFrontpage(): Promise<Forecast[]> { | ||||||
|   return res.rows[0].frontpage_sliced; |   return res.rows[0].frontpage_sliced; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function getFrontpageFull(): Promise<Forecast[]> { | export async function getFrontpageFull(): Promise<Question[]> { | ||||||
|   const res = await pool.query( |   const res = await pool.query( | ||||||
|     "SELECT frontpage_full FROM frontpage ORDER BY id DESC LIMIT 1" |     "SELECT frontpage_full FROM frontpage ORDER BY id DESC LIMIT 1" | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ import axios from "axios"; | ||||||
| import https from "https"; | import https from "https"; | ||||||
| 
 | 
 | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| const platformName = "betfair"; | const platformName = "betfair"; | ||||||
| 
 | 
 | ||||||
|  | @ -80,7 +80,7 @@ async function whipIntoShape(data) { | ||||||
| async function processPredictions(data) { | async function processPredictions(data) { | ||||||
|   let predictions = await whipIntoShape(data); |   let predictions = await whipIntoShape(data); | ||||||
|   // console.log(JSON.stringify(predictions, null, 4))
 |   // console.log(JSON.stringify(predictions, null, 4))
 | ||||||
|   let results: Forecast[] = predictions.map((prediction) => { |   let results: Question[] = predictions.map((prediction) => { | ||||||
|     /* if(Math.floor(Math.random() * 10) % 20 ==0){ |     /* if(Math.floor(Math.random() * 10) % 20 ==0){ | ||||||
|        console.log(JSON.stringify(prediction, null, 4)) |        console.log(JSON.stringify(prediction, null, 4)) | ||||||
|     } */ |     } */ | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| const platformName = "fantasyscotus"; | const platformName = "fantasyscotus"; | ||||||
| 
 | 
 | ||||||
|  | @ -67,7 +67,7 @@ async function processData(data) { | ||||||
|   let historicalPercentageCorrect = data.stats.pcnt_correct; |   let historicalPercentageCorrect = data.stats.pcnt_correct; | ||||||
|   let historicalProbabilityCorrect = |   let historicalProbabilityCorrect = | ||||||
|     Number(historicalPercentageCorrect.replace("%", "")) / 100; |     Number(historicalPercentageCorrect.replace("%", "")) / 100; | ||||||
|   let results: Forecast[] = []; |   let results: Question[] = []; | ||||||
|   for (let event of events) { |   for (let event of events) { | ||||||
|     if (event.accuracy == "") { |     if (event.accuracy == "") { | ||||||
|       let id = `${platformName}-${event.id}`; |       let id = `${platformName}-${event.id}`; | ||||||
|  | @ -75,7 +75,7 @@ async function processData(data) { | ||||||
|       let predictionData = await getPredictionsData(event.docket_url); |       let predictionData = await getPredictionsData(event.docket_url); | ||||||
|       let pAffirm = predictionData.proportionAffirm; |       let pAffirm = predictionData.proportionAffirm; | ||||||
|       //let trackRecord = event.prediction.includes("Affirm") ? historicalProbabilityCorrect : 1-historicalProbabilityCorrect
 |       //let trackRecord = event.prediction.includes("Affirm") ? historicalProbabilityCorrect : 1-historicalProbabilityCorrect
 | ||||||
|       let eventObject: Forecast = { |       let eventObject: Question = { | ||||||
|         id: id, |         id: id, | ||||||
|         title: `In ${event.short_name}, the SCOTUS will affirm the lower court's decision`, |         title: `In ${event.short_name}, the SCOTUS will affirm the lower court's decision`, | ||||||
|         url: `https://fantasyscotus.net/user-predictions${event.docket_url}`, |         url: `https://fantasyscotus.net/user-predictions${event.docket_url}`, | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ import { smarkets } from "./smarkets"; | ||||||
| import { wildeford } from "./wildeford"; | import { wildeford } from "./wildeford"; | ||||||
| import { xrisk } from "./xrisk"; | import { xrisk } from "./xrisk"; | ||||||
| 
 | 
 | ||||||
| export interface Forecast { | export interface Question { | ||||||
|   id: string; |   id: string; | ||||||
|   // "fantasyscotus-580"
 |   // "fantasyscotus-580"
 | ||||||
| 
 | 
 | ||||||
|  | @ -63,8 +63,8 @@ export interface Forecast { | ||||||
|   extra?: any; |   extra?: any; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // fetcher should return null if platform failed to fetch forecasts for some reason
 | // fetcher should return null if platform failed to fetch questions for some reason
 | ||||||
| export type PlatformFetcher = () => Promise<Forecast[] | null>; | export type PlatformFetcher = () => Promise<Question[] | null>; | ||||||
| 
 | 
 | ||||||
| export interface Platform { | export interface Platform { | ||||||
|   name: string; // short name for ids and `platform` db column, e.g. "xrisk"
 |   name: string; // short name for ids and `platform` db column, e.g. "xrisk"
 | ||||||
|  | @ -76,7 +76,7 @@ export interface Platform { | ||||||
| // draft for the future callback-based streaming/chunking API:
 | // draft for the future callback-based streaming/chunking API:
 | ||||||
| // interface FetchOptions {
 | // interface FetchOptions {
 | ||||||
| //   since?: string; // some kind of cursor, Date object or opaque string?
 | //   since?: string; // some kind of cursor, Date object or opaque string?
 | ||||||
| //   save: (forecasts: Forecast[]) => Promise<void>;
 | //   save: (questions: Question[]) => Promise<void>;
 | ||||||
| // }
 | // }
 | ||||||
| 
 | 
 | ||||||
| // export type PlatformFetcher = (options: FetchOptions) => Promise<void>;
 | // export type PlatformFetcher = (options: FetchOptions) => Promise<void>;
 | ||||||
|  |  | ||||||
|  | @ -1,12 +1,11 @@ | ||||||
| /* Imports */ | /* Imports */ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| import { Tabletojson } from "tabletojson"; |  | ||||||
| 
 | 
 | ||||||
| import { applyIfSecretExists } from "../utils/getSecrets"; | import { applyIfSecretExists } from "../utils/getSecrets"; | ||||||
| import { measureTime } from "../utils/measureTime"; | import { measureTime } from "../utils/measureTime"; | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import toMarkdown from "../utils/toMarkdown"; | import toMarkdown from "../utils/toMarkdown"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| /* Definitions */ | /* Definitions */ | ||||||
| const platformName = "infer"; | const platformName = "infer"; | ||||||
|  | @ -78,12 +77,12 @@ async function fetchStats(questionUrl, cookie) { | ||||||
|   let comments_count = firstEmbeddedJson.question.comments_count; |   let comments_count = firstEmbeddedJson.question.comments_count; | ||||||
|   let numforecasters = firstEmbeddedJson.question.predictors_count; |   let numforecasters = firstEmbeddedJson.question.predictors_count; | ||||||
|   let numforecasts = firstEmbeddedJson.question.prediction_sets_count; |   let numforecasts = firstEmbeddedJson.question.prediction_sets_count; | ||||||
|   let forecastType = firstEmbeddedJson.question.type; |   let questionType = firstEmbeddedJson.question.type; | ||||||
|   if ( |   if ( | ||||||
|     forecastType.includes("Binary") || |     questionType.includes("Binary") || | ||||||
|     forecastType.includes("NonExclusiveOpinionPoolQuestion") || |     questionType.includes("NonExclusiveOpinionPoolQuestion") || | ||||||
|     forecastType.includes("Forecast::Question") || |     questionType.includes("Forecast::Question") || | ||||||
|     !forecastType.includes("Forecast::MultiTimePeriodQuestion") |     !questionType.includes("Forecast::MultiTimePeriodQuestion") | ||||||
|   ) { |   ) { | ||||||
|     options = firstEmbeddedJson.question.answers.map((answer) => ({ |     options = firstEmbeddedJson.question.answers.map((answer) => ({ | ||||||
|       name: answer.name, |       name: answer.name, | ||||||
|  | @ -148,7 +147,7 @@ function sleep(ms) { | ||||||
| async function infer_inner(cookie: string) { | async function infer_inner(cookie: string) { | ||||||
|   let i = 1; |   let i = 1; | ||||||
|   let response = await fetchPage(i, cookie); |   let response = await fetchPage(i, cookie); | ||||||
|   let results: Forecast[] = []; |   let results: Question[] = []; | ||||||
| 
 | 
 | ||||||
|   await measureTime(async () => { |   await measureTime(async () => { | ||||||
|     // console.log("Downloading... This might take a couple of minutes. Results will be shown.")
 |     // console.log("Downloading... This might take a couple of minutes. Results will be shown.")
 | ||||||
|  | @ -179,7 +178,7 @@ async function infer_inner(cookie: string) { | ||||||
|           let questionNumRegex = new RegExp("questions/([0-9]+)"); |           let questionNumRegex = new RegExp("questions/([0-9]+)"); | ||||||
|           let questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0];
 |           let questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0];
 | ||||||
|           let id = `${platformName}-${questionNum}`; |           let id = `${platformName}-${questionNum}`; | ||||||
|           let question: Forecast = { |           let question: Question = { | ||||||
|             id: id, |             id: id, | ||||||
|             title: title, |             title: title, | ||||||
|             description: moreinfo.description, |             description: moreinfo.description, | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| /* Definitions */ | /* Definitions */ | ||||||
| const platformName = "manifold"; | const platformName = "manifold"; | ||||||
|  | @ -23,7 +23,7 @@ async function fetchData() { | ||||||
|   return response; |   return response; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function showStatistics(results: Forecast[]) { | function showStatistics(results: Question[]) { | ||||||
|   console.log(`Num unresolved markets: ${results.length}`); |   console.log(`Num unresolved markets: ${results.length}`); | ||||||
|   let sum = (arr) => arr.reduce((tally, a) => tally + a, 0); |   let sum = (arr) => arr.reduce((tally, a) => tally + a, 0); | ||||||
|   let num2StarsOrMore = results.filter( |   let num2StarsOrMore = results.filter( | ||||||
|  | @ -44,7 +44,7 @@ function showStatistics(results: Forecast[]) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function processPredictions(predictions) { | async function processPredictions(predictions) { | ||||||
|   let results: Forecast[] = await predictions.map((prediction) => { |   let results: Question[] = await predictions.map((prediction) => { | ||||||
|     let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name
 |     let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name
 | ||||||
|     let probability = prediction.probability; |     let probability = prediction.probability; | ||||||
|     let options = [ |     let options = [ | ||||||
|  | @ -59,7 +59,7 @@ async function processPredictions(predictions) { | ||||||
|         type: "PROBABILITY", |         type: "PROBABILITY", | ||||||
|       }, |       }, | ||||||
|     ]; |     ]; | ||||||
|     const result: Forecast = { |     const result: Question = { | ||||||
|       id: id, |       id: id, | ||||||
|       title: prediction.question, |       title: prediction.question, | ||||||
|       url: prediction.url, |       url: prediction.url, | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| /* Definitions */ | /* Definitions */ | ||||||
| const platformName = "polymarket"; | const platformName = "polymarket"; | ||||||
|  | @ -68,7 +68,7 @@ export const polymarket: Platform = { | ||||||
|   label: "PolyMarket", |   label: "PolyMarket", | ||||||
|   color: "#00314e", |   color: "#00314e", | ||||||
|   async fetcher() { |   async fetcher() { | ||||||
|     let results: Forecast[] = []; |     let results: Question[] = []; | ||||||
|     let webpageEndpointData = await fetchAllContractInfo(); |     let webpageEndpointData = await fetchAllContractInfo(); | ||||||
|     for (let marketInfo of webpageEndpointData) { |     for (let marketInfo of webpageEndpointData) { | ||||||
|       let address = marketInfo.marketMakerAddress; |       let address = marketInfo.marketMakerAddress; | ||||||
|  | @ -102,7 +102,7 @@ export const polymarket: Platform = { | ||||||
|             }); |             }); | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           let result: Forecast = { |           let result: Question = { | ||||||
|             id: id, |             id: id, | ||||||
|             title: marketInfo.question, |             title: marketInfo.question, | ||||||
|             url: "https://polymarket.com/market/" + marketInfo.slug, |             url: "https://polymarket.com/market/" + marketInfo.slug, | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ import { JSDOM } from "jsdom"; | ||||||
| 
 | 
 | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import toMarkdown from "../utils/toMarkdown"; | import toMarkdown from "../utils/toMarkdown"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| const platformName = "rootclaim"; | const platformName = "rootclaim"; | ||||||
| const jsonEndpoint = | const jsonEndpoint = | ||||||
|  | @ -50,7 +50,7 @@ export const rootclaim: Platform = { | ||||||
|   color: "#0d1624", |   color: "#0d1624", | ||||||
|   async fetcher() { |   async fetcher() { | ||||||
|     const claims = await fetchAllRootclaims(); |     const claims = await fetchAllRootclaims(); | ||||||
|     const results: Forecast[] = []; |     const results: Question[] = []; | ||||||
| 
 | 
 | ||||||
|     for (const claim of claims) { |     for (const claim of claims) { | ||||||
|       const id = `${platformName}-${claim.slug.toLowerCase()}`; |       const id = `${platformName}-${claim.slug.toLowerCase()}`; | ||||||
|  | @ -71,7 +71,7 @@ export const rootclaim: Platform = { | ||||||
| 
 | 
 | ||||||
|       const description = await fetchDescription(url, claim.isclaim); |       const description = await fetchDescription(url, claim.isclaim); | ||||||
| 
 | 
 | ||||||
|       let obj: Forecast = { |       let obj: Question = { | ||||||
|         id, |         id, | ||||||
|         title: toMarkdown(claim.question).replace("\n", ""), |         title: toMarkdown(claim.question).replace("\n", ""), | ||||||
|         url, |         url, | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| import { calculateStars } from "../utils/stars"; | import { calculateStars } from "../utils/stars"; | ||||||
| import { Forecast, Platform } from "./"; | import { Platform, Question } from "./"; | ||||||
| 
 | 
 | ||||||
| /* Definitions */ | /* Definitions */ | ||||||
| const platformName = "smarkets"; | const platformName = "smarkets"; | ||||||
|  | @ -159,7 +159,7 @@ export const smarkets: Platform = { | ||||||
|       name = name+ (contractName=="Yes"?'':` (${contracts["contracts"][0].name})`) |       name = name+ (contractName=="Yes"?'':` (${contracts["contracts"][0].name})`) | ||||||
|     } |     } | ||||||
|     */ |     */ | ||||||
|       let result: Forecast = { |       let result: Question = { | ||||||
|         id: id, |         id: id, | ||||||
|         title: name, |         title: name, | ||||||
|         url: "https://smarkets.com/event/" + market.event_id + market.slug, |         url: "https://smarkets.com/event/" + market.event_id + market.slug, | ||||||
|  |  | ||||||
|  | @ -8,14 +8,14 @@ import { pgRead } from "../../database/pg-wrapper"; | ||||||
| /* Utilities */ | /* Utilities */ | ||||||
| 
 | 
 | ||||||
| /* Support functions */ | /* Support functions */ | ||||||
| let getQualityIndicators = (forecast) => | const getQualityIndicators = (question) => | ||||||
|   Object.entries(forecast.qualityindicators) |   Object.entries(question.qualityindicators) | ||||||
|     .map((entry) => `${entry[0]}: ${entry[1]}`) |     .map((entry) => `${entry[0]}: ${entry[1]}`) | ||||||
|     .join("; "); |     .join("; "); | ||||||
| 
 | 
 | ||||||
| /* Body */ | /* Body */ | ||||||
| 
 | 
 | ||||||
| let main = async () => { | const main = async () => { | ||||||
|   let highQualityPlatforms = [ |   let highQualityPlatforms = [ | ||||||
|     "CSET-foretell", |     "CSET-foretell", | ||||||
|     "Foretold", |     "Foretold", | ||||||
|  | @ -24,21 +24,21 @@ let main = async () => { | ||||||
|     "PredictIt", |     "PredictIt", | ||||||
|     "Rootclaim", |     "Rootclaim", | ||||||
|   ]; |   ]; | ||||||
|   let json = await pgRead({ tableName: "questions" }); |   const json = await pgRead({ tableName: "questions" }); | ||||||
|   console.log(json.length); |   console.log(json.length); | ||||||
|   //let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))]
 |   //let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))]
 | ||||||
|   //console.log(uniquePlatforms)
 |   //console.log(uniquePlatforms)
 | ||||||
| 
 | 
 | ||||||
|   let forecastsFromGoodPlatforms = json.filter((forecast) => |   const questionsFromGoodPlatforms = json.filter((question) => | ||||||
|     highQualityPlatforms.includes(forecast.platform) |     highQualityPlatforms.includes(question.platform) | ||||||
|   ); |   ); | ||||||
|   let tsv = |   const tsv = | ||||||
|     "index\ttitle\turl\tqualityindicators\n" + |     "index\ttitle\turl\tqualityindicators\n" + | ||||||
|     forecastsFromGoodPlatforms |     questionsFromGoodPlatforms | ||||||
|       .map((forecast, index) => { |       .map((question, index) => { | ||||||
|         let row = `${index}\t${forecast.title}\t${ |         let row = `${index}\t${question.title}\t${ | ||||||
|           forecast.url |           question.url | ||||||
|         }\t${getQualityIndicators(forecast)}`;
 |         }\t${getQualityIndicators(question)}`;
 | ||||||
|         console.log(row); |         console.log(row); | ||||||
|         return row; |         return row; | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|  | @ -8,8 +8,8 @@ import { pgRead } from "../../database/pg-wrapper"; | ||||||
| /* Utilities */ | /* Utilities */ | ||||||
| 
 | 
 | ||||||
| /* Support functions */ | /* Support functions */ | ||||||
| let getQualityIndicators = (forecast) => | let getQualityIndicators = (question) => | ||||||
|   Object.entries(forecast.qualityindicators) |   Object.entries(question.qualityindicators) | ||||||
|     .map((entry) => `${entry[0]}: ${entry[1]}`) |     .map((entry) => `${entry[0]}: ${entry[1]}`) | ||||||
|     .join("; "); |     .join("; "); | ||||||
| 
 | 
 | ||||||
|  | @ -28,22 +28,22 @@ let main = async () => { | ||||||
|   let highQualityPlatforms = ["Metaculus"]; // ['CSET-foretell', 'Foretold', 'Good Judgment Open', 'Metaculus', 'PredictIt', 'Rootclaim']
 |   let highQualityPlatforms = ["Metaculus"]; // ['CSET-foretell', 'Foretold', 'Good Judgment Open', 'Metaculus', 'PredictIt', 'Rootclaim']
 | ||||||
|   let json = await pgRead({ tableName: "questions" }); |   let json = await pgRead({ tableName: "questions" }); | ||||||
|   console.log(json.length); |   console.log(json.length); | ||||||
|   //let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))]
 |   //let uniquePlatforms = [...new Set(json.map(question => question.platform))]
 | ||||||
|   //console.log(uniquePlatforms)
 |   //console.log(uniquePlatforms)
 | ||||||
| 
 | 
 | ||||||
|   let forecastsFromGoodPlatforms = json.filter((forecast) => |   let questionsFromGoodPlatforms = json.filter((question) => | ||||||
|     highQualityPlatforms.includes(forecast.platform) |     highQualityPlatforms.includes(question.platform) | ||||||
|   ); |   ); | ||||||
|   let forecastsFromGoodPlatformsShuffled = shuffleArray( |   let questionsFromGoodPlatformsShuffled = shuffleArray( | ||||||
|     forecastsFromGoodPlatforms |     questionsFromGoodPlatforms | ||||||
|   ); |   ); | ||||||
|   let tsv = |   let tsv = | ||||||
|     "index\ttitle\turl\tqualityindicators\n" + |     "index\ttitle\turl\tqualityindicators\n" + | ||||||
|     forecastsFromGoodPlatforms |     questionsFromGoodPlatforms | ||||||
|       .map((forecast, index) => { |       .map((question, index) => { | ||||||
|         let row = `${index}\t${forecast.title}\t${ |         let row = `${index}\t${question.title}\t${ | ||||||
|           forecast.url |           question.url | ||||||
|         }\t${getQualityIndicators(forecast)}`;
 |         }\t${getQualityIndicators(question)}`;
 | ||||||
|         console.log(row); |         console.log(row); | ||||||
|         return row; |         return row; | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import { NextPage } from "next"; | import { NextPage } from "next"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| 
 | 
 | ||||||
| import { displayForecastsWrapperForCapture } from "../web/display/displayForecastsWrappers"; | import { displayQuestionsWrapperForCapture } from "../web/display/displayQuestionsWrappers"; | ||||||
| import { Layout } from "../web/display/Layout"; | import { Layout } from "../web/display/Layout"; | ||||||
| import { Props } from "../web/search/anySearchPage"; | import { Props } from "../web/search/anySearchPage"; | ||||||
| import CommonDisplay from "../web/search/CommonDisplay"; | import CommonDisplay from "../web/search/CommonDisplay"; | ||||||
|  | @ -18,7 +18,7 @@ const CapturePage: NextPage<Props> = (props) => { | ||||||
|         hasAdvancedOptions={false} |         hasAdvancedOptions={false} | ||||||
|         placeholder={"Get best title match..."} |         placeholder={"Get best title match..."} | ||||||
|         displaySeeMoreHint={false} |         displaySeeMoreHint={false} | ||||||
|         displayForecastsWrapper={displayForecastsWrapperForCapture} |         displayQuestionsWrapper={displayQuestionsWrapperForCapture} | ||||||
|       /> |       /> | ||||||
|     </Layout> |     </Layout> | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  | @ -2,13 +2,13 @@ import { GetServerSideProps, NextPage } from "next"; | ||||||
| import Error from "next/error"; | import Error from "next/error"; | ||||||
| 
 | 
 | ||||||
| import { DashboardItem } from "../../../backend/dashboards"; | import { DashboardItem } from "../../../backend/dashboards"; | ||||||
| import { DisplayForecasts } from "../../../web/display/DisplayForecasts"; | import { DisplayQuestions } from "../../../web/display/DisplayQuestions"; | ||||||
| import { FrontendForecast } from "../../../web/platforms"; | import { FrontendQuestion } from "../../../web/platforms"; | ||||||
| import { getDashboardForecastsByDashboardId } from "../../../web/worker/getDashboardForecasts"; |  | ||||||
| import { reqToBasePath } from "../../../web/utils"; | import { reqToBasePath } from "../../../web/utils"; | ||||||
|  | import { getDashboardQuestionsByDashboardId } from "../../../web/worker/getDashboardQuestions"; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   dashboardForecasts: FrontendForecast[]; |   dashboardQuestions: FrontendQuestion[]; | ||||||
|   dashboardItem: DashboardItem; |   dashboardItem: DashboardItem; | ||||||
|   numCols?: number; |   numCols?: number; | ||||||
| } | } | ||||||
|  | @ -19,8 +19,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
|   const dashboardId = context.query.id as string; |   const dashboardId = context.query.id as string; | ||||||
|   const numCols = Number(context.query.numCols); |   const numCols = Number(context.query.numCols); | ||||||
| 
 | 
 | ||||||
|   const { dashboardItem, dashboardForecasts } = |   const { dashboardItem, dashboardQuestions } = | ||||||
|     await getDashboardForecastsByDashboardId({ |     await getDashboardQuestionsByDashboardId({ | ||||||
|       dashboardId, |       dashboardId, | ||||||
|       basePath: reqToBasePath(context.req), // required on server side to find the API endpoint
 |       basePath: reqToBasePath(context.req), // required on server side to find the API endpoint
 | ||||||
|     }); |     }); | ||||||
|  | @ -31,7 +31,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     props: { |     props: { | ||||||
|       dashboardForecasts, |       dashboardQuestions, | ||||||
|       dashboardItem, |       dashboardItem, | ||||||
|       numCols: !numCols ? null : numCols < 5 ? numCols : 4, |       numCols: !numCols ? null : numCols < 5 ? numCols : 4, | ||||||
|     }, |     }, | ||||||
|  | @ -39,7 +39,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const EmbedDashboardPage: NextPage<Props> = ({ | const EmbedDashboardPage: NextPage<Props> = ({ | ||||||
|   dashboardForecasts, |   dashboardQuestions, | ||||||
|   dashboardItem, |   dashboardItem, | ||||||
|   numCols, |   numCols, | ||||||
| }) => { | }) => { | ||||||
|  | @ -57,9 +57,9 @@ const EmbedDashboardPage: NextPage<Props> = ({ | ||||||
|             numCols || 3 |             numCols || 3 | ||||||
|           } gap-4 mb-6`}
 |           } gap-4 mb-6`}
 | ||||||
|         > |         > | ||||||
|           <DisplayForecasts |           <DisplayQuestions | ||||||
|             results={dashboardForecasts} |             results={dashboardQuestions} | ||||||
|             numDisplay={dashboardForecasts.length} |             numDisplay={dashboardQuestions.length} | ||||||
|             showIdToggle={false} |             showIdToggle={false} | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  | @ -3,16 +3,16 @@ import Error from "next/error"; | ||||||
| import Link from "next/link"; | import Link from "next/link"; | ||||||
| 
 | 
 | ||||||
| import { DashboardItem } from "../../../backend/dashboards"; | import { DashboardItem } from "../../../backend/dashboards"; | ||||||
| import { DisplayForecasts } from "../../../web/display/DisplayForecasts"; | import { DisplayQuestions } from "../../../web/display/DisplayQuestions"; | ||||||
| import { InfoBox } from "../../../web/display/InfoBox"; | import { InfoBox } from "../../../web/display/InfoBox"; | ||||||
| import { Layout } from "../../../web/display/Layout"; | import { Layout } from "../../../web/display/Layout"; | ||||||
| import { LineHeader } from "../../../web/display/LineHeader"; | import { LineHeader } from "../../../web/display/LineHeader"; | ||||||
| import { FrontendForecast } from "../../../web/platforms"; | import { FrontendQuestion } from "../../../web/platforms"; | ||||||
| import { reqToBasePath } from "../../../web/utils"; | import { reqToBasePath } from "../../../web/utils"; | ||||||
| import { getDashboardForecastsByDashboardId } from "../../../web/worker/getDashboardForecasts"; | import { getDashboardQuestionsByDashboardId } from "../../../web/worker/getDashboardQuestions"; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   dashboardForecasts: FrontendForecast[]; |   dashboardQuestions: FrontendQuestion[]; | ||||||
|   dashboardItem: DashboardItem; |   dashboardItem: DashboardItem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -21,8 +21,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
| ) => { | ) => { | ||||||
|   const dashboardId = context.query.id as string; |   const dashboardId = context.query.id as string; | ||||||
| 
 | 
 | ||||||
|   const { dashboardForecasts, dashboardItem } = |   const { dashboardQuestions, dashboardItem } = | ||||||
|     await getDashboardForecastsByDashboardId({ |     await getDashboardQuestionsByDashboardId({ | ||||||
|       dashboardId, |       dashboardId, | ||||||
|       basePath: reqToBasePath(context.req), // required on server side to find the API endpoint
 |       basePath: reqToBasePath(context.req), // required on server side to find the API endpoint
 | ||||||
|     }); |     }); | ||||||
|  | @ -33,7 +33,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     props: { |     props: { | ||||||
|       dashboardForecasts, |       dashboardQuestions, | ||||||
|       dashboardItem, |       dashboardItem, | ||||||
|     }, |     }, | ||||||
|   }; |   }; | ||||||
|  | @ -78,7 +78,7 @@ const DashboardMetadata: React.FC<{ dashboardItem: DashboardItem }> = ({ | ||||||
| 
 | 
 | ||||||
| /* Body */ | /* Body */ | ||||||
| const ViewDashboardPage: NextPage<Props> = ({ | const ViewDashboardPage: NextPage<Props> = ({ | ||||||
|   dashboardForecasts, |   dashboardQuestions, | ||||||
|   dashboardItem, |   dashboardItem, | ||||||
| }) => { | }) => { | ||||||
|   return ( |   return ( | ||||||
|  | @ -91,9 +91,9 @@ const ViewDashboardPage: NextPage<Props> = ({ | ||||||
|         )} |         )} | ||||||
| 
 | 
 | ||||||
|         <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> |         <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | ||||||
|           <DisplayForecasts |           <DisplayQuestions | ||||||
|             results={dashboardForecasts} |             results={dashboardQuestions} | ||||||
|             numDisplay={dashboardForecasts.length} |             numDisplay={dashboardQuestions.length} | ||||||
|             showIdToggle={false} |             showIdToggle={false} | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import { NextPage } from "next"; | import { NextPage } from "next"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| 
 | 
 | ||||||
| import { displayForecastsWrapperForSearch } from "../web/display/displayForecastsWrappers"; | import { displayQuestionsWrapperForSearch } from "../web/display/displayQuestionsWrappers"; | ||||||
| import { Layout } from "../web/display/Layout"; | import { Layout } from "../web/display/Layout"; | ||||||
| import { Props } from "../web/search/anySearchPage"; | import { Props } from "../web/search/anySearchPage"; | ||||||
| import CommonDisplay from "../web/search/CommonDisplay"; | import CommonDisplay from "../web/search/CommonDisplay"; | ||||||
|  | @ -18,7 +18,7 @@ const IndexPage: NextPage<Props> = (props) => { | ||||||
|         hasAdvancedOptions={true} |         hasAdvancedOptions={true} | ||||||
|         placeholder={"Find forecasts about..."} |         placeholder={"Find forecasts about..."} | ||||||
|         displaySeeMoreHint={true} |         displaySeeMoreHint={true} | ||||||
|         displayForecastsWrapper={displayForecastsWrapperForSearch} |         displayQuestionsWrapper={displayQuestionsWrapperForSearch} | ||||||
|       /> |       /> | ||||||
|     </Layout> |     </Layout> | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  | @ -4,12 +4,12 @@ import { GetServerSideProps, NextPage } from "next"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| 
 | 
 | ||||||
| import { platforms } from "../backend/platforms"; | import { platforms } from "../backend/platforms"; | ||||||
| import { DisplayForecast } from "../web/display/DisplayForecast"; | import { DisplayQuestion } from "../web/display/DisplayQuestion"; | ||||||
| import { FrontendForecast } from "../web/platforms"; | import { FrontendQuestion } from "../web/platforms"; | ||||||
| import searchAccordingToQueryData from "../web/worker/searchAccordingToQueryData"; | import searchAccordingToQueryData from "../web/worker/searchAccordingToQueryData"; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   results: FrontendForecast[]; |   results: FrontendQuestion[]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const getServerSideProps: GetServerSideProps<Props> = async ( | export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
|  | @ -25,7 +25,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
|     ...urlQuery, |     ...urlQuery, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   let results: FrontendForecast[] = []; |   let results: FrontendQuestion[] = []; | ||||||
|   if (initialQueryParameters.query != "") { |   if (initialQueryParameters.query != "") { | ||||||
|     results = await searchAccordingToQueryData(initialQueryParameters, 1); |     results = await searchAccordingToQueryData(initialQueryParameters, 1); | ||||||
|   } |   } | ||||||
|  | @ -46,8 +46,8 @@ const SecretEmbedPage: NextPage<Props> = ({ results }) => { | ||||||
|         <div> |         <div> | ||||||
|           <div id="secretEmbed"> |           <div id="secretEmbed"> | ||||||
|             {result ? ( |             {result ? ( | ||||||
|               <DisplayForecast |               <DisplayQuestion | ||||||
|                 forecast={result} |                 question={result} | ||||||
|                 showTimeStamp={true} |                 showTimeStamp={true} | ||||||
|                 expandFooterToFullWidth={true} |                 expandFooterToFullWidth={true} | ||||||
|               /> |               /> | ||||||
|  |  | ||||||
|  | @ -1,38 +0,0 @@ | ||||||
| import React from "react"; |  | ||||||
| 
 |  | ||||||
| import { FrontendForecast } from "../platforms"; |  | ||||||
| import { DisplayForecast } from "./DisplayForecast"; |  | ||||||
| 
 |  | ||||||
| interface Props { |  | ||||||
|   results: FrontendForecast[]; |  | ||||||
|   numDisplay: number; |  | ||||||
|   showIdToggle: boolean; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export const DisplayForecasts: React.FC<Props> = ({ |  | ||||||
|   results, |  | ||||||
|   numDisplay, |  | ||||||
|   showIdToggle, |  | ||||||
| }) => { |  | ||||||
|   if (!results) { |  | ||||||
|     return <></>; |  | ||||||
|   } |  | ||||||
|   return ( |  | ||||||
|     <> |  | ||||||
|       {results.slice(0, numDisplay).map((result) => ( |  | ||||||
|         /*let displayWithMetaculusCapture = |  | ||||||
|           fuseSearchResult.item.platform == "Metaculus" |  | ||||||
|             ? metaculusEmbed(fuseSearchResult.item) |  | ||||||
|             : displayForecast({ ...fuseSearchResult.item }); |  | ||||||
|         */ |  | ||||||
|         <DisplayForecast |  | ||||||
|           key={result.id} |  | ||||||
|           forecast={result} |  | ||||||
|           showTimeStamp={false} |  | ||||||
|           expandFooterToFullWidth={false} |  | ||||||
|           showIdToggle={showIdToggle} |  | ||||||
|         /> |  | ||||||
|       ))} |  | ||||||
|     </> |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  | @ -2,16 +2,16 @@ import domtoimage from "dom-to-image"; // https://github.com/tsayen/dom-to-image | ||||||
| import { useEffect, useRef, useState } from "react"; | import { useEffect, useRef, useState } from "react"; | ||||||
| import { CopyToClipboard } from "react-copy-to-clipboard"; | import { CopyToClipboard } from "react-copy-to-clipboard"; | ||||||
| 
 | 
 | ||||||
| import { FrontendForecast } from "../platforms"; | import { FrontendQuestion } from "../platforms"; | ||||||
| import { uploadToImgur } from "../worker/uploadToImgur"; | import { uploadToImgur } from "../worker/uploadToImgur"; | ||||||
| import { DisplayForecast } from "./DisplayForecast"; | import { DisplayQuestion } from "./DisplayQuestion"; | ||||||
| 
 | 
 | ||||||
| function displayOneForecastInner(result: FrontendForecast, containerRef) { | function displayOneQuestionInner(result: FrontendQuestion, containerRef) { | ||||||
|   return ( |   return ( | ||||||
|     <div ref={containerRef}> |     <div ref={containerRef}> | ||||||
|       {result ? ( |       {result ? ( | ||||||
|         <DisplayForecast |         <DisplayQuestion | ||||||
|           forecast={result} |           question={result} | ||||||
|           showTimeStamp={true} |           showTimeStamp={true} | ||||||
|           expandFooterToFullWidth={true} |           expandFooterToFullWidth={true} | ||||||
|         /> |         /> | ||||||
|  | @ -168,10 +168,10 @@ let generateMetaculusSource = (result, hasDisplayBeenCaptured) => { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   result: FrontendForecast; |   result: FrontendQuestion; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const DisplayOneForecastForCapture: React.FC<Props> = ({ result }) => { | export const DisplayOneQuestionForCapture: React.FC<Props> = ({ result }) => { | ||||||
|   const [hasDisplayBeenCaptured, setHasDisplayBeenCaptured] = useState(false); |   const [hasDisplayBeenCaptured, setHasDisplayBeenCaptured] = useState(false); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|  | @ -226,7 +226,7 @@ export const DisplayOneForecastForCapture: React.FC<Props> = ({ result }) => { | ||||||
|   return ( |   return ( | ||||||
|     <div className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full justify-center"> |     <div className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full justify-center"> | ||||||
|       <div className="flex col-span-1 items-center justify-center"> |       <div className="flex col-span-1 items-center justify-center"> | ||||||
|         {displayOneForecastInner(result, containerRef)} |         {displayOneQuestionInner(result, containerRef)} | ||||||
|       </div> |       </div> | ||||||
|       <div className="flex col-span-1 items-center justify-center"> |       <div className="flex col-span-1 items-center justify-center"> | ||||||
|         {generateCaptureButton(result, onCaptureButtonClick)} |         {generateCaptureButton(result, onCaptureButtonClick)} | ||||||
|  | @ -243,7 +243,7 @@ interface Props { | ||||||
|   expandFooterToFullWidth: boolean; |   expandFooterToFullWidth: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const ForecastFooter: React.FC<Props> = ({ | export const QuestionFooter: React.FC<Props> = ({ | ||||||
|   stars, |   stars, | ||||||
|   platform, |   platform, | ||||||
|   platformLabel, |   platformLabel, | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| import { FaRegClipboard } from "react-icons/fa"; | import { FaRegClipboard } from "react-icons/fa"; | ||||||
| import ReactMarkdown from "react-markdown"; | import ReactMarkdown from "react-markdown"; | ||||||
| 
 | 
 | ||||||
| import { FrontendForecast } from "../../platforms"; | import { FrontendQuestion } from "../../platforms"; | ||||||
| import { Card } from "../Card"; | import { Card } from "../Card"; | ||||||
| import { ForecastFooter } from "./ForecastFooter"; | import { QuestionFooter } from "./QuestionFooter"; | ||||||
| 
 | 
 | ||||||
| const truncateText = (length: number, text: string): string => { | const truncateText = (length: number, text: string): string => { | ||||||
|   if (!text) { |   if (!text) { | ||||||
|  | @ -268,14 +268,14 @@ const LastUpdated: React.FC<{ timestamp: string }> = ({ timestamp }) => ( | ||||||
| // Main component
 | // Main component
 | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   forecast: FrontendForecast; |   question: FrontendQuestion; | ||||||
|   showTimeStamp: boolean; |   showTimeStamp: boolean; | ||||||
|   expandFooterToFullWidth: boolean; |   expandFooterToFullWidth: boolean; | ||||||
|   showIdToggle?: boolean; |   showIdToggle?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const DisplayForecast: React.FC<Props> = ({ | export const DisplayQuestion: React.FC<Props> = ({ | ||||||
|   forecast: { |   question: { | ||||||
|     id, |     id, | ||||||
|     title, |     title, | ||||||
|     url, |     url, | ||||||
|  | @ -372,7 +372,7 @@ export const DisplayForecast: React.FC<Props> = ({ | ||||||
|             <LastUpdated timestamp={timestamp} /> |             <LastUpdated timestamp={timestamp} /> | ||||||
|           </div> |           </div> | ||||||
|           <div className="w-full"> |           <div className="w-full"> | ||||||
|             <ForecastFooter |             <QuestionFooter | ||||||
|               stars={qualityindicators.stars} |               stars={qualityindicators.stars} | ||||||
|               platform={platform} |               platform={platform} | ||||||
|               platformLabel={platformLabel || platform} // author || platformLabel,
 |               platformLabel={platformLabel || platform} // author || platformLabel,
 | ||||||
							
								
								
									
										33
									
								
								src/web/display/DisplayQuestions.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/web/display/DisplayQuestions.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | import React from "react"; | ||||||
|  | 
 | ||||||
|  | import { FrontendQuestion } from "../platforms"; | ||||||
|  | import { DisplayQuestion } from "./DisplayQuestion"; | ||||||
|  | 
 | ||||||
|  | interface Props { | ||||||
|  |   results: FrontendQuestion[]; | ||||||
|  |   numDisplay: number; | ||||||
|  |   showIdToggle: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const DisplayQuestions: React.FC<Props> = ({ | ||||||
|  |   results, | ||||||
|  |   numDisplay, | ||||||
|  |   showIdToggle, | ||||||
|  | }) => { | ||||||
|  |   if (!results) { | ||||||
|  |     return <></>; | ||||||
|  |   } | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       {results.slice(0, numDisplay).map((result) => ( | ||||||
|  |         <DisplayQuestion | ||||||
|  |           key={result.id} | ||||||
|  |           question={result} | ||||||
|  |           showTimeStamp={false} | ||||||
|  |           expandFooterToFullWidth={false} | ||||||
|  |           showIdToggle={showIdToggle} | ||||||
|  |         /> | ||||||
|  |       ))} | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| import { DisplayForecasts } from "./DisplayForecasts"; | import { DisplayOneQuestionForCapture } from "./DisplayOneQuestionForCapture"; | ||||||
| import { DisplayOneForecastForCapture } from "./DisplayOneForecastForCapture"; | import { DisplayQuestions } from "./DisplayQuestions"; | ||||||
| 
 | 
 | ||||||
| export function displayForecastsWrapperForSearch({ | export function displayQuestionsWrapperForSearch({ | ||||||
|   results, |   results, | ||||||
|   numDisplay, |   numDisplay, | ||||||
|   showIdToggle, |   showIdToggle, | ||||||
| }) { | }) { | ||||||
|   return ( |   return ( | ||||||
|     <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> |     <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | ||||||
|       <DisplayForecasts |       <DisplayQuestions | ||||||
|         results={results || []} |         results={results || []} | ||||||
|         numDisplay={numDisplay} |         numDisplay={numDisplay} | ||||||
|         showIdToggle={showIdToggle} |         showIdToggle={showIdToggle} | ||||||
|  | @ -17,13 +17,13 @@ export function displayForecastsWrapperForSearch({ | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function displayForecastsWrapperForCapture({ | export function displayQuestionsWrapperForCapture({ | ||||||
|   results, |   results, | ||||||
|   whichResultToDisplayAndCapture, |   whichResultToDisplayAndCapture, | ||||||
| }) { | }) { | ||||||
|   return ( |   return ( | ||||||
|     <div className="grid grid-cols-1 w-full justify-center"> |     <div className="grid grid-cols-1 w-full justify-center"> | ||||||
|       <DisplayOneForecastForCapture |       <DisplayOneQuestionForCapture | ||||||
|         result={results[whichResultToDisplayAndCapture]} |         result={results[whichResultToDisplayAndCapture]} | ||||||
|       /> |       /> | ||||||
|     </div> |     </div> | ||||||
|  | @ -1,20 +1,20 @@ | ||||||
| import { Forecast, PlatformConfig } from "../backend/platforms"; | import { PlatformConfig, Question } from "../backend/platforms"; | ||||||
| 
 | 
 | ||||||
| export type FrontendForecast = Forecast & { | export type FrontendQuestion = Question & { | ||||||
|   platformLabel: string; |   platformLabel: string; | ||||||
|   visualization?: any; |   visualization?: any; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // ok on client side
 | // ok on client side
 | ||||||
| export const addLabelsToForecasts = ( | export const addLabelsToQuestions = ( | ||||||
|   forecasts: Forecast[], |   questions: Question[], | ||||||
|   platformsConfig: PlatformConfig[] |   platformsConfig: PlatformConfig[] | ||||||
| ): FrontendForecast[] => { | ): FrontendQuestion[] => { | ||||||
|   const platformNameToLabel = Object.fromEntries( |   const platformNameToLabel = Object.fromEntries( | ||||||
|     platformsConfig.map((platform) => [platform.name, platform.label]) |     platformsConfig.map((platform) => [platform.name, platform.label]) | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   return forecasts.map((result) => ({ |   return questions.map((result) => ({ | ||||||
|     ...result, |     ...result, | ||||||
|     platformLabel: platformNameToLabel[result.platform] || result.platform, |     platformLabel: platformNameToLabel[result.platform] || result.platform, | ||||||
|   })); |   })); | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import { MultiSelectPlatform } from "../display/MultiSelectPlatform"; | ||||||
| import { QueryForm } from "../display/QueryForm"; | import { QueryForm } from "../display/QueryForm"; | ||||||
| import { SliderElement } from "../display/SliderElement"; | import { SliderElement } from "../display/SliderElement"; | ||||||
| import { useNoInitialEffect } from "../hooks"; | import { useNoInitialEffect } from "../hooks"; | ||||||
| import { FrontendForecast } from "../platforms"; | import { FrontendQuestion } from "../platforms"; | ||||||
| import searchAccordingToQueryData from "../worker/searchAccordingToQueryData"; | import searchAccordingToQueryData from "../worker/searchAccordingToQueryData"; | ||||||
| import { Props as AnySearchPageProps, QueryParameters } from "./anySearchPage"; | import { Props as AnySearchPageProps, QueryParameters } from "./anySearchPage"; | ||||||
| 
 | 
 | ||||||
|  | @ -16,8 +16,8 @@ interface Props extends AnySearchPageProps { | ||||||
|   hasAdvancedOptions: boolean; |   hasAdvancedOptions: boolean; | ||||||
|   placeholder: string; |   placeholder: string; | ||||||
|   displaySeeMoreHint: boolean; |   displaySeeMoreHint: boolean; | ||||||
|   displayForecastsWrapper: (opts: { |   displayQuestionsWrapper: (opts: { | ||||||
|     results: FrontendForecast[]; |     results: FrontendQuestion[]; | ||||||
|     numDisplay: number; |     numDisplay: number; | ||||||
|     whichResultToDisplayAndCapture: number; |     whichResultToDisplayAndCapture: number; | ||||||
|     showIdToggle: boolean; |     showIdToggle: boolean; | ||||||
|  | @ -38,7 +38,7 @@ const CommonDisplay: React.FC<Props> = ({ | ||||||
|   hasAdvancedOptions, |   hasAdvancedOptions, | ||||||
|   placeholder, |   placeholder, | ||||||
|   displaySeeMoreHint, |   displaySeeMoreHint, | ||||||
|   displayForecastsWrapper, |   displayQuestionsWrapper, | ||||||
| }) => { | }) => { | ||||||
|   const router = useRouter(); |   const router = useRouter(); | ||||||
|   /* States */ |   /* States */ | ||||||
|  | @ -68,7 +68,7 @@ const CommonDisplay: React.FC<Props> = ({ | ||||||
| 
 | 
 | ||||||
|     const filterManually = ( |     const filterManually = ( | ||||||
|       queryData: QueryParameters, |       queryData: QueryParameters, | ||||||
|       results: FrontendForecast[] |       results: FrontendQuestion[] | ||||||
|     ) => { |     ) => { | ||||||
|       if ( |       if ( | ||||||
|         queryData.forecastingPlatforms && |         queryData.forecastingPlatforms && | ||||||
|  | @ -99,13 +99,13 @@ const CommonDisplay: React.FC<Props> = ({ | ||||||
|     setResults(results); |     setResults(results); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // I don't want the function which display forecasts (displayForecasts) to change with a change in queryParameters. But I want it to have access to the queryParameters, and in particular access to queryParameters.numDisplay. Hence why this function lives inside Home.
 |   // I don't want the component which display questions (DisplayQuestions) to change with a change in queryParameters. But I want it to have access to the queryParameters, and in particular access to queryParameters.numDisplay. Hence why this function lives inside Home.
 | ||||||
|   const getInfoToDisplayForecastsFunction = () => { |   const getInfoToDisplayQuestionsFunction = () => { | ||||||
|     const numDisplayRounded = |     const numDisplayRounded = | ||||||
|       numDisplay % 3 != 0 |       numDisplay % 3 != 0 | ||||||
|         ? numDisplay + (3 - (Math.round(numDisplay) % 3)) |         ? numDisplay + (3 - (Math.round(numDisplay) % 3)) | ||||||
|         : numDisplay; |         : numDisplay; | ||||||
|     return displayForecastsWrapper({ |     return displayQuestionsWrapper({ | ||||||
|       results, |       results, | ||||||
|       numDisplay: numDisplayRounded, |       numDisplay: numDisplayRounded, | ||||||
|       whichResultToDisplayAndCapture, |       whichResultToDisplayAndCapture, | ||||||
|  | @ -307,7 +307,7 @@ const CommonDisplay: React.FC<Props> = ({ | ||||||
|         </div> |         </div> | ||||||
|       ) : null} |       ) : null} | ||||||
| 
 | 
 | ||||||
|       <div>{getInfoToDisplayForecastsFunction()}</div> |       <div>{getInfoToDisplayQuestionsFunction()}</div> | ||||||
| 
 | 
 | ||||||
|       {displaySeeMoreHint && |       {displaySeeMoreHint && | ||||||
|       (!results || (results.length != 0 && numDisplay < results.length)) ? ( |       (!results || (results.length != 0 && numDisplay < results.length)) ? ( | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ import { GetServerSideProps } from "next"; | ||||||
| 
 | 
 | ||||||
| import { getFrontpage } from "../../backend/frontpage"; | import { getFrontpage } from "../../backend/frontpage"; | ||||||
| import { getPlatformsConfig, PlatformConfig, platforms } from "../../backend/platforms"; | import { getPlatformsConfig, PlatformConfig, platforms } from "../../backend/platforms"; | ||||||
| import { addLabelsToForecasts, FrontendForecast } from "../platforms"; | import { addLabelsToQuestions, FrontendQuestion } from "../platforms"; | ||||||
| import searchAccordingToQueryData from "../worker/searchAccordingToQueryData"; | import searchAccordingToQueryData from "../worker/searchAccordingToQueryData"; | ||||||
| 
 | 
 | ||||||
| /* Common code for / and /capture */ | /* Common code for / and /capture */ | ||||||
|  | @ -15,8 +15,8 @@ export interface QueryParameters { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface Props { | export interface Props { | ||||||
|   defaultResults: FrontendForecast[]; |   defaultResults: FrontendQuestion[]; | ||||||
|   initialResults: FrontendForecast[]; |   initialResults: FrontendQuestion[]; | ||||||
|   initialQueryParameters: QueryParameters; |   initialQueryParameters: QueryParameters; | ||||||
|   defaultQueryParameters: QueryParameters; |   defaultQueryParameters: QueryParameters; | ||||||
|   initialNumDisplay: number; |   initialNumDisplay: number; | ||||||
|  | @ -61,7 +61,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | ||||||
|   const defaultNumDisplay = 21; |   const defaultNumDisplay = 21; | ||||||
|   const initialNumDisplay = Number(urlQuery.numDisplay) || defaultNumDisplay; |   const initialNumDisplay = Number(urlQuery.numDisplay) || defaultNumDisplay; | ||||||
| 
 | 
 | ||||||
|   const defaultResults = addLabelsToForecasts( |   const defaultResults = addLabelsToQuestions( | ||||||
|     await getFrontpage(), |     await getFrontpage(), | ||||||
|     platformsConfig |     platformsConfig | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  | @ -1,25 +1,25 @@ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| import { DashboardItem } from "../../backend/dashboards"; | import { DashboardItem } from "../../backend/dashboards"; | ||||||
| import { Forecast, getPlatformsConfig } from "../../backend/platforms"; | import { getPlatformsConfig, Question } from "../../backend/platforms"; | ||||||
| import { addLabelsToForecasts, FrontendForecast } from "../platforms"; | import { addLabelsToQuestions, FrontendQuestion } from "../platforms"; | ||||||
| 
 | 
 | ||||||
| export async function getDashboardForecastsByDashboardId({ | export async function getDashboardQuestionsByDashboardId({ | ||||||
|   dashboardId, |   dashboardId, | ||||||
|   basePath, |   basePath, | ||||||
| }: { | }: { | ||||||
|   dashboardId: string; |   dashboardId: string; | ||||||
|   basePath?: string; |   basePath?: string; | ||||||
| }): Promise<{ | }): Promise<{ | ||||||
|   dashboardForecasts: FrontendForecast[]; |   dashboardQuestions: FrontendQuestion[]; | ||||||
|   dashboardItem: DashboardItem; |   dashboardItem: DashboardItem; | ||||||
| }> { | }> { | ||||||
|   console.log("getDashboardForecastsByDashboardId: "); |   console.log("getDashboardQuestionsByDashboardId: "); | ||||||
|   if (typeof window === undefined && !basePath) { |   if (typeof window === undefined && !basePath) { | ||||||
|     throw new Error("`basePath` option is required on server side"); |     throw new Error("`basePath` option is required on server side"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   let dashboardForecasts: Forecast[] = []; |   let dashboardQuestions: Question[] = []; | ||||||
|   let dashboardItem: DashboardItem | null = null; |   let dashboardItem: DashboardItem | null = null; | ||||||
|   try { |   try { | ||||||
|     let { data } = await axios({ |     let { data } = await axios({ | ||||||
|  | @ -31,18 +31,18 @@ export async function getDashboardForecastsByDashboardId({ | ||||||
|     }); |     }); | ||||||
|     console.log(data); |     console.log(data); | ||||||
| 
 | 
 | ||||||
|     dashboardForecasts = data.dashboardContents; |     dashboardQuestions = data.dashboardContents; | ||||||
|     dashboardItem = data.dashboardItem as DashboardItem; |     dashboardItem = data.dashboardItem as DashboardItem; | ||||||
|   } catch (error) { |   } catch (error) { | ||||||
|     console.log(error); |     console.log(error); | ||||||
|   } finally { |   } finally { | ||||||
|     const labeledDashboardForecasts = addLabelsToForecasts( |     const labeledDashboardQuestions = addLabelsToQuestions( | ||||||
|       dashboardForecasts, |       dashboardQuestions, | ||||||
|       getPlatformsConfig({ withGuesstimate: false }) |       getPlatformsConfig({ withGuesstimate: false }) | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|       dashboardForecasts: labeledDashboardForecasts, |       dashboardQuestions: labeledDashboardQuestions, | ||||||
|       dashboardItem, |       dashboardItem, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import { FrontendForecast } from "../platforms"; | import { FrontendQuestion } from "../platforms"; | ||||||
| import { QueryParameters } from "../search/anySearchPage"; | import { QueryParameters } from "../search/anySearchPage"; | ||||||
| import searchGuesstimate from "./searchGuesstimate"; | import searchGuesstimate from "./searchGuesstimate"; | ||||||
| import searchWithAlgolia from "./searchWithAlgolia"; | import searchWithAlgolia from "./searchWithAlgolia"; | ||||||
|  | @ -6,8 +6,8 @@ import searchWithAlgolia from "./searchWithAlgolia"; | ||||||
| export default async function searchAccordingToQueryData( | export default async function searchAccordingToQueryData( | ||||||
|   queryData: QueryParameters, |   queryData: QueryParameters, | ||||||
|   limit: number |   limit: number | ||||||
| ): Promise<FrontendForecast[]> { | ): Promise<FrontendQuestion[]> { | ||||||
|   let results: FrontendForecast[] = []; |   let results: FrontendQuestion[] = []; | ||||||
| 
 | 
 | ||||||
|   try { |   try { | ||||||
|     // defs
 |     // defs
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| /* Imports */ | /* Imports */ | ||||||
| import axios from "axios"; | import axios from "axios"; | ||||||
| 
 | 
 | ||||||
| import { FrontendForecast } from "../platforms"; | import { FrontendQuestion } from "../platforms"; | ||||||
| 
 | 
 | ||||||
| /* Definitions */ | /* Definitions */ | ||||||
| let urlEndPoint = | let urlEndPoint = | ||||||
|  | @ -11,7 +11,7 @@ let urlEndPoint = | ||||||
| 
 | 
 | ||||||
| export default async function searchGuesstimate( | export default async function searchGuesstimate( | ||||||
|   query |   query | ||||||
| ): Promise<FrontendForecast[]> { | ): Promise<FrontendQuestion[]> { | ||||||
|   let response = await axios({ |   let response = await axios({ | ||||||
|     url: urlEndPoint, |     url: urlEndPoint, | ||||||
|     // credentials: "omit",
 |     // credentials: "omit",
 | ||||||
|  | @ -31,7 +31,7 @@ export default async function searchGuesstimate( | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   const models: any[] = response.data.hits; |   const models: any[] = response.data.hits; | ||||||
|   const mappedModels: FrontendForecast[] = models.map((model, index) => { |   const mappedModels: FrontendQuestion[] = models.map((model, index) => { | ||||||
|     let description = model.description |     let description = model.description | ||||||
|       ? model.description.replace(/\n/g, " ").replace(/  /g, " ") |       ? model.description.replace(/\n/g, " ").replace(/  /g, " ") | ||||||
|       : ""; |       : ""; | ||||||
|  | @ -57,7 +57,7 @@ export default async function searchGuesstimate( | ||||||
| 
 | 
 | ||||||
|   // filter for duplicates. Surprisingly common.
 |   // filter for duplicates. Surprisingly common.
 | ||||||
|   let uniqueTitles = []; |   let uniqueTitles = []; | ||||||
|   let uniqueModels: FrontendForecast[] = []; |   let uniqueModels: FrontendQuestion[] = []; | ||||||
|   for (let model of mappedModels) { |   for (let model of mappedModels) { | ||||||
|     if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) { |     if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) { | ||||||
|       uniqueModels.push(model); |       uniqueModels.push(model); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import algoliasearch from "algoliasearch"; | import algoliasearch from "algoliasearch"; | ||||||
| 
 | 
 | ||||||
| import { FrontendForecast } from "../platforms"; | import { FrontendQuestion } from "../platforms"; | ||||||
| 
 | 
 | ||||||
| const client = algoliasearch( | const client = algoliasearch( | ||||||
|   process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, |   process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, | ||||||
|  | @ -82,8 +82,8 @@ export default async function searchWithAlgolia({ | ||||||
|   starsThreshold, |   starsThreshold, | ||||||
|   filterByPlatforms, |   filterByPlatforms, | ||||||
|   forecastsThreshold, |   forecastsThreshold, | ||||||
| }): Promise<FrontendForecast[]> { | }): Promise<FrontendQuestion[]> { | ||||||
|   let response = await index.search<FrontendForecast>(queryString, { |   let response = await index.search<FrontendQuestion>(queryString, { | ||||||
|     hitsPerPage, |     hitsPerPage, | ||||||
|     filters: buildFilter({ |     filters: buildFilter({ | ||||||
|       starsThreshold, |       starsThreshold, | ||||||
|  | @ -93,7 +93,7 @@ export default async function searchWithAlgolia({ | ||||||
|     //facetFilters: buildFacetFilter({filterByPlatforms}),
 |     //facetFilters: buildFacetFilter({filterByPlatforms}),
 | ||||||
|     getRankingInfo: true, |     getRankingInfo: true, | ||||||
|   }); |   }); | ||||||
|   let results: FrontendForecast[] = response.hits; |   let results: FrontendQuestion[] = response.hits; | ||||||
| 
 | 
 | ||||||
|   let recursionError = ["metaforecast", "metaforecasts", "metaforecasting"]; |   let recursionError = ["metaforecast", "metaforecasts", "metaforecasting"]; | ||||||
|   if ( |   if ( | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user