feat: guesstimate platform, show guesstimate viz on question pages
This commit is contained in:
		
							parent
							
								
									05b0daa631
								
							
						
					
					
						commit
						5c248388b6
					
				
							
								
								
									
										103
									
								
								src/backend/platforms/guesstimate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/backend/platforms/guesstimate.ts
									
									
									
									
									
										Normal 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, | ||||
| }; | ||||
|  | @ -7,6 +7,7 @@ import { foretold } from "./foretold"; | |||
| import { givewellopenphil } from "./givewellopenphil"; | ||||
| import { goodjudgment } from "./goodjudgment"; | ||||
| import { goodjudgmentopen } from "./goodjudgmentopen"; | ||||
| import { guesstimate } from "./guesstimate"; | ||||
| import { infer } from "./infer"; | ||||
| import { kalshi } from "./kalshi"; | ||||
| import { manifold } from "./manifold"; | ||||
|  | @ -72,6 +73,7 @@ export const platforms: Platform[] = [ | |||
|   givewellopenphil, | ||||
|   goodjudgment, | ||||
|   goodjudgmentopen, | ||||
|   guesstimate, | ||||
|   infer, | ||||
|   kalshi, | ||||
|   manifold, | ||||
|  | @ -161,21 +163,12 @@ export interface PlatformConfig { | |||
| } | ||||
| 
 | ||||
| // get frontend-safe version of platforms data
 | ||||
| export const getPlatformsConfig = (options: { | ||||
|   withGuesstimate: boolean; | ||||
| }): PlatformConfig[] => { | ||||
| export const getPlatformsConfig = (): PlatformConfig[] => { | ||||
|   const platformsConfig = platforms.map((platform) => ({ | ||||
|     name: platform.name, | ||||
|     label: platform.label, | ||||
|     color: platform.color, | ||||
|   })); | ||||
|   if (options.withGuesstimate) { | ||||
|     platformsConfig.push({ | ||||
|       name: "guesstimate", | ||||
|       label: "Guesstimate", | ||||
|       color: "223900", | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   return platformsConfig; | ||||
| }; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { History, Question } from "@prisma/client"; | |||
| 
 | ||||
| import { prisma } from "../../backend/database/prisma"; | ||||
| import { QualityIndicators } from "../../backend/platforms"; | ||||
| import { guesstimate } from "../../backend/platforms/guesstimate"; | ||||
| import { builder } from "../builder"; | ||||
| import { PlatformObj } from "./platforms"; | ||||
| 
 | ||||
|  | @ -144,6 +145,13 @@ builder.queryField("question", (t) => | |||
|       id: t.arg({ type: "ID", required: true }), | ||||
|     }, | ||||
|     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({ | ||||
|         where: { | ||||
|           id: String(args.id), | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import { guesstimate } from "../../backend/platforms/guesstimate"; | ||||
| import { AlgoliaQuestion } from "../../backend/utils/algolia"; | ||||
| import searchGuesstimate from "../../web/worker/searchGuesstimate"; | ||||
| import searchWithAlgolia from "../../web/worker/searchWithAlgolia"; | ||||
| import { builder } from "../builder"; | ||||
| import { QuestionObj } from "./questions"; | ||||
|  | @ -54,7 +54,7 @@ builder.queryField("searchQuestions", (t) => | |||
|         const [responsesNotGuesstimate, responsesGuesstimate] = | ||||
|           await Promise.all([ | ||||
|             unawaitedAlgoliaResponse, | ||||
|             searchGuesstimate(query), | ||||
|             guesstimate.search(query), | ||||
|           ]); // faster than two separate requests
 | ||||
|         results = [...responsesNotGuesstimate, ...responsesGuesstimate]; | ||||
|       } else { | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ( | |||
|   const [ssrCache, client] = ssrUrql(); | ||||
|   const urlQuery = context.query; | ||||
| 
 | ||||
|   const platformsConfig = getPlatformsConfig({ withGuesstimate: true }); | ||||
|   const platformsConfig = getPlatformsConfig(); | ||||
| 
 | ||||
|   const defaultQueryParameters: QueryParameters = { | ||||
|     query: "", | ||||
|  |  | |||
|  | @ -74,7 +74,17 @@ const LargeQuestionCard: React.FC<{ | |||
|     </div> | ||||
| 
 | ||||
|     <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> | ||||
| 
 | ||||
|     <ReactMarkdown | ||||
|  |  | |||
|  | @ -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))
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user