Created
May 27, 2023 07:36
-
-
Save BeSpunky/10cffec294452dccb4a65cebef9d0b38 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Name: Show Scraped Apartments | |
import '@johnlindquist/kit'; | |
import { Choice } from '@johnlindquist/kit'; | |
import { z } from 'zod'; | |
const ScrapedItemSchema = z.object({ | |
id: z.string(), | |
address: z.string(), | |
neighborhood: z.string().optional(), | |
city: z.string().optional(), | |
price: z.number(), | |
description: z.string(), | |
imageUrl: z.string(), | |
scrapeDate: z.date() | |
}); | |
const AnalyzedItemSchema = z.object({ | |
id: z.string(), | |
score: z.number(), | |
explanation: z.string(), | |
analysisDate: z.date(), | |
hide: z.boolean().optional() | |
}); | |
const ScrapedItemMapSchema = z.record(z.string(), ScrapedItemSchema); | |
const AnalyzedItemListSchema = z.array(AnalyzedItemSchema); | |
const apartments = await db('_apartment-scraper', { scraped: {} as z.infer<typeof ScrapedItemMapSchema>, analyzed: [] as z.infer<typeof AnalyzedItemListSchema> }); | |
const scoreAsStars = (score: number) => '⭐'.repeat(Math.round(score / 2)); | |
const scaledImageUrl = (url: string) => url.replace(/w=\d&h=\d/, 'w=1000&h=1000').replace(/c=\d/, 'c=0'); | |
const previewApartment = (scraped: z.infer<typeof ScrapedItemSchema>, analyzed: z.infer<typeof AnalyzedItemSchema>) => /*html*/` | |
<div class="flex flex-col" style="gap: 10px"> | |
<div dir="rtl"> | |
<h2>${ scraped.address } (${ scraped.price }₪)</h2> | |
<h4>${ scraped.neighborhood }, ${ scraped.city }</h4> | |
<p>${ scraped.description }</p> | |
</div> | |
<div> | |
<h2 class="text-center">Analysis</h2> | |
<p>${ scoreAsStars(analyzed.score) } ${ analyzed.score }</p> | |
<p>Explanation: ${ analyzed.explanation }</p> | |
<img class="w-full" src="${ scaledImageUrl(scraped.imageUrl) }" /> | |
</div> | |
</div> | |
`; | |
const apartmentById = (id: string) => apartments.scraped[id]; | |
const hideApartment = async (id: string) => | |
{ | |
const whyNot = await mini('Why are you hiding this apartment?'); | |
Object.assign(apartments.analyzed.find(a => a.id === id)!, { hide: true, whyNot }); | |
await apartments.write(); | |
main(); // Previously hidden by the explanation prompt | |
}; | |
const createChoices = () => apartments.analyzed.filter(a => !a.hide).map(a => ({ | |
html: /*html*/` | |
<!-- Create a tailwind defined template to display a list item for the apartment, which includes: the address, description, score as star emojis--> | |
<div class="flex flex-col relative p-2 overflow-x-auto" dir="rtl"> | |
<h2 class="text-right">${ apartmentById(a.id).address } <small>${ scoreAsStars(a.score) } (${ a.score })</small></h2> | |
<p class="text-sm">${ apartmentById(a.id).description }</p> | |
</div> | |
`, | |
id: a.id, | |
name: apartmentById(a.id).address, | |
description: apartmentById(a.id).description, | |
className: 'p-2', | |
value: a, | |
preview: () => previewApartment(apartments.scraped[a.id], a) | |
}) satisfies Choice<typeof a>); | |
let lastChoiceId = ''; | |
async function main() | |
{ | |
while (true) | |
{ | |
const choice = await arg({ | |
placeholder: 'Choose an apartment', | |
ignoreBlur: true, | |
defaultChoiceId: lastChoiceId, | |
x: 0, y: 0, | |
height: 1500, | |
width: 1000, | |
shortcuts: [ | |
{ | |
key: 'delete', | |
name: 'Hide apartment', | |
bar: 'right', | |
onPress(_, state) | |
{ | |
if (!state.focused) return; | |
return hideApartment(state.focused.id!); | |
} | |
} | |
] | |
}, createChoices); | |
const apartment = { ...choice, ...apartments.scraped[choice.id] }; | |
lastChoiceId = choice.id; | |
open(`https://www.yad2.co.il/item/${ apartment.id }`); | |
} | |
}; | |
await main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment