feat: guesstimate platform, show guesstimate viz on question pages

This commit is contained in:
Vyacheslav Matyukhin 2022-05-09 17:49:36 +04:00
parent 05b0daa631
commit 5c248388b6
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
7 changed files with 128 additions and 86 deletions

View File

@ -0,0 +1,103 @@
import axios from "axios";
import { parseISO } from "date-fns";
/* Imports */
import { Question } from "@prisma/client";
import { AlgoliaQuestion } from "../../backend/utils/algolia";
import { prisma } from "../database/prisma";
import { Platform } from "./";
/* Definitions */
const searchEndpoint =
"https://m629r9ugsg-dsn.algolia.net/1/indexes/Space_production/query?x-algolia-agent=Algolia%20for%20vanilla%20JavaScript%203.32.1&x-algolia-application-id=M629R9UGSG&x-algolia-api-key=4e893740a2bd467a96c8bfcf95b2809c";
const apiEndpoint = "https://guesstimate.herokuapp.com";
/* Body */
const modelToQuestion = (model: any): Question => {
const { description } = model;
// const description = model.description
// ? model.description.replace(/\n/g, " ").replace(/ /g, " ")
// : "";
const stars = description.length > 250 ? 2 : 1;
const timestamp = parseISO(model.created_at);
const q: Question = {
id: `guesstimate-${model.id}`,
title: model.name,
url: `https://www.getguesstimate.com/models/${model.id}`,
timestamp,
platform: "guesstimate",
description,
options: [],
qualityindicators: {
stars,
numforecasts: 1,
numforecasters: 1,
},
extra: {
visualization: model.big_screenshot,
},
// ranking: 10 * (index + 1) - 0.5, //(model._rankingInfo - 1*index)// hack
};
return q;
};
async function search(query: string): Promise<AlgoliaQuestion[]> {
const response = await axios({
url: searchEndpoint,
headers: {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
},
data: `{\"params\":\"query=${query.replace(
/ /g,
"%20"
)}&hitsPerPage=20&page=0&getRankingInfo=true\"}`,
method: "POST",
});
const models: any[] = response.data.hits;
const mappedModels: AlgoliaQuestion[] = models.map((model) => {
const q = modelToQuestion(model);
return {
...q,
timestamp: String(q.timestamp),
};
});
// filter for duplicates. Surprisingly common.
let uniqueTitles = [];
let uniqueModels: AlgoliaQuestion[] = [];
for (let model of mappedModels) {
if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) {
uniqueModels.push(model);
uniqueTitles.push(model.title);
}
}
return uniqueModels;
}
const fetchQuestion = async (id: number): Promise<Question> => {
const response = await axios({ url: `${apiEndpoint}/spaces/${id}` });
let q = modelToQuestion(response.data);
q = await prisma.question.upsert({
where: { id: q.id },
create: q,
update: q,
});
return q;
};
export const guesstimate: Platform & {
search: typeof search;
fetchQuestion: typeof fetchQuestion;
} = {
name: "guesstimate",
label: "Guesstimate",
color: "#223900",
search,
fetchQuestion,
};

View File

@ -7,6 +7,7 @@ import { foretold } from "./foretold";
import { givewellopenphil } from "./givewellopenphil"; import { givewellopenphil } from "./givewellopenphil";
import { goodjudgment } from "./goodjudgment"; import { goodjudgment } from "./goodjudgment";
import { goodjudgmentopen } from "./goodjudgmentopen"; import { goodjudgmentopen } from "./goodjudgmentopen";
import { guesstimate } from "./guesstimate";
import { infer } from "./infer"; import { infer } from "./infer";
import { kalshi } from "./kalshi"; import { kalshi } from "./kalshi";
import { manifold } from "./manifold"; import { manifold } from "./manifold";
@ -72,6 +73,7 @@ export const platforms: Platform[] = [
givewellopenphil, givewellopenphil,
goodjudgment, goodjudgment,
goodjudgmentopen, goodjudgmentopen,
guesstimate,
infer, infer,
kalshi, kalshi,
manifold, manifold,
@ -161,21 +163,12 @@ export interface PlatformConfig {
} }
// get frontend-safe version of platforms data // get frontend-safe version of platforms data
export const getPlatformsConfig = (options: { export const getPlatformsConfig = (): PlatformConfig[] => {
withGuesstimate: boolean;
}): PlatformConfig[] => {
const platformsConfig = platforms.map((platform) => ({ const platformsConfig = platforms.map((platform) => ({
name: platform.name, name: platform.name,
label: platform.label, label: platform.label,
color: platform.color, color: platform.color,
})); }));
if (options.withGuesstimate) {
platformsConfig.push({
name: "guesstimate",
label: "Guesstimate",
color: "223900",
});
}
return platformsConfig; return platformsConfig;
}; };

View File

