refactor: rename forecast -> question (see #40)

This commit is contained in:
Vyacheslav Matyukhin 2022-04-13 00:47:06 +03:00
parent 2ee82cdd15
commit 6543a729f3
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
31 changed files with 190 additions and 196 deletions

View File

@ -13,7 +13,7 @@
# React
- 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
- it's better for refactoring
- and it plays well with `React.FC` typing

View File

@ -1,13 +1,13 @@
import { Pool, PoolClient } from "pg";
import { Forecast } from "../platforms";
import { Question } from "../platforms";
import { hash } from "../utils/hash";
import { measureTime } from "../utils/measureTime";
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 */
const databaseURL = process.env.DIGITALOCEAN_POSTGRES;
@ -51,11 +51,11 @@ export async function pgBulkInsert({
tableName,
client,
}: {
data: Forecast[];
data: Question[];
tableName: string;
client: PoolClient;
}) {
if (!forecastTableNames.includes(tableName)) {
if (!questionTableNames.includes(tableName)) {
throw Error(
`Table ${tableName} not in whitelist; stopping to avoid tricky sql injections`
);
@ -171,11 +171,11 @@ export async function pgUpsert({
tableName,
replacePlatform,
}: {
contents: Forecast[];
contents: Question[];
tableName: string;
replacePlatform?: string;
}) {
if (!forecastTableNames.includes(tableName)) {
if (!questionTableNames.includes(tableName)) {
throw Error(
`Table ${tableName} not in whitelist; stopping to avoid tricky sql injections`
);

View File

@ -1,7 +1,7 @@
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(
"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;
}
export async function getFrontpageFull(): Promise<Forecast[]> {
export async function getFrontpageFull(): Promise<Question[]> {
const res = await pool.query(
"SELECT frontpage_full FROM frontpage ORDER BY id DESC LIMIT 1"
);

View File

@ -3,7 +3,7 @@ import axios from "axios";
import https from "https";
import { calculateStars } from "../utils/stars";
import { Forecast, Platform } from "./";
import { Platform, Question } 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: Forecast[] = predictions.map((prediction) => {
let results: Question[] = predictions.map((prediction) => {
/* if(Math.floor(Math.random() * 10) % 20 ==0){
console.log(JSON.stringify(prediction, null, 4))
} */

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { calculateStars } from "../utils/stars";
import { Forecast, Platform } from "./";
import { Platform, Question } 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: Forecast[] = [];
let results: Question[] = [];
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: Forecast = {
let eventObject: Question = {
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}`,

View File

@ -16,7 +16,7 @@ import { smarkets } from "./smarkets";
import { wildeford } from "./wildeford";
import { xrisk } from "./xrisk";
export interface Forecast {
export interface Question {
id: string;
// "fantasyscotus-580"
@ -63,8 +63,8 @@ export interface Forecast {
extra?: any;
}
// fetcher should return null if platform failed to fetch forecasts for some reason
export type PlatformFetcher = () => Promise<Forecast[] | null>;
// fetcher should return null if platform failed to fetch questions for some reason
export type PlatformFetcher = () => Promise<Question[] | null>;
export interface Platform {
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:
// interface FetchOptions {
// 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>;

View File

@ -1,12 +1,11 @@
/* Imports */
import axios from "axios";
import { Tabletojson } from "tabletojson";
import { applyIfSecretExists } from "../utils/getSecrets";
import { measureTime } from "../utils/measureTime";
import { calculateStars } from "../utils/stars";
import toMarkdown from "../utils/toMarkdown";
import { Forecast, Platform } from "./";
import { Platform, Question } from "./";
/* Definitions */
const platformName = "infer";
@ -78,12 +77,12 @@ async function fetchStats(questionUrl, cookie) {
let comments_count = firstEmbeddedJson.question.comments_count;
let numforecasters = firstEmbeddedJson.question.predictors_count;
let numforecasts = firstEmbeddedJson.question.prediction_sets_count;
let forecastType = firstEmbeddedJson.question.type;
let questionType = firstEmbeddedJson.question.type;
if (
forecastType.includes("Binary") ||
forecastType.includes("NonExclusiveOpinionPoolQuestion") ||
forecastType.includes("Forecast::Question") ||
!forecastType.includes("Forecast::MultiTimePeriodQuestion")
questionType.includes("Binary") ||
questionType.includes("NonExclusiveOpinionPoolQuestion") ||
questionType.includes("Forecast::Question") ||
!questionType.includes("Forecast::MultiTimePeriodQuestion")
) {
options = firstEmbeddedJson.question.answers.map((answer) => ({
name: answer.name,
@ -148,7 +147,7 @@ function sleep(ms) {
async function infer_inner(cookie: string) {
let i = 1;
let response = await fetchPage(i, cookie);
let results: Forecast[] = [];
let results: Question[] = [];
await measureTime(async () => {
// 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 questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0];
let id = `${platformName}-${questionNum}`;
let question: Forecast = {
let question: Question = {
id: id,
title: title,
description: moreinfo.description,

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { calculateStars } from "../utils/stars";
import { Forecast, Platform } from "./";
import { Platform, Question } from "./";
/* Definitions */
const platformName = "manifold";
@ -23,7 +23,7 @@ async function fetchData() {
return response;
}
function showStatistics(results: Forecast[]) {
function showStatistics(results: Question[]) {
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: Forecast[]) {
}
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 probability = prediction.probability;
let options = [
@ -59,7 +59,7 @@ async function processPredictions(predictions) {
type: "PROBABILITY",
},
];
const result: Forecast = {
const result: Question = {
id: id,
title: prediction.question,
url: prediction.url,

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { calculateStars } from "../utils/stars";
import { Forecast, Platform } from "./";
import { Platform, Question } from "./";
/* Definitions */
const platformName = "polymarket";
@ -68,7 +68,7 @@ export const polymarket: Platform = {
label: "PolyMarket",
color: "#00314e",
async fetcher() {
let results: Forecast[] = [];
let results: Question[] = [];
let webpageEndpointData = await fetchAllContractInfo();
for (let marketInfo of webpageEndpointData) {
let address = marketInfo.marketMakerAddress;
@ -102,7 +102,7 @@ export const polymarket: Platform = {
});
}
let result: Forecast = {
let result: Question = {
id: id,
title: marketInfo.question,
url: "https://polymarket.com/market/" + marketInfo.slug,

View File

@ -3,7 +3,7 @@ import { JSDOM } from "jsdom";
import { calculateStars } from "../utils/stars";
import toMarkdown from "../utils/toMarkdown";
import { Forecast, Platform } from "./";
import { Platform, Question } from "./";
const platformName = "rootclaim";
const jsonEndpoint =
@ -50,7 +50,7 @@ export const rootclaim: Platform = {
color: "#0d1624",
async fetcher() {
const claims = await fetchAllRootclaims();
const results: Forecast[] = [];
const results: Question[] = [];
for (const claim of claims) {
const id = `${platformName}-${claim.slug.toLowerCase()}`;
@ -71,7 +71,7 @@ export const rootclaim: Platform = {
const description = await fetchDescription(url, claim.isclaim);
let obj: Forecast = {
let obj: Question = {
id,
title: toMarkdown(claim.question).replace("\n", ""),
url,

View File

@ -1,7 +1,7 @@
import axios from "axios";
import { calculateStars } from "../utils/stars";
import { Forecast, Platform } from "./";
import { Platform, Question } from "./";
/* Definitions */
const platformName = "smarkets";
@ -159,7 +159,7 @@ export const smarkets: Platform = {
name = name+ (contractName=="Yes"?'':` (${contracts["contracts"][0].name})`)
}
*/
let result: Forecast = {
let result: Question = {
id: id,
title: name,
url: "https://smarkets.com/event/" + market.event_id + market.slug,

View File

@ -8,14 +8,14 @@ import { pgRead } from "../../database/pg-wrapper";
/* Utilities */
/* Support functions */
let getQualityIndicators = (forecast) =>
Object.entries(forecast.qualityindicators)
const getQualityIndicators = (question) =>
Object.entries(question.qualityindicators)
.map((entry) => `${entry[0]}: ${entry[1]}`)
.join("; ");
/* Body */
let main = async () => {
const main = async () => {
let highQualityPlatforms = [
"CSET-foretell",
"Foretold",
@ -24,21 +24,21 @@ let main = async () => {
"PredictIt",
"Rootclaim",
];
let json = await pgRead({ tableName: "questions" });
const json = await pgRead({ tableName: "questions" });
console.log(json.length);
//let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))]
//console.log(uniquePlatforms)
let forecastsFromGoodPlatforms = json.filter((forecast) =>
highQualityPlatforms.includes(forecast.platform)
const questionsFromGoodPlatforms = json.filter((question) =>
highQualityPlatforms.includes(question.platform)
);
let tsv =
const tsv =
"index\ttitle\turl\tqualityindicators\n" +
forecastsFromGoodPlatforms
.map((forecast, index) => {
let row = `${index}\t${forecast.title}\t${
forecast.url
}\t${getQualityIndicators(forecast)}`;
questionsFromGoodPlatforms
.map((question, index) => {
let row = `${index}\t${question.title}\t${
question.url
}\t${getQualityIndicators(question)}`;
console.log(row);
return row;
})

View File

@ -8,8 +8,8 @@ import { pgRead } from "../../database/pg-wrapper";
/* Utilities */
/* Support functions */
let getQualityIndicators = (forecast) =>
Object.entries(forecast.qualityindicators)
let getQualityIndicators = (question) =>
Object.entries(question.qualityindicators)
.map((entry) => `${entry[0]}: ${entry[1]}`)
.join("; ");
@ -28,22 +28,22 @@ let main = async () => {
let highQualityPlatforms = ["Metaculus"]; // ['CSET-foretell', 'Foretold', 'Good Judgment Open', 'Metaculus', 'PredictIt', 'Rootclaim']
let json = await pgRead({ tableName: "questions" });
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)
let forecastsFromGoodPlatforms = json.filter((forecast) =>
highQualityPlatforms.includes(forecast.platform)
let questionsFromGoodPlatforms = json.filter((question) =>
highQualityPlatforms.includes(question.platform)
);
let forecastsFromGoodPlatformsShuffled = shuffleArray(
forecastsFromGoodPlatforms
let questionsFromGoodPlatformsShuffled = shuffleArray(
questionsFromGoodPlatforms
);
let tsv =
"index\ttitle\turl\tqualityindicators\n" +
forecastsFromGoodPlatforms
.map((forecast, index) => {
let row = `${index}\t${forecast.title}\t${
forecast.url
}\t${getQualityIndicators(forecast)}`;
questionsFromGoodPlatforms
.map((question, index) => {
let row = `${index}\t${question.title}\t${
question.url
}\t${getQualityIndicators(question)}`;
console.log(row);
return row;
})

View File

@ -1,7 +1,7 @@
import { NextPage } from "next";
import React from "react";
import { displayForecastsWrapperForCapture } from "../web/display/displayForecastsWrappers";
import { displayQuestionsWrapperForCapture } from "../web/display/displayQuestionsWrappers";
import { Layout } from "../web/display/Layout";
import { Props } from "../web/search/anySearchPage";
import CommonDisplay from "../web/search/CommonDisplay";
@ -18,7 +18,7 @@ const CapturePage: NextPage<Props> = (props) => {
hasAdvancedOptions={false}
placeholder={"Get best title match..."}
displaySeeMoreHint={false}
displayForecastsWrapper={displayForecastsWrapperForCapture}
displayQuestionsWrapper={displayQuestionsWrapperForCapture}
/>
</Layout>
);

View File

@ -2,13 +2,13 @@ import { GetServerSideProps, NextPage } from "next";
import Error from "next/error";
import { DashboardItem } from "../../../backend/dashboards";
import { DisplayForecasts } from "../../../web/display/DisplayForecasts";
import { FrontendForecast } from "../../../web/platforms";
import { getDashboardForecastsByDashboardId } from "../../../web/worker/getDashboardForecasts";
import { DisplayQuestions } from "../../../web/display/DisplayQuestions";
import { FrontendQuestion } from "../../../web/platforms";
import { reqToBasePath } from "../../../web/utils";
import { getDashboardQuestionsByDashboardId } from "../../../web/worker/getDashboardQuestions";
interface Props {
dashboardForecasts: FrontendForecast[];
dashboardQuestions: FrontendQuestion[];
dashboardItem: DashboardItem;
numCols?: number;
}
@ -19,8 +19,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
const dashboardId = context.query.id as string;
const numCols = Number(context.query.numCols);
const { dashboardItem, dashboardForecasts } =
await getDashboardForecastsByDashboardId({
const { dashboardItem, dashboardQuestions } =
await getDashboardQuestionsByDashboardId({
dashboardId,
basePath: reqToBasePath(context.req), // required on server side to find the API endpoint
});
@ -31,7 +31,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
return {
props: {
dashboardForecasts,
dashboardQuestions,
dashboardItem,
numCols: !numCols ? null : numCols < 5 ? numCols : 4,
},
@ -39,7 +39,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
};
const EmbedDashboardPage: NextPage<Props> = ({
dashboardForecasts,
dashboardQuestions,
dashboardItem,
numCols,
}) => {
@ -57,9 +57,9 @@ const EmbedDashboardPage: NextPage<Props> = ({
numCols || 3
} gap-4 mb-6`}
>
<DisplayForecasts
results={dashboardForecasts}
numDisplay={dashboardForecasts.length}
<DisplayQuestions
results={dashboardQuestions}
numDisplay={dashboardQuestions.length}
showIdToggle={false}
/>
</div>

View File

@ -3,16 +3,16 @@ import Error from "next/error";
import Link from "next/link";
import { DashboardItem } from "../../../backend/dashboards";
import { DisplayForecasts } from "../../../web/display/DisplayForecasts";
import { DisplayQuestions } from "../../../web/display/DisplayQuestions";
import { InfoBox } from "../../../web/display/InfoBox";
import { Layout } from "../../../web/display/Layout";
import { LineHeader } from "../../../web/display/LineHeader";
import { FrontendForecast } from "../../../web/platforms";
import { FrontendQuestion } from "../../../web/platforms";
import { reqToBasePath } from "../../../web/utils";
import { getDashboardForecastsByDashboardId } from "../../../web/worker/getDashboardForecasts";
import { getDashboardQuestionsByDashboardId } from "../../../web/worker/getDashboardQuestions";
interface Props {
dashboardForecasts: FrontendForecast[];
dashboardQuestions: FrontendQuestion[];
dashboardItem: DashboardItem;
}
@ -21,8 +21,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
) => {
const dashboardId = context.query.id as string;
const { dashboardForecasts, dashboardItem } =
await getDashboardForecastsByDashboardId({
const { dashboardQuestions, dashboardItem } =
await getDashboardQuestionsByDashboardId({
dashboardId,
basePath: reqToBasePath(context.req), // required on server side to find the API endpoint
});
@ -33,7 +33,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
return {
props: {
dashboardForecasts,
dashboardQuestions,
dashboardItem,
},
};
@ -78,7 +78,7 @@ const DashboardMetadata: React.FC<{ dashboardItem: DashboardItem }> = ({
/* Body */
const ViewDashboardPage: NextPage<Props> = ({
dashboardForecasts,
dashboardQuestions,
dashboardItem,
}) => {
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">
<DisplayForecasts
results={dashboardForecasts}
numDisplay={dashboardForecasts.length}
<DisplayQuestions
results={dashboardQuestions}
numDisplay={dashboardQuestions.length}
showIdToggle={false}
/>
</div>

View File

@ -1,7 +1,7 @@
import { NextPage } from "next";
import React from "react";
import { displayForecastsWrapperForSearch } from "../web/display/displayForecastsWrappers";
import { displayQuestionsWrapperForSearch } from "../web/display/displayQuestionsWrappers";
import { Layout } from "../web/display/Layout";
import { Props } from "../web/search/anySearchPage";
import CommonDisplay from "../web/search/CommonDisplay";
@ -18,7 +18,7 @@ const IndexPage: NextPage<Props> = (props) => {
hasAdvancedOptions={true}
placeholder={"Find forecasts about..."}
displaySeeMoreHint={true}
displayForecastsWrapper={displayForecastsWrapperForSearch}
displayQuestionsWrapper={displayQuestionsWrapperForSearch}
/>
</Layout>
);

View File

@ -4,12 +4,12 @@ import { GetServerSideProps, NextPage } from "next";
import React from "react";
import { platforms } from "../backend/platforms";
import { DisplayForecast } from "../web/display/DisplayForecast";
import { FrontendForecast } from "../web/platforms";
import { DisplayQuestion } from "../web/display/DisplayQuestion";
import { FrontendQuestion } from "../web/platforms";
import searchAccordingToQueryData from "../web/worker/searchAccordingToQueryData";
interface Props {
results: FrontendForecast[];
results: FrontendQuestion[];
}
export const getServerSideProps: GetServerSideProps<Props> = async (
@ -25,7 +25,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
...urlQuery,
};
let results: FrontendForecast[] = [];
let results: FrontendQuestion[] = [];
if (initialQueryParameters.query != "") {
results = await searchAccordingToQueryData(initialQueryParameters, 1);
}
@ -46,8 +46,8 @@ const SecretEmbedPage: NextPage<Props> = ({ results }) => {
<div>
<div id="secretEmbed">
{result ? (
<DisplayForecast
forecast={result}
<DisplayQuestion
question={result}
showTimeStamp={true}
expandFooterToFullWidth={true}
/>

View File

@ -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}
/>
))}
</>
);
};

View File

@ -2,16 +2,16 @@ import domtoimage from "dom-to-image"; // https://github.com/tsayen/dom-to-image
import { useEffect, useRef, useState } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { FrontendForecast } from "../platforms";
import { FrontendQuestion } from "../platforms";
import { uploadToImgur } from "../worker/uploadToImgur";
import { DisplayForecast } from "./DisplayForecast";
import { DisplayQuestion } from "./DisplayQuestion";
function displayOneForecastInner(result: FrontendForecast, containerRef) {
function displayOneQuestionInner(result: FrontendQuestion, containerRef) {
return (
<div ref={containerRef}>
{result ? (
<DisplayForecast
forecast={result}
<DisplayQuestion
question={result}
showTimeStamp={true}
expandFooterToFullWidth={true}
/>
@ -168,10 +168,10 @@ let generateMetaculusSource = (result, hasDisplayBeenCaptured) => {
};
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);
useEffect(() => {
@ -226,7 +226,7 @@ export const DisplayOneForecastForCapture: React.FC<Props> = ({ result }) => {
return (
<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">
{displayOneForecastInner(result, containerRef)}
{displayOneQuestionInner(result, containerRef)}
</div>
<div className="flex col-span-1 items-center justify-center">
{generateCaptureButton(result, onCaptureButtonClick)}

View File

@ -243,7 +243,7 @@ interface Props {
expandFooterToFullWidth: boolean;
}
export const ForecastFooter: React.FC<Props> = ({
export const QuestionFooter: React.FC<Props> = ({
stars,
platform,
platformLabel,

View File

@ -1,9 +1,9 @@
import { FaRegClipboard } from "react-icons/fa";
import ReactMarkdown from "react-markdown";
import { FrontendForecast } from "../../platforms";
import { FrontendQuestion } from "../../platforms";
import { Card } from "../Card";
import { ForecastFooter } from "./ForecastFooter";
import { QuestionFooter } from "./QuestionFooter";
const truncateText = (length: number, text: string): string => {
if (!text) {
@ -268,14 +268,14 @@ const LastUpdated: React.FC<{ timestamp: string }> = ({ timestamp }) => (
// Main component
interface Props {
forecast: FrontendForecast;
question: FrontendQuestion;
showTimeStamp: boolean;
expandFooterToFullWidth: boolean;
showIdToggle?: boolean;
}
export const DisplayForecast: React.FC<Props> = ({
forecast: {
export const DisplayQuestion: React.FC<Props> = ({
question: {
id,
title,
url,
@ -372,7 +372,7 @@ export const DisplayForecast: React.FC<Props> = ({
<LastUpdated timestamp={timestamp} />
</div>
<div className="w-full">
<ForecastFooter
<QuestionFooter
stars={qualityindicators.stars}
platform={platform}
platformLabel={platformLabel || platform} // author || platformLabel,

View 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}
/>
))}
</>
);
};

View File

@ -1,14 +1,14 @@
import { DisplayForecasts } from "./DisplayForecasts";
import { DisplayOneForecastForCapture } from "./DisplayOneForecastForCapture";
import { DisplayOneQuestionForCapture } from "./DisplayOneQuestionForCapture";
import { DisplayQuestions } from "./DisplayQuestions";
export function displayForecastsWrapperForSearch({
export function displayQuestionsWrapperForSearch({
results,
numDisplay,
showIdToggle,
}) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<DisplayForecasts
<DisplayQuestions
results={results || []}
numDisplay={numDisplay}
showIdToggle={showIdToggle}
@ -17,13 +17,13 @@ export function displayForecastsWrapperForSearch({
);
}
export function displayForecastsWrapperForCapture({
export function displayQuestionsWrapperForCapture({
results,
whichResultToDisplayAndCapture,
}) {
return (
<div className="grid grid-cols-1 w-full justify-center">
<DisplayOneForecastForCapture
<DisplayOneQuestionForCapture
result={results[whichResultToDisplayAndCapture]}
/>
</div>

View File

@ -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;
visualization?: any;
};
// ok on client side
export const addLabelsToForecasts = (
forecasts: Forecast[],
export const addLabelsToQuestions = (
questions: Question[],
platformsConfig: PlatformConfig[]
): FrontendForecast[] => {
): FrontendQuestion[] => {
const platformNameToLabel = Object.fromEntries(
platformsConfig.map((platform) => [platform.name, platform.label])
);
return forecasts.map((result) => ({
return questions.map((result) => ({
...result,
platformLabel: platformNameToLabel[result.platform] || result.platform,
}));

View File

@ -6,7 +6,7 @@ import { MultiSelectPlatform } from "../display/MultiSelectPlatform";
import { QueryForm } from "../display/QueryForm";
import { SliderElement } from "../display/SliderElement";
import { useNoInitialEffect } from "../hooks";
import { FrontendForecast } from "../platforms";
import { FrontendQuestion } from "../platforms";
import searchAccordingToQueryData from "../worker/searchAccordingToQueryData";
import { Props as AnySearchPageProps, QueryParameters } from "./anySearchPage";
@ -16,8 +16,8 @@ interface Props extends AnySearchPageProps {
hasAdvancedOptions: boolean;
placeholder: string;
displaySeeMoreHint: boolean;
displayForecastsWrapper: (opts: {
results: FrontendForecast[];
displayQuestionsWrapper: (opts: {
results: FrontendQuestion[];
numDisplay: number;
whichResultToDisplayAndCapture: number;
showIdToggle: boolean;
@ -38,7 +38,7 @@ const CommonDisplay: React.FC<Props> = ({
hasAdvancedOptions,
placeholder,
displaySeeMoreHint,
displayForecastsWrapper,
displayQuestionsWrapper,
}) => {
const router = useRouter();
/* States */
@ -68,7 +68,7 @@ const CommonDisplay: React.FC<Props> = ({
const filterManually = (
queryData: QueryParameters,
results: FrontendForecast[]
results: FrontendQuestion[]
) => {
if (
queryData.forecastingPlatforms &&
@ -99,13 +99,13 @@ const CommonDisplay: React.FC<Props> = ({
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.
const getInfoToDisplayForecastsFunction = () => {
// 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 getInfoToDisplayQuestionsFunction = () => {
const numDisplayRounded =
numDisplay % 3 != 0
? numDisplay + (3 - (Math.round(numDisplay) % 3))
: numDisplay;
return displayForecastsWrapper({
return displayQuestionsWrapper({
results,
numDisplay: numDisplayRounded,
whichResultToDisplayAndCapture,
@ -307,7 +307,7 @@ const CommonDisplay: React.FC<Props> = ({
</div>
) : null}
<div>{getInfoToDisplayForecastsFunction()}</div>
<div>{getInfoToDisplayQuestionsFunction()}</div>
{displaySeeMoreHint &&
(!results || (results.length != 0 && numDisplay < results.length)) ? (

View File

@ -2,7 +2,7 @@ import { GetServerSideProps } from "next";
import { getFrontpage } from "../../backend/frontpage";
import { getPlatformsConfig, PlatformConfig, platforms } from "../../backend/platforms";
import { addLabelsToForecasts, FrontendForecast } from "../platforms";
import { addLabelsToQuestions, FrontendQuestion } from "../platforms";
import searchAccordingToQueryData from "../worker/searchAccordingToQueryData";
/* Common code for / and /capture */
@ -15,8 +15,8 @@ export interface QueryParameters {
}
export interface Props {
defaultResults: FrontendForecast[];
initialResults: FrontendForecast[];
defaultResults: FrontendQuestion[];
initialResults: FrontendQuestion[];
initialQueryParameters: QueryParameters;
defaultQueryParameters: QueryParameters;
initialNumDisplay: number;
@ -61,7 +61,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
const defaultNumDisplay = 21;
const initialNumDisplay = Number(urlQuery.numDisplay) || defaultNumDisplay;
const defaultResults = addLabelsToForecasts(
const defaultResults = addLabelsToQuestions(
await getFrontpage(),
platformsConfig
);

View File

@ -1,25 +1,25 @@
import axios from "axios";
import { DashboardItem } from "../../backend/dashboards";
import { Forecast, getPlatformsConfig } from "../../backend/platforms";
import { addLabelsToForecasts, FrontendForecast } from "../platforms";
import { getPlatformsConfig, Question } from "../../backend/platforms";
import { addLabelsToQuestions, FrontendQuestion } from "../platforms";
export async function getDashboardForecastsByDashboardId({
export async function getDashboardQuestionsByDashboardId({
dashboardId,
basePath,
}: {
dashboardId: string;
basePath?: string;
}): Promise<{
dashboardForecasts: FrontendForecast[];
dashboardQuestions: FrontendQuestion[];
dashboardItem: DashboardItem;
}> {
console.log("getDashboardForecastsByDashboardId: ");
console.log("getDashboardQuestionsByDashboardId: ");
if (typeof window === undefined && !basePath) {
throw new Error("`basePath` option is required on server side");
}
let dashboardForecasts: Forecast[] = [];
let dashboardQuestions: Question[] = [];
let dashboardItem: DashboardItem | null = null;
try {
let { data } = await axios({
@ -31,18 +31,18 @@ export async function getDashboardForecastsByDashboardId({
});
console.log(data);
dashboardForecasts = data.dashboardContents;
dashboardQuestions = data.dashboardContents;
dashboardItem = data.dashboardItem as DashboardItem;
} catch (error) {
console.log(error);
} finally {
const labeledDashboardForecasts = addLabelsToForecasts(
dashboardForecasts,
const labeledDashboardQuestions = addLabelsToQuestions(
dashboardQuestions,
getPlatformsConfig({ withGuesstimate: false })
);
return {
dashboardForecasts: labeledDashboardForecasts,
dashboardQuestions: labeledDashboardQuestions,
dashboardItem,
};
}

View File

@ -1,4 +1,4 @@
import { FrontendForecast } from "../platforms";
import { FrontendQuestion } from "../platforms";
import { QueryParameters } from "../search/anySearchPage";
import searchGuesstimate from "./searchGuesstimate";
import searchWithAlgolia from "./searchWithAlgolia";
@ -6,8 +6,8 @@ import searchWithAlgolia from "./searchWithAlgolia";
export default async function searchAccordingToQueryData(
queryData: QueryParameters,
limit: number
): Promise<FrontendForecast[]> {
let results: FrontendForecast[] = [];
): Promise<FrontendQuestion[]> {
let results: FrontendQuestion[] = [];
try {
// defs

View File

@ -1,7 +1,7 @@
/* Imports */
import axios from "axios";
import { FrontendForecast } from "../platforms";
import { FrontendQuestion } from "../platforms";
/* Definitions */
let urlEndPoint =
@ -11,7 +11,7 @@ let urlEndPoint =
export default async function searchGuesstimate(
query
): Promise<FrontendForecast[]> {
): Promise<FrontendQuestion[]> {
let response = await axios({
url: urlEndPoint,
// credentials: "omit",
@ -31,7 +31,7 @@ export default async function searchGuesstimate(
});
const models: any[] = response.data.hits;
const mappedModels: FrontendForecast[] = models.map((model, index) => {
const mappedModels: FrontendQuestion[] = models.map((model, index) => {
let description = model.description
? model.description.replace(/\n/g, " ").replace(/ /g, " ")
: "";
@ -57,7 +57,7 @@ export default async function searchGuesstimate(
// filter for duplicates. Surprisingly common.
let uniqueTitles = [];
let uniqueModels: FrontendForecast[] = [];
let uniqueModels: FrontendQuestion[] = [];
for (let model of mappedModels) {
if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) {
uniqueModels.push(model);

View File

@ -1,6 +1,6 @@
import algoliasearch from "algoliasearch";
import { FrontendForecast } from "../platforms";
import { FrontendQuestion } from "../platforms";
const client = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
@ -82,8 +82,8 @@ export default async function searchWithAlgolia({
starsThreshold,
filterByPlatforms,
forecastsThreshold,
}): Promise<FrontendForecast[]> {
let response = await index.search<FrontendForecast>(queryString, {
}): Promise<FrontendQuestion[]> {
let response = await index.search<FrontendQuestion>(queryString, {
hitsPerPage,
filters: buildFilter({
starsThreshold,
@ -93,7 +93,7 @@ export default async function searchWithAlgolia({
//facetFilters: buildFacetFilter({filterByPlatforms}),
getRankingInfo: true,
});
let results: FrontendForecast[] = response.hits;
let results: FrontendQuestion[] = response.hits;
let recursionError = ["metaforecast", "metaforecasts", "metaforecasting"];
if (