Back to APIs list

Notion API examples & templates

Use these vals as a playground to view and fork Notion API examples and templates on Val Town. Run any example below or find templates that can be used as a pre-built solution.

stevekrouse.com - my personal website

This val hosts my personal website. The view data is stored in Val Town SQLite - @std/sqlite.

It used to live on Github Pages, which is why I proxy over requests to certain blog posts over to the Github Pages site still.

Todos

  • Speed up page load by loading sqlite data later like in @healeycodes/steve_web
  • Store more (legally storable) analytics data, and maybe make a sparkline!
  • Add some sort of way to contact me
  • Move over all my blog posts from Github Pages (maybe into @std/blob as a CMS?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import { ReloadScriptReactElement } from "https://esm.town/v/stevekrouse/ReloadScript";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import { renderToString } from "npm:react-dom/server";
const linkClass = "text-blue-500 hover:underline";
const Link = (
{ children, href }: {
children?: React.ReactNode;
href: string;
},
) => <a className={linkClass} href={href}>{children}</a>;
const dateClass = "text-xs text-gray-400 font-mono mr-1 hidden sm:inline-block";
async function getHits() {
const [, , { rows: [[allHits]] }, { rows: [[todayHits]] }] = await sqlite.batch([
"CREATE TABLE IF NOT EXISTS stevekrouse_com_hits (timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO stevekrouse_com_hits DEFAULT VALUES",
"SELECT COUNT(*) FROM stevekrouse_com_hits where timestamp > datetime('now', '-28 day')",
"SELECT COUNT(*) from stevekrouse_com_hits where timestamp > datetime('now', '-1 day')",
]);
if (allHits % 100 === 0) email({ subject: `You got ${todayHits} hits today! (${allHits} total)` });
return { allHits, todayHits };
}
export default async (request: Request) => {
const url = new URL(request.url);
if (url.pathname === "/favicon.ico") return new Response(null, { status: 404 });
if (url.pathname !== "/")
return fetch(
`https://stevekrouse.github.io/${url.pathname}${url.search}`,
request as any as RequestInit,
);
const { allHits, todayHits } = await getHits();
return new Response(
renderToString(
<html>
<head>
<title>Steve Krouse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script src={tailwindURL} />
{url.searchParams.get("dev")
? <ReloadScriptReactElement vals={[{ valName: "dot_com", userHandle: "stevekrouse" }]} />
: null}
</head>
<body>
<div className="max-w-3xl p-10 space-y-4 mx-auto">
<h1 className="text-orange-600 text-2xl font-medium">Steve Krouse</h1>
<div>
👋 Hi, I'm Steve. I live in Prospect Heights, Brooklyn.
</div>
<div>
I build <Link href="https://val.town">Val Town</Link>, a social website to code in the cloud.
</div>
<div>
This site was{" "}
<Link href="https://www.val.town/v/stevekrouse/dot_com">built in Val Town</Link>. It was viewed{" "}
{todayHits} times today, and {allHits} times this month.
</div>
<div>
<h2 className="text-xl mb-1">Projects</h2>
<ul>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jul -</span>
<Link href="https://val.town">Val Town</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Sep -</span>
<Link href="https://dateme.directory">Date Me Directory</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jan -</span>
<Link href="https://twitter.com/stevekrouse/status/1520162279899078657">Zaplib</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2021 Mar -</span>
<Link href="http://updates.compose.run">Compose</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2017 Jul -</span>
<Link href="https://futureofcoding.org/">Future of Coding</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2016 May -</span>
<Link href="https://github.com/stevekrouse/woofjs">WoofJS</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Sep -</span>
<Link href="http://coding.space">The Coding Space Curriculum</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Jul -</span>
<Link href="http://thecodingspace.com">The Coding Space</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2014 Jan -</span>Software Engineer @{" "}
<Link href="https://looker.com/">Looker</Link>
</li>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import date_me_doc_locations from "https://esm.town/v/stevekrouse/date_me_doc_locations";
import Layout from "https://esm.town/v/stevekrouse/dateme_layout";
import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave";
import { Hono } from "npm:hono@3";
const Link = ({ href, children }) => {
return <a href={href} class="text-sky-600 hover:text-sky-500" target="_blank">{children}</a>;
};
export const Form = (c) =>
c.html(
<Layout activeTab={"/submit"}>
<form class="max-w-xl mx-auto bg-white p-6" method="post">
<h2 class="text-2xl mb-4 font-semibold text-gray-800">Submit Your Date Me Doc</h2>
<div class="mb-4">
<label for="Name" class="block text-gray-700 font-bold mb-2">
Name<span class="ml-1 text-red-500 ">*</span>
</label>
<input
type="text"
id="Name"
name="Name"
required
placeholder="William Thacker"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
/>
</div>
<div class="mb-4">
<label for="Profile" class="block text-gray-700 font-bold mb-1">
Profile URL<span class="ml-1 text-red-500 ">*</span>
</label>
<div class="text-sm mb-2">
If you don't have a Date Me Doc yet, create one in <Link href="https://notion.com">Notion</Link> or{" "}
<Link href="https://docs.google.com">Google Docs</Link>, and then copy its public link here.
</div>
<input
type="url"
id="Profile"
name="Profile"
required
placeholder="https://horse-and-hound.com/dating"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
/>
</div>
<div class="mb-4">
<label for="Age" class="block text-gray-700 font-bold mb-2">Age</label>
<input
type="number"
id="Age"
name="Age"
required
placeholder="35"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
/>
</div>
<div class="mb-4 flex items-center">
<label for="Gender" class="text-gray-700 font-bold mr-4">Your Gender</label>
{["F", "M", "NB"].map(gender => (
<>
<input
type="checkbox"
id={`Gender-${gender}`}
name="Gender[]"
value={gender}
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label for={`Gender-${gender}`} class="ml-2 block text-sm text-gray-900 mr-4">
{gender}
</label>
</>
))}
</div>
<div class="mb-4 flex items-center">
<label for="InterestedIn" class="text-gray-700 font-bold mr-4">Interested In</label>
{["F", "M", "NB"].map(gender => (
<>
<input
type="checkbox"
id={`InterestedIn-${gender}`}
name="InterestedIn[]"
value={gender}
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label for={`InterestedIn-${gender}`} class="ml-2 block text-sm text-gray-900 mr-4">
{gender}
</label>
</>
))}
</div>
<div class="mb-4">
<label for="Location" class="block text-gray-700 font-bold mb-1">Location</label>

stevekrouse.com - my personal website

This val hosts my personal website. The view data is stored in Val Town SQLite - @std/sqlite.

It used to live on Github Pages, which is why I proxy over requests to certain blog posts over to the Github Pages site still.

Todos

  • Speed up page load by loading sqlite data later like in @healeycodes/steve_web
  • Store more (legally storable) analytics data, and maybe make a sparkline!
  • Add some sort of way to contact me
  • Move over all my blog posts from Github Pages (maybe into @std/blob as a CMS?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import { ReloadScriptReactElement } from "https://esm.town/v/stevekrouse/ReloadScript";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import React from "npm:react";
import { renderToString } from "npm:react-dom/server";
const linkClass = "text-blue-500 hover:underline";
const Link = (
{ children, href }: {
children?: React.ReactNode;
href: string;
},
) => <a className={linkClass} href={href}>{children}</a>;
const dateClass = "text-xs text-gray-400 font-mono mr-1 hidden sm:inline-block";
async function getHits() {
const [, , { rows: [[allHits]] }, { rows: [[todayHits]] }] = await sqlite.batch([
"CREATE TABLE IF NOT EXISTS stevekrouse_com_hits (timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO stevekrouse_com_hits DEFAULT VALUES",
"SELECT COUNT(*) FROM stevekrouse_com_hits where timestamp > datetime('now', '-28 day')",
"SELECT COUNT(*) from stevekrouse_com_hits where timestamp > datetime('now', '-1 day')",
]);
if (allHits % 100 === 0) email({ subject: `You got ${todayHits} hits today! (${allHits} total)` });
return { allHits, todayHits };
}
export default async (request: Request) => {
const url = new URL(request.url);
console.log(url);
if (url.pathname === "/favicon.ico") return new Response(null, { status: 404 });
if (url.pathname !== "/")
return fetch(
`https://stevekrouse.github.io/${url.pathname}${url.search}`,
request as any as RequestInit,
);
const { allHits, todayHits } = await getHits();
return new Response(
renderToString(
<html>
<head>
<title>Steve Krouse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script src={tailwindURL} />
{url.searchParams.get("dev")
? <ReloadScriptReactElement vals={[{ valName: "dot_com", userHandle: "stevekrouse" }]} />
: null}
</head>
<body>
<div className="max-w-3xl p-10 space-y-4 mx-auto">
<h1 className="text-orange-600 text-2xl font-medium">Steve Krouse</h1>
<div>
👋 Hi, I'm Steve. I live in Prospect Heights, Brooklyn.
</div>
<div>
I build <Link href="https://val.town">Val Town</Link>, a social website to code in the cloud.
</div>
<div>
This site was{" "}
<Link href="https://www.val.town/v/stevekrouse/dot_com">built in Val Town</Link>. It was viewed{" "}
{todayHits} times today, and {allHits} times this month.
</div>
<div>
<h2 className="text-xl mb-1">Projects</h2>
<ul>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jul -</span>
<Link href="https://val.town">Val Town</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Sep -</span>
<Link href="https://dateme.directory">Date Me Directory</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jan -</span>
<Link href="https://twitter.com/stevekrouse/status/1520162279899078657">Zaplib</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2021 Mar -</span>
<Link href="http://updates.compose.run">Compose</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2017 Jul -</span>
<Link href="https://futureofcoding.org/">Future of Coding</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2016 May -</span>
<Link href="https://github.com/stevekrouse/woofjs">WoofJS</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Sep -</span>
<Link href="http://coding.space">The Coding Space Curriculum</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Jul -</span>
<Link href="http://thecodingspace.com">The Coding Space</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2014 Jan -</span>Software Engineer @{" "}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import date_me_doc_locations from "https://esm.town/v/stevekrouse/date_me_doc_locations";
import { Hono } from "npm:hono@3";
const Link = ({ href, children }) => {
return <a href={href} class="text-sky-600 hover:text-sky-500" target="_blank">{children}</a>;
};
export const Form = (c) =>
c.html(
<form class="max-w-xl mx-auto bg-white p-6 rounded">
<h2 class="text-2xl mb-4 font-semibold text-gray-800">Submit Your Date Me Doc</h2>
<div class="mb-4">
<label for="name" class="block text-gray-700 font-bold mb-2">
Name<span class="ml-1 text-red-500 ">*</span>
</label>
<input
type="text"
id="name"
name="name"
required
placeholder="William Thacker"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
/>
</div>
<div class="mb-4">
<label for="Profile" class="block text-gray-700 font-bold mb-1">
Profile URL<span class="ml-1 text-red-500 ">*</span>
</label>
<div class="text-sm mb-2">
If you don't have a Date Me Doc yet, create one in <Link href="https://notion.com">Notion</Link> or{" "}
<Link href="https://docs.google.com">Google Docs</Link>, and then copy its public link here.
</div>
<input
type="url"
id="Profile"
name="Profile"
required
placeholder="https://horse-and-hound.com/dating"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
/>
</div>
<div class="mb-4">
<label for="Age" class="block text-gray-700 font-bold mb-2">Age</label>
<input
type="number"
id="Age"
name="Age"
required
placeholder="35"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
/>
</div>
<div class="mb-4 flex items-center">
<label for="Gender" class="text-gray-700 font-bold mr-4">Your Gender</label>
{["Female", "Male", "Non-binary"].map(gender => (
<>
<input
type="checkbox"
id={`Gender-${gender}`}
name="Gender[]"
value={gender}
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label for={`Gender-${gender}`} class="ml-2 block text-sm text-gray-900 mr-4">
{gender}
</label>
</>
))}
</div>
<div class="mb-4 flex items-center">
<label for="InterestedIn" class="text-gray-700 font-bold mr-4">Interested In</label>
{["Female", "Male", "Non-binary"].map(gender => (
<>
<input
type="checkbox"
id={`InterestedIn-${gender}`}
name="InterestedIn[]"
value={gender}
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label for={`InterestedIn-${gender}`} class="ml-2 block text-sm text-gray-900 mr-4">
{gender}
</label>
</>
))}
</div>
<div class="mb-4">
<label for="Location" class="block text-gray-700 font-bold mb-1">Location</label>
<div class="text-sm mb-2">
If you don't see your location represented, request it to be added after you submit your doc.
</div>
<div
id="Location"

Date Me Directory

This is the source code for the Date Me Directory. Contributions welcome!

Architecture

This version of the site still uses Notion to store the data and NoteForms for the form to get your submission into Notion. I intend to cut Notion out of the equation shortly by building our own HTML form that writes data directly to my sqlite database.

Todos

  • Make a form to send data directly to sqlite (in progress: @stevekrouse/date_me_form)
    • Require an email (that isn't shared publicly)
  • Filters: Gender, Interested In, Style, Location, etc
  • Table: hide location, location flexibility, community, contact, last updated in more details
  • Refactor Location to an array of Lat, Lon
    • Geocode all the existing locations
    • Add a geocoder map input to the form
    • Allow selecting multiple location through the form
  • Profile performance & speed up site, possibly add more caching
  • Let people edit their forms
  • Featured profiles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** @jsxImportSource https://esm.sh/preact */
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import browse from "https://esm.town/v/stevekrouse/dateme_browse";
import faq from "https://esm.town/v/stevekrouse/dateme_faq";
import form from "https://esm.town/v/stevekrouse/date_me_form";
import home from "https://esm.town/v/stevekrouse/dateme_home";
import { Hono } from "npm:hono@3";
const app = new Hono();
app.get("/", (c) => c.redirect("/home"));
app.get("/home", home);
app.get("/browse", browse);
app.all("/submit", form);
app.get("/faq", faq);
export default modifyFetchHandler(app.fetch, {
style: `@media (max-width: 500px) {
.github-fork-ribbon {
display: none !important;
}
}`,
val: { handle: "stevekrouse", name: "dateme" },
});

stevekrouse.com - my personal website

This val hosts my personal website. The view data is stored in Val Town SQLite - @std/sqlite.

It used to live on Github Pages, which is why I proxy over requests to certain blog posts over to the Github Pages site still.

Todos

  • Speed up page load by loading sqlite data later like in @healeycodes/steve_web
  • Store more (legally storable) analytics data, and maybe make a sparkline!
  • Add some sort of way to contact me
  • Move over all my blog posts from Github Pages (maybe into @std/blob as a CMS?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import { ReloadScriptReactElement } from "https://esm.town/v/stevekrouse/ReloadScript";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import { renderToString } from "npm:react-dom/server";
const linkClass = "text-blue-500 hover:underline";
const Link = (
{ children, href }: {
children?: React.ReactNode;
href: string;
},
) => <a className={linkClass} href={href}>{children}</a>;
const dateClass = "text-xs text-gray-400 font-mono mr-1 hidden sm:inline-block";
async function getHits() {
const [, , { rows: [[allHits]] }, { rows: [[todayHits]] }] = await sqlite.batch([
"CREATE TABLE IF NOT EXISTS stevekrouse_com_hits (timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO stevekrouse_com_hits DEFAULT VALUES",
"SELECT COUNT(*) FROM stevekrouse_com_hits where timestamp > datetime('now', '-28 day')",
"SELECT COUNT(*) from stevekrouse_com_hits where timestamp > datetime('now', '-1 day')",
]);
if (allHits % 100 === 0) email({ subject: `You got ${todayHits} hits today! (${allHits} total)` });
return { allHits, todayHits };
}
export default async (request: Request) => {
const url = new URL(request.url);
console.log(url);
if (url.pathname === "/favicon.ico") return new Response(null, { status: 404 });
if (url.pathname !== "/")
return fetch(
`https://stevekrouse.github.io/${url.pathname}${url.search}`,
request as any as RequestInit,
);
const { allHits, todayHits } = await getHits();
return new Response(
renderToString(
<html>
<head>
<title>Steve Krouse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script src={tailwindURL} />
{url.searchParams.get("dev")
? <ReloadScriptReactElement vals={[{ valName: "dot_com", userHandle: "stevekrouse" }]} />
: null}
</head>
<body>
<div className="max-w-3xl p-10 space-y-4 mx-auto">
<h1 className="text-orange-600 text-2xl font-medium">Steve Krouse</h1>
<div>
👋 Hi, I'm Steve. I live in Prospect Heights, Brooklyn.
</div>
<div>
I build <Link href="https://val.town">Val Town</Link>, a social website to code in the cloud.
</div>
<div>
This site was{" "}
<Link href="https://www.val.town/v/stevekrouse/dot_com">built in Val Town</Link>. It was viewed{" "}
{todayHits} times today, and {allHits} times this month.
</div>
<div>
<h2 className="text-xl mb-1">Projects</h2>
<ul>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jul -</span>
<Link href="https://val.town">Val Town</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Sep -</span>
<Link href="https://dateme.directory">Date Me Directory</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jan -</span>
<Link href="https://twitter.com/stevekrouse/status/1520162279899078657">Zaplib</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2021 Mar -</span>
<Link href="http://updates.compose.run">Compose</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2017 Jul -</span>
<Link href="https://futureofcoding.org/">Future of Coding</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2016 May -</span>
<Link href="https://github.com/stevekrouse/woofjs">WoofJS</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Sep -</span>
<Link href="http://coding.space">The Coding Space Curriculum</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Jul -</span>
<Link href="http://thecodingspace.com">The Coding Space</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2014 Jan -</span>Software Engineer @{" "}
<Link href="https://looker.com/">Looker</Link>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { parseNotionDateDoc } from "https://esm.town/v/stevekrouse/date_me_doc_helpers";
import { dateMeNotionDatabase } from "https://esm.town/v/stevekrouse/dateMeNotionDatabase";
function jsonifyDoc(doc) {
return Object.fromEntries(
Object.entries(doc)
.filter(([key, value]) => value)
.map(
([key, value]) => [key, key === "Age" ? value : JSON.stringify(value)],
),
);
}
function sqlifyDoc(doc) {
let keys = Object.keys(doc);
let sql = `INSERT INTO DateMeDocs
(${keys.join(", ")})
VALUES
(${keys.map(k => ":" + k).join(", ")})`;
return {
sql,
args: doc,
};
}
export default async function sync_dateme_docs() {
await sqlite.execute("delete from DateMeDocs");
const docs = (await dateMeNotionDatabase)
.map(parseNotionDateDoc)
.map(jsonifyDoc)
.map(sqlifyDoc);
console.log(docs.slice(0, 10));
return sqlite.batch(docs);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { generateLessonHtml } from "https://esm.town/v/petermillspaugh/lessonTemplate";
import { renderToString } from "npm:react-dom/server";
/*
* Work-in-progress! 👷‍♂️
*/
const TITLE = "Retrieval practice";
const FILL_BLANK = (
<>
We often hear things like, "I am a visual learner," or "the way I learn best is by re-reading my notes and
underlining key concepts." Do you think there is any merit to personal preference of learning style? Why or why not?
</>
);
const CONTENT = (
<>
<h2>About this course</h2>
<p>
The main purpose of this course is to teach you about about effective, research-backed learning techniques covered
in the book{" "}
<a href="https://www.goodreads.com/book/show/18770267-make-it-stick">
<em>Make It Stick</em>
</a>. The secondary purpose of this course is to provide a copyable template for creating email-based courses with
a structure informed by effective learning techniques covered in that book. If you know some JavaScript/HTML and
are interested in that, all the code is public and{" "}
<a href="https://petemillspaugh.com/email-course-creator">
I wrote about the implementation on my digital garden
</a>.
</p>
<p>
As a disclaimer, I am not a learning expert. I like to learn, and I like learning about learning. As mentioned
just above, this course is a distillation of{" "}
<em>Make It Stick</em>, supported by some follow-up research I did. It's also a way for me to strengthen what I
learned from the book. If you have any feedback on the course please shoot me an email at
pete@petemillspaugh.com—I'm open to suggestions, corrections, or anything else you'd like to share.
</p>
{/* <h2>Is rereading an effective learning tactic?</h2> */}
{/* <p>Massed practice</p> */}
{/* <p>Rereading is not effective.</p> */}
{
/* <p>
We are poor judges of when we are learning well and when we are not. A good example of this for me is when I take
an online coding course or tutorial on a new technology/tool, writing lots of caffeine-fueled notes and not
self-testing nor applying that new technology to a project. The good feeling from cruising through content and
taking notes is illusory—durable learning requires retrieval and application.
</p> */
}
<h2>📣 This course is still a work-in-progress 📣</h2>
<p>
Thank you for signing up to test things out! I'm still working on the course content, so this lesson is
incomplete, but lmk any thoughts or suggestions you have in the meantime.
</p>
<h2>What is retrieval practice?</h2>
<p>
<em>Retrieval practice</em>{" "}
is cognitive psychology's term for the act of recalling something from memory. Studying flashcards is a very
structured form of retrieval practice, but we retrieve things all the time—like when a friend asks you about the
book you're reading, when you cook a recipe without referencing instructions, or when speaking in another language
that you don't use often.
</p>
<p>
As chapter 2 of <em>Make It Stick</em>—"To Learn, Retrieve"—puts it:
</p>
<blockquote>
<code>
Retrieval practice—recalling facts or concepts or events from memory—is a more effective learning strategy than
review by rereading. Flashcards are a simple example. Retrieval strengthens the memory and interrupts
forgetting. A single, simple quiz after reading a text or hearing a lecture produces better learning and
remembering than rereading the text or reviewing lecture notes.
</code>
</blockquote>
<p>
Each lesson in this course starts with a fill-in-the-blank question and ends with a set of review questions and
reflection. This structure is deliberately designed to force retrieval practice. Not only that, but retrieval
should be spaced, which is the topic of the next lesson and the reason this course includes a one-day delay before
sending the following lesson. From chapter 2 of the book:
</p>
<blockquote>
<code>
When retrieval practice is spaced, allowing some forgetting to occur between tests, it leads to stronger long-
term retention than when it is massed.
</code>
</blockquote>
<h2>Brain food: retrieval as reconsolidation</h2>
{/* <p>Your brain reconsolidates memory upon recall.</p> */}
<p>From the book:</p>
<blockquote>
<code>
While the brain is not a muscle that gets stronger with exercise, the neural pathways that make up a body of
learning do get stronger, when the memory is retrieved and the learning is practiced. Periodic practice arrests
forgetting, strengthens retrieval routes, and is essential for hanging onto the knowledge you want to gain.
</code>
</blockquote>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// NOTE: This doesn't work great.
const valDomain = "chet-notionSiteProxy.web.val.run";
const notionPage = "https://chetcorcos.notion.site/0e27612403084b2fb4a3166edafd623a";
export default async function(req: Request): Promise<Response> {
const notionUrl = new URL(notionPage);
const notionDomain = notionUrl.host;
const url = new URL(req.url);
if (url.pathname === "/") {
url.host = valDomain;
url.pathname = notionUrl.pathname;
return Response.redirect(url);
}
url.host = notionDomain;
const response = await fetch(url, req);
const contents = await response.text();
const fixed = contents
.split(notionDomain)
.join(valDomain)
.split("notion.so")
.join("val.run")
.split("notion.site")
.join("val.run");
const headers = new Headers(response.headers);
headers.delete("Content-Security-Policy");
headers.set("Access-Control-Allow-Origin", "*");
headers.set("Access-Control-Allow-Methods", "GET, HEAD, POST,PUT, OPTIONS");
headers.set("Access-Control-Allow-Headers", "Content-Type");
const proxyRepsonse = new Response(fixed, { ...response, headers });
return proxyRepsonse;
}

Example usage of the add_to_notion_w_ai val

Try with the money database.

Read and watch the demo run here

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/preact */
import { blob } from "https://esm.town/v/std/blob";
import process from "node:process";
import Instructor from "npm:@instructor-ai/instructor";
import { Client } from "npm:@notionhq/client";
import OpenAI from "npm:openai";
import { render } from "npm:preact-render-to-string";
import { z } from "npm:zod";
const dbid = "DB_ID_GOES_HERE";
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const notion = new Client({
auth: NOTION_API_KEY,
});
const MAGIC_AI_FILL_SYMB = "✨";
const supported_notion_props = {
"checkbox": "boolean",
"date": "date",
"multi_select": "array_enum",
"number": "number",
"rich_text": "string",
"select": "enum",
"status": "enum",
"title": "string",
"url": "string_url",
"email": "string_email",
};
const oai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY ?? undefined,
});
const client = Instructor({
client: oai,
mode: "TOOLS",
});
function createPrompt(title, description, properties) {
let prompt =
"You are processing content into a database. Based on the title of the database, its properties, their types and options, and any existing descriptions, infer appropriate values for the fields:\n";
prompt += `Database Title: ${title}\n`;
if (description) {
prompt += `Database Description: ${description}\n\n`;
} else {
prompt += "\n";
}
prompt += "Properties (with types and options where applicable.):\n";
Object.keys(properties).forEach(key => {
const prop = properties[key];
prompt += `Name: ${prop.name}, Type: ${prop.type}`;
if (prop.description) {
prompt += `, Description: ${prop.description}`;
}
prompt += "\n";
if (prop.options) {
if (prop.type === "select" || prop.type === "status") {
prompt += "Options (choose one or none of these options):\n";
} else if (prop.type === "multi_select") {
prompt += "Options (choose one, multiple or none of these options):\n";
}
prop.options.forEach(option => {
prompt += ` - ${option.name}`;
if (option.description) {
prompt += `: ${option.description}`;
}
prompt += "\n";
});
}
});
prompt +=
"\nInfer and assign values to these properties based on the provided content and the aforementioned cotext.";
return prompt;
}
function processProperties(jsonObject) {
const properties = jsonObject.properties;
const filteredProps = {};
Object.keys(properties).forEach(key => {
const prop = properties[key];
const supportedType = supported_notion_props[prop.type];
if (supportedType && (prop.description?.startsWith(MAGIC_AI_FILL_SYMB) || prop.type === "title")) {
filteredProps[key] = {
name: prop.name,
type: prop.type,
description: prop.description ? prop.description.replace(MAGIC_AI_FILL_SYMB, "") : "",
};

Uses instructor and open ai (with gpt-4-turbo) to process any content into a notion database entry.

Use addToNotion with any database id and content.

await addToNotion(
  "DB_ID_GOES_HERE",
  "CONTENT_GOES HERE"//"for example: $43.28 ordered malai kofta and kadhi (doordash) [me and mom] jan 3 2024"
);

Prompts are created based on your database name, database description, property name, property type, property description, and if applicable, property options (and their descriptions).

Supports: checkbox, date, multi_select, number, rich_text, select, status, title, url, email

  • Uses NOTION_API_KEY, OPENAI_API_KEY stored in env variables and uses Valtown blob storage to store information about the database.
  • Use get_notion_db_info to use the stored blob if exists or create one, use get_and_save_notion_db_info to create a new blob (and replace an existing one if exists).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { blob } from "https://esm.town/v/std/blob";
import process from "node:process";
import Instructor from "npm:@instructor-ai/instructor";
import { Client } from "npm:@notionhq/client";
import OpenAI from "npm:openai";
import { z } from "npm:zod";
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const notion = new Client({
auth: NOTION_API_KEY,
});
const MAGIC_AI_FILL_SYMB = "✨";
const supported_notion_props = {
"checkbox": "boolean",
"date": "date",
"multi_select": "array_enum",
"number": "number",
"rich_text": "string",
"select": "enum",
"status": "enum",
"title": "string",
"url": "string_url",
"email": "string_email",
};
const oai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY ?? undefined,
});
const client = Instructor({
client: oai,
mode: "TOOLS",
});
function createPrompt(title, description, properties) {
let prompt =
"You are processing content into a database. Based on the title of the database, its properties, their types and options, and any existing descriptions, infer appropriate values for the fields:\n";
prompt += `Database Title: ${title}\n`;
if (description) {
prompt += `Database Description: ${description}\n\n`;
} else {
prompt += "\n";
}
prompt += "Properties (with types and options where applicable.):\n";
Object.keys(properties).forEach(key => {
const prop = properties[key];
prompt += `Name: ${prop.name}, Type: ${prop.type}`;
if (prop.description) {
prompt += `, Description: ${prop.description}`;
}
prompt += "\n";
if (prop.options) {
if (prop.type === "select" || prop.type === "status") {
prompt += "Options (choose one or none of these options):\n";
} else if (prop.type === "multi_select") {
prompt += "Options (choose one, multiple or none of these options):\n";
}
prop.options.forEach(option => {
prompt += ` - ${option.name}`;
if (option.description) {
prompt += `: ${option.description}`;
}
prompt += "\n";
});
}
});
prompt +=
"\nInfer and assign values to these properties based on the provided content and the aforementioned cotext.";
return prompt;
}
function processProperties(jsonObject) {
const properties = jsonObject.properties;
const filteredProps = {};
Object.keys(properties).forEach(key => {
const prop = properties[key];
const supportedType = supported_notion_props[prop.type];
if (supportedType && (prop.description?.startsWith(MAGIC_AI_FILL_SYMB) || prop.type === "title")) {
filteredProps[key] = {
name: prop.name,
type: prop.type,
description: prop.description ? prop.description.replace(MAGIC_AI_FILL_SYMB, "") : "",
};
if (prop.type === "multi_select" || prop.type === "select" || prop.type === "status") {
filteredProps[key].options = prop[prop.type].options.map(option => ({
name: option.name,
description: option.description || "",

Use todoist for quick notes to add to notion. Uses project to decide which project to fetch to add stuff to notion. Can add to page or database based on config below. Demarkation using sections in the todoist project. Extracts date for page blocks that are added as callouts.

Runs every 1461 days
Fork
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import process from "node:process";
import { TodoistApi } from "npm:@doist/todoist-api-typescript";
import { Client } from "npm:@notionhq/client";
const TODOIST_API_KEY = process.env.TODOIST_API_KEY;
const todoistapi = new TodoistApi(TODOIST_API_KEY);
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const notion = new Client({
auth: NOTION_API_KEY,
});
var add_to_notion_todoist_project_id = "PROJECT_ID_HERE";
var todoist_dict_mapping = {
"habit": {
"todoist-section-id": "SECTION_ID_HERE",
"notion-map-type": "page",
"notion-id": "PAGE_ID_HERE",
},
"papers": {
"todoist-section-id": "SECTION_ID_HERE",
"notion-map-type": "database",
"notion-id": "DB_ID_HERE",
},
};
function getNotionId(section_id) {
if (!section_id) {
return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];
}
for (var key in todoist_dict_mapping) {
if (todoist_dict_mapping[key]["todoist-section-id"] === section_id) {
return [
todoist_dict_mapping[key]["notion-map-type"] || todoist_dict_mapping["dump"]["notion-map-type"],
todoist_dict_mapping[key]["notion-id"] || todoist_dict_mapping["dump"]["notion-id"],
];
}
}
return [todoist_dict_mapping["dump"]["notion-map-type"], todoist_dict_mapping["dump"]["notion-id"]];
}
function convertDateObject(due) {
function convertToISOWithOffset(datetimeStr, timezoneStr) {
const date = new Date(datetimeStr);
const [, sign, hours, minutes] = timezoneStr.match(/GMT ([+-])(\d{1,2}):(\d{2})/);
date.setUTCMinutes(date.getUTCMinutes() + (parseInt(hours) * 60 + parseInt(minutes)) * (sign === "+" ? 1 : -1));
return date.toISOString().split(".")[0]
+ `${sign}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
}
const formatDate = (date, datetime, timezone) => {
let isoString = datetime ? datetime : date;
if (timezone && timezone.startsWith("GMT") && timezone.length > 3) {
return convertToISOWithOffset(datetime, timezone);
} else {
return isoString;
}
};
return {
start: due ? formatDate(due.date, due.datetime, due.timezone) : new Date().toISOString(),
end: null,
time_zone: due && due.datetime && due.timezone && due.timezone.startsWith("GMT") && due.timezone.length > 3
? null
: (due && due.datetime && due.timezone ? due.timezone : "America/Los_Angeles"),
};
}
async function addCalloutToNotionPage(page_id, content, date) {
console.log(JSON.stringify(date));
const response = await notion.blocks.children.append({
block_id: page_id,
children: [{
"callout": {
"rich_text": [{
"type": "mention",
"mention": {
"type": "date",
"date": date,
},
}],
"icon": {
"type": "external",
"external": {
"url": "https://www.notion.so/icons/circle-dot_lightgray.svg",
},
},
"children": [{
"paragraph": {
"rich_text": [{
"text": {
"content": content,
},
}],
},
}],
},
}],
});
Runs every 1461 days
Fork
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import process from "node:process";
import { Client } from "npm:@notionhq/client";
import { fetch } from "npm:cross-fetch";
export default async function(interval: Interval) {
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const PAPERPILE_DB_ID = "DB_ID_GOES_HERE";
if (!NOTION_API_KEY || !PAPERPILE_DB_ID) {
throw new Error("Please fill in your API key and database ID");
}
let dont_update = [];
const notion = new Client({ auth: NOTION_API_KEY });
const databaseId = PAPERPILE_DB_ID;
const queryResponse = await notion.databases.query({
database_id: databaseId,
page_size: 100,
filter: {
or: [
{
property: "Name",
rich_text: {
contains: ";",
},
},
],
},
});
const relevant_results = queryResponse.results.filter(
(i) => !dont_update.includes(i.id),
);
console.log(
`Checked database, found ${relevant_results.length} items to update.`,
);
const all_updated = [];
for (var i of relevant_results) {
let semscholar_query = i.properties.Name.title[0]
.plain_text.replace(
/[^\w\s]/gi,
" ",
); /*+ " " + i.properties["Author(s)"].multi_select.map(x => x.name).join(", ")*/
console.log(semscholar_query);
let fields = `url,title,abstract,authors,year,externalIds`;
const j = await fetch(
`https://api.semanticscholar.org/graph/v1/paper/search?query=${
encodeURIComponent(
semscholar_query,
)
}&limit=1&fields=${
encodeURIComponent(
fields,
)
}`,
).then((r) => r.json()).catch(function() {
console.log("Promise Rejected");
});
if (!(j.total > 0)) {
console.log("No results found for " + semscholar_query);
continue;
}
const paper = j.data[0];
// get bibtex
let all_external_ids = paper.externalIds;
// console.log(paper)
// console.log(all_external_ids)
let doi_to_add = null;
let bibtext_to_add = null;
if (paper.externalIds.DOI) {
doi_to_add = paper.externalIds.DOI;
}
else if (paper.externalIds.PubMed) {
doi_to_add = paper.externalIds.PubMed;
}
else if (paper.externalIds.PubMedCentral) {
doi_to_add = paper.externalIds.PubMedCentral;
}
else if (paper.externalIds.ArXiv) {
doi_to_add = "arxiv." + paper.externalIds.ArXiv;
}
if (doi_to_add) {
// const bib = await fetch('https://api.paperpile.com/api/public/convert', {
// method: 'POST',
// headers: {
// 'Accept': 'application/json, text/plain, */*',
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({fromIds: true, input: doi_to_add.replace("arxiv.",""), targetFormat: "Bibtex"})

RSS feed of stevekrouse.com essays

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import cheerio from "npm:cheerio";
import { Feed } from "npm:feed";
export const stevekrouseRSS = async () => {
const response = await fetch("https://stevekrouse.com/");
const body = await response.text();
const $ = cheerio.load(body);
/*
<li><span class="date">2022 Nov 08 - </span><a href="https://val-town.notion.site/End-programmer-Programming-a749beb4a9b143f2990f575fb7e59b33">End-programmer Programming</a></li>
<li><span class="date">2019 Apr 17 - </span><a href="https://futureofcoding.org/notes/alan-kay-lunch">Lunch with Alan Kay: how to become educated enough to invent the future</a></li>
*/
// parse this into a JSON array with date, link, title
const parsedItems = $("#page > div:nth-child(4) > ul > li").map((_, el) => {
const date = $(el).find(".date").text().trim().replace(" -", "");
const link = $(el).find("a").attr("href");
const title = $(el).find("a").text();
return { date, link, title };
}).get();
console.log(JSON.stringify(parsedItems));
// return this as an RSS feed
const feed = new Feed({
title: "Steve Krouse's Blog",
description: "RSS Feed for Steve Krouse's Blog",
id: "https://stevekrouse.com/",
link: "https://stevekrouse.com/",
language: "en",
updated: new Date(parsedItems[0]?.date),
generator: "Cheerio & Feed for TypeScript",
});
parsedItems.forEach((item) => {
feed.addItem({
title: item.title,
id: item.link,
link: item.link,
description: item.title,
content: item.title,
date: new Date(item.date),
});
});
return new Response(feed.rss2(), {
headers: {
"Content-Type": "application/rss+xml",
},
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
export function parseNotionDateDoc(obj): DateMeDoc {
return {
Id: obj.id,
Name: obj.properties["Name"]?.title?.[0]?.plain_text,
Profile: obj.properties["Profile"]?.url,
Gender: obj.properties["Gender"]?.multi_select?.map((g) => g.name),
Age: JSON.stringify(obj.properties["Age"]?.number),
Contact: obj.properties["Contact "]?.rich_text[0]?.plain_text,
LastUpdated: obj.last_edited_time,
InterestedIn: obj.properties["Interested In"]?.multi_select?.map((i) => i.name),
Location: obj.properties["Location"]?.multi_select?.map((l) => l.name),
Style: obj.properties["Style"]?.multi_select?.map((s) => s.name),
WantsKids: obj.properties["Wants Kids"]?.select?.name,
LocationFlexibility: obj.properties["Location flexibility"]?.select?.name,
Community: obj.properties["Community"]?.multi_select?.map((c) => c.name),
};
}
type Location =
| "San Francisco Bay Area"
| "NYC"
| "DC"
| "Chicago"
| "WI"
| "Western US"
| "Seattle"
| "Boston"
| "Portland"
| "Los Angeles"
| "London"
| "UK"
| "Toronto"
| "Berlin"
| "Canada"
| "Vancouver"
| "NY"
| "Central Europe"
| "TX"
| "South Bay"
| "Brighton"
| "NJ"
| "Brazil"
| "PA"
| "Southern US"
| "Somerville"
| "Cambridge"
| "MA"
| "WA"
| "Dubai"
| "Berkeley"
| "South of France"
| "Amsterdam"
| "Flexible"
| "Austin"
| "North America"
| "Kansas City"
| "Sacramento"
| "Idaho"
| "Asia"
| "Nebraska"
| "Philadelphia"
| "North Carolina"
| "Oxford"
| "Ohio"
| "Indianapolis"
| "Seoul"
| "Oregon"
| "Colorado"
| "CT"
| "New Haven"
| "Denver"
| "Georgia"
| "Ladysmith BC"
| "Lubbock"
| "Asheville"
| "Vancouver Island"
| "Collingswood"
| "Ewing"
| "Ribeirão Bonito"
| "Hackney"
| "Madison";
interface DateMeDoc {
Id: string;
Name: string;
Profile: string;
Gender: "F" | "M" | "NF" | ("F" | "M" | "NF")[];
InterestedIn?: "F" | "M" | "NF" | ("F" | "M" | "NF")[];
Age: string;
Location?: Location[];
Style?: string[];
WantsKids?: string;
LocationFlexibility?: "Flexible" | "Some" | "None";
Community?: "EA" | "Tech" | "Rationalism" | ("EA" | "Tech" | "Rationalism")[];
Contact?: string;
LastUpdated?: string;
}