Skip to content

Commit 80fd553

Browse files
committed
Build from cached JSON files
1 parent b8dbb84 commit 80fd553

106 files changed

Lines changed: 4206 additions & 122 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/api/OpenProcessing.ts

Lines changed: 61 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
// HELPER FUNCTIONS TO USE THE OPENPROCESSING API
2-
// SEE https://documenter.getpostman.com/view/16936458/2s9YC1Xa6X#intro
3-
41
import type { AnyEntryMap, CollectionEntry } from "astro:content";
5-
import memoize from "lodash/memoize";
2+
import { readFile, access } from "node:fs/promises";
3+
import { constants as FS } from "node:fs";
4+
import path from "node:path";
65

7-
const openProcessingEndpoint = "https://openprocessing.org/api/";
8-
/**
9-
* ID of the OpenProcessing Curation we pull sketches from.
10-
* Currently a placeholder (https://openprocessing.org/curation/78544/)
11-
*/
12-
const curationId = "87649";
13-
const newCurationId = "89576";
6+
const DATA_DIR = path.join(process.cwd(), "src", "cached-data");
7+
8+
const CURATION_2024_FILE = path.join(DATA_DIR, "openprocessing-curation-87649-sketches.json");
9+
const CURATION_2025_FILE = path.join(DATA_DIR, "openprocessing-curation-89576-sketches.json");
10+
const SKETCH_FILE = (id: number) => path.join(DATA_DIR, "openprocessing-sketches", `${id}.json`);
1411

1512
/**
16-
* API Response from a call to the Curation Sketches endpoint
17-
*
13+
* API Response from a call to the Curation Sketches endpoint, cached in the above files
1814
* see https://documenter.getpostman.com/view/16936458/2s9YC1Xa6X#7cd344f6-6e87-426a-969b-2b4a79701dd1
1915
*/
2016
export type OpenProcessingCurationResponse = Array<{
@@ -33,51 +29,6 @@ export type OpenProcessingCurationResponse = Array<{
3329
fullname: string;
3430
}>;
3531

36-
/**
37-
* Get basic info for the sketches contained in a Curation
38-
* from the OpenProcessing API
39-
*
40-
* @param limit max number of sketches to return
41-
* @returns sketches
42-
*/
43-
export const getCurationSketches = memoize(async (
44-
limit?: number,
45-
): Promise<OpenProcessingCurationResponse> => {
46-
const limitParam = limit ? `limit=${limit}` : "";
47-
const response1 = await fetch(
48-
`${openProcessingEndpoint}curation/${curationId}/sketches?${limitParam}`,
49-
);
50-
if(!response1.ok){ //log error instead of throwing error to not cache result in memoize
51-
console.error('getCurationSketches', response1.status, response1.statusText)
52-
}
53-
const payload1 = await response1.json();
54-
55-
const response2 = await fetch(
56-
`${openProcessingEndpoint}curation/${newCurationId}/sketches?${limitParam}`,
57-
);
58-
if(!response2.ok){ //log error instead of throwing error to not cache result in memoize
59-
console.error('getCurationSketches', response2.status, response2.statusText)
60-
}
61-
const payload2 = await response2.json();
62-
63-
// Selected Sketches from the 2025 curation
64-
const priorityIds = ['2690038', '2484739', '2688829', '2689119', '2690571', '2690405','2684408' , '2693274', '2693345', '2691712']
65-
66-
const prioritySketches = payload2.filter(
67-
(sketch: OpenProcessingCurationResponse[number]) => priorityIds.includes(String(sketch.visualID)))
68-
.sort((a: OpenProcessingCurationResponse[number], b: OpenProcessingCurationResponse[number]) => priorityIds.indexOf(String(a.visualID)) - priorityIds.indexOf(String(b.visualID)));
69-
70-
71-
const finalSketches = [
72-
...prioritySketches.map((sketch: OpenProcessingCurationResponse[number]) => ({ ...sketch, curation: '2025' })),
73-
...payload1.map((sketch: OpenProcessingCurationResponse[number]) => ({ ...sketch, curation: '2024' })),
74-
];
75-
76-
return [
77-
...finalSketches,
78-
] as OpenProcessingCurationResponse;
79-
});
80-
8132
/**
8233
* API Response from a call to the Sketch endpoint
8334
*
@@ -96,73 +47,62 @@ export type OpenProcessingSketchResponse = {
9647
submittedOn: string;
9748
createdOn: string;
9849
mode: string;
50+
/* This is extracted from /code */
51+
width: number;
52+
height: number;
9953
};
10054

101-
/**
102-
* Get info about a specific sketch from the OpenProcessing API
103-
* First checks if the sketch is in the memoized curated sketches and returns the data if so,
104-
* Otherwise calls OpenProcessing API for this specific sketch
105-
*
106-
* https://documenter.getpostman.com/view/16936458/2s9YC1Xa6X#7cd344f6-6e87-426a-969b-2b4a79701dd1
107-
* @param id
108-
* @returns
109-
*/
110-
export const getSketch = memoize(
111-
async (id: number): Promise<OpenProcessingSketchResponse> => {
112-
// check for memoized sketch in curation sketches
113-
const curationSketches = await getCurationSketches();
114-
const memoizedSketch = curationSketches.find((el) => el.visualID === id);
115-
if (memoizedSketch) {
116-
return {
117-
...memoizedSketch,
118-
license: "",
119-
} as OpenProcessingSketchResponse;
120-
}
55+
// Selected Sketches from the 2025 curation
56+
export const priorityIds = ['2690038', '2484739', '2688829', '2689119', '2690571', '2690405','2684408' , '2693274', '2693345', '2691712']
12157

122-
// check for sketch data in Open Processing API
123-
const response = await fetch(`${openProcessingEndpoint}sketch/${id}`);
124-
if (!response.ok) {
125-
//log error instead of throwing error to not cache result in memoize
126-
console.error("getSketch", id, response.status, response.statusText);
58+
async function exists(p: string) {
59+
try {
60+
await access(p, FS.F_OK);
61+
return true;
62+
} catch {
63+
return false;
12764
}
128-
const payload = await response.json();
129-
return payload as OpenProcessingSketchResponse;
130-
});
65+
}
66+
67+
async function readJson<T>(filePath: string): Promise<T> {
68+
const text = await readFile(filePath, "utf8");
69+
return JSON.parse(text) as T;
70+
}
13171

13272
/**
133-
* Note: this currently calls `/api/sketch/:id/code`
134-
* But only uses the width and height properties from this call
135-
* Width and height should instead be added to properties for `/api/sketch/:id` or `api/curation/:curationId/sketches` instead
73+
* Get basic info for the sketches contained in a Curation
74+
* from the OpenProcessing API
75+
*
76+
* @param limit max number of sketches to return
77+
* @returns sketches
13678
*/
137-
export const getSketchSize = memoize(async (id: number) => {
138-
const sketch = await getSketch(id)
139-
if (sketch.mode !== 'p5js') {
140-
return { width: undefined, height: undefined };
141-
}
79+
export async function getCurationSketches(): Promise<OpenProcessingCurationResponse> {
80+
const payload2024 = await readJson<OpenProcessingCurationResponse>(CURATION_2024_FILE);
81+
const payload2025 = await readJson<OpenProcessingCurationResponse>(CURATION_2025_FILE);
14282

143-
const response = await fetch(`${openProcessingEndpoint}sketch/${id}/code`);
144-
if(!response.ok){ //log error instead of throwing error to not cache result in memoize
145-
console.error('getSketchSize', id, response.status, response.statusText)
146-
}
147-
const payload = await response.json();
148-
149-
for (const tab of payload) {
150-
if (!tab.code) continue;
151-
const match = /createCanvas\(\s*(\w+),\s*(\w+)\s*(?:,\s*(?:P2D|WEBGL)\s*)?\)/m.exec(tab.code);
152-
if (match) {
153-
if (match[1] === 'windowWidth' && match[2] === 'windowHeight') {
154-
return { width: undefined, height: undefined };
155-
}
156-
157-
const width = parseFloat(match[1]);
158-
const height = parseFloat(match[2]);
159-
if (width && height) {
160-
return { width, height };
161-
}
162-
}
83+
const prioritySketches = payload2025.filter(
84+
(sketch: OpenProcessingCurationResponse[number]) => priorityIds.includes(String(sketch.visualID)))
85+
.sort((a: OpenProcessingCurationResponse[number], b: OpenProcessingCurationResponse[number]) => priorityIds.indexOf(String(a.visualID)) - priorityIds.indexOf(String(b.visualID)));
86+
87+
88+
const allSketches = [
89+
...prioritySketches.map((sketch: OpenProcessingCurationResponse[number]) => ({ ...sketch, curation: '2025' })),
90+
...payload2024.map((sketch: OpenProcessingCurationResponse[number]) => ({ ...sketch, curation: '2024' })),
91+
];
92+
93+
const availableSketches: OpenProcessingCurationResponse = [];
94+
for (const sketch of allSketches) {
95+
if (await exists(SKETCH_FILE(sketch.visualID))) availableSketches.push(sketch);
16396
}
164-
return { width: undefined, height: undefined };
165-
});
97+
98+
return [
99+
...availableSketches,
100+
] as OpenProcessingCurationResponse;
101+
};
102+
103+
export async function getSketch(id: number): Promise<OpenProcessingSketchResponse> {
104+
return await readJson<OpenProcessingSketchResponse>(SKETCH_FILE(id));
105+
}
166106

167107
export const makeSketchLinkUrl = (id: number) =>
168108
`https://openprocessing.org/sketch/${id}`;
@@ -195,12 +135,14 @@ export function isCurationResponse<C extends keyof AnyEntryMap>(
195135
return "visualID" in (item as any);
196136
}
197137

198-
export const getRandomCurationSketches = memoize(async (num = 4) => {
138+
export async function getRandomCurationSketches(num = 4) {
199139
const curationSketches = await getCurationSketches();
200140
const result: OpenProcessingCurationResponse = [];
201141
const usedIndices: Set<number> = new Set();
202142

203-
while (result.length < num) {
143+
const n = Math.min(num, curationSketches.length);
144+
145+
while (result.length < n) {
204146
const randomIndex = Math.floor(Math.random() * curationSketches.length);
205147
if (!usedIndices.has(randomIndex)) {
206148
result.push(curationSketches[randomIndex]);
@@ -209,4 +151,4 @@ export const getRandomCurationSketches = memoize(async (num = 4) => {
209151
}
210152

211153
return result;
212-
});
154+
}

0 commit comments

Comments
 (0)