@ -2,6 +2,7 @@ import { History, Question } from "@prisma/client";
import { prisma } from "../../backend/database/prisma"; import { prisma } from "../../backend/database/prisma";
import { QualityIndicators } from "../../backend/platforms"; import { QualityIndicators } from "../../backend/platforms";
import { guesstimate } from "../../backend/platforms/guesstimate";
import { builder } from "../builder"; import { builder } from "../builder";
import { PlatformObj } from "./platforms"; import { PlatformObj } from "./platforms";
@ -144,6 +145,13 @@ builder.queryField("question", (t) =>
id: t.arg({ type: "ID", required: true }), id: t.arg({ type: "ID", required: true }),
}, },
resolve: async (parent, args) => { resolve: async (parent, args) => {
const parts = String(args.id).split("-");
const [platform, id] = [parts[0], parts.slice(1).join("-")];
if (platform === "guesstimate") {
const q = await guesstimate.fetchQuestion(Number(id));
console.log(q);
return q;
}
return await prisma.question.findUnique({ return await prisma.question.findUnique({
where: { where: {
id: String(args.id), id: String(args.id),

View File

@ -1,5 +1,5 @@
import { guesstimate } from "../../backend/platforms/guesstimate";
import { AlgoliaQuestion } from "../../backend/utils/algolia"; import { AlgoliaQuestion } from "../../backend/utils/algolia";
import searchGuesstimate from "../../web/worker/searchGuesstimate";
import searchWithAlgolia from "../../web/worker/searchWithAlgolia"; import searchWithAlgolia from "../../web/worker/searchWithAlgolia";
import { builder } from "../builder"; import { builder } from "../builder";
import { QuestionObj } from "./questions"; import { QuestionObj } from "./questions";
@ -54,7 +54,7 @@ builder.queryField("searchQuestions", (t) =>
const [responsesNotGuesstimate, responsesGuesstimate] = const [responsesNotGuesstimate, responsesGuesstimate] =
await Promise.all([ await Promise.all([
unawaitedAlgoliaResponse, unawaitedAlgoliaResponse,
searchGuesstimate(query), guesstimate.search(query),
]); // faster than two separate requests ]); // faster than two separate requests
results = [...responsesNotGuesstimate, ...responsesGuesstimate]; results = [...responsesNotGuesstimate, ...responsesGuesstimate];
} else { } else {

View File

@ -13,7 +13,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
const [ssrCache, client] = ssrUrql(); const [ssrCache, client] = ssrUrql();
const urlQuery = context.query; const urlQuery = context.query;
const platformsConfig = getPlatformsConfig({ withGuesstimate: true }); const platformsConfig = getPlatformsConfig();
const defaultQueryParameters: QueryParameters = { const defaultQueryParameters: QueryParameters = {
query: "", query: "",

View File

@ -74,7 +74,17 @@ const LargeQuestionCard: React.FC<{
</div> </div>
<div className="mb-8"> <div className="mb-8">
<HistoryChart question={question} /> {question.platform.id === "guesstimate" ? (
<a className="no-underline" href={question.url} target="_blank">
<img
className="rounded-sm"
src={question.visualization}
alt="Guesstimate Screenshot"
/>
</a>
) : (
<HistoryChart question={question} />
)}
</div> </div>
<ReactMarkdown <ReactMarkdown

View File

@ -1,72 +0,0 @@
/* Imports */
import axios from "axios";
import { AlgoliaQuestion } from "../../backend/utils/algolia";
/* Definitions */
const urlEndPoint =
"https://m629r9ugsg-dsn.algolia.net/1/indexes/Space_production/query?x-algolia-agent=Algolia%20for%20vanilla%20JavaScript%203.32.1&x-algolia-application-id=M629R9UGSG&x-algolia-api-key=4e893740a2bd467a96c8bfcf95b2809c";
/* Body */
export default async function searchGuesstimate(
query
): Promise<AlgoliaQuestion[]> {
const response = await axios({
url: urlEndPoint,
// credentials: "omit",
headers: {
// "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0",
Accept: "application/json",
"Accept-Language": "en-US,en;q=0.5",
"content-type": "application/x-www-form-urlencoded",
},
data: `{\"params\":\"query=${query.replace(
/ /g,
"%20"
)}&hitsPerPage=20&page=0&getRankingInfo=true\"}`,
method: "POST",
});
const models: any[] = response.data.hits;
const mappedModels: AlgoliaQuestion[] = models.map((model, index) => {
const description = model.description
? model.description.replace(/\n/g, " ").replace(/ /g, " ")
: "";
const stars = description.length > 250 ? 2 : 1;
const q: AlgoliaQuestion = {
id: `guesstimate-${model.id}`,
title: model.name,
url: `https://www.getguesstimate.com/models/${model.id}`,
timestamp: model.created_at, // TODO - check that format matches
platform: "guesstimate",
description,
options: [],
qualityindicators: {
stars,
numforecasts: 1,
numforecasters: 1,
},
extra: {
visualization: model.big_screenshot,
},
// ranking: 10 * (index + 1) - 0.5, //(model._rankingInfo - 1*index)// hack
};
return q;
});
// filter for duplicates. Surprisingly common.
let uniqueTitles = [];
let uniqueModels: AlgoliaQuestion[] = [];
for (let model of mappedModels) {
if (!uniqueTitles.includes(model.title) && !model.title.includes("copy")) {
uniqueModels.push(model);
uniqueTitles.push(model.title);
}
}
console.log(uniqueModels);
return uniqueModels;
}
// searchGuesstimate("COVID-19").then(guesstimateModels => console.log(guesstimateModels))