Skip to content

Commit a0a3cd1

Browse files
authored
Merge branch 'main' into hi-translation-doc
2 parents 0378c1a + b6c597f commit a0a3cd1

File tree

107 files changed

+4211
-145
lines changed

Some content is hidden

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

107 files changed

+4211
-145
lines changed

src/api/OpenProcessing.ts

Lines changed: 61 additions & 120 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,53 +29,6 @@ export type OpenProcessingCurationResponse = Array<{
3329
fullname: string;
3430
}>;
3531

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

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

124-
// check for sketch data in Open Processing API
125-
const response = await fetch(`${openProcessingEndpoint}sketch/${id}`);
126-
if (!response.ok) {
127-
throw new 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;
12864
}
129-
const payload = await response.json();
130-
return payload as OpenProcessingSketchResponse;
131-
});
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+
}
13271

13372
/**
134-
* Note: this currently calls `/api/sketch/:id/code`
135-
* But only uses the width and height properties from this call
136-
* 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
13778
*/
138-
export const getSketchSize = memoize(async (id: number) => {
139-
const sketch = await getSketch(id)
140-
if (sketch.mode !== 'p5js') {
141-
return { width: undefined, height: undefined };
142-
}
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);
14382

144-
const response = await fetch(`${openProcessingEndpoint}sketch/${id}/code`);
145-
if(!response.ok){
146-
throw new Error(`getSketchSize: ${id} ${response.status} ${response.statusText}`)
147-
}
148-
const payload = await response.json();
149-
150-
for (const tab of payload) {
151-
if (!tab.code) continue;
152-
const match = /createCanvas\(\s*(\w+),\s*(\w+)\s*(?:,\s*(?:P2D|WEBGL)\s*)?\)/m.exec(tab.code);
153-
if (match) {
154-
if (match[1] === 'windowWidth' && match[2] === 'windowHeight') {
155-
return { width: undefined, height: undefined };
156-
}
157-
158-
const width = parseFloat(match[1]);
159-
const height = parseFloat(match[2]);
160-
if (width && height) {
161-
return { width, height };
162-
}
163-
}
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);
16496
}
165-
return { width: undefined, height: undefined };
166-
});
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+
}
167106

168107
export const makeSketchLinkUrl = (id: number) =>
169108
`https://openprocessing.org/sketch/${id}`;
@@ -196,12 +135,14 @@ export function isCurationResponse<C extends keyof AnyEntryMap>(
196135
return "visualID" in (item as any);
197136
}
198137

199-
export const getRandomCurationSketches = memoize(async (num = 4) => {
138+
export async function getRandomCurationSketches(num = 4) {
200139
const curationSketches = await getCurationSketches();
201140
const result: OpenProcessingCurationResponse = [];
202141
const usedIndices: Set<number> = new Set();
203142

204-
while (result.length < num) {
143+
const n = Math.min(num, curationSketches.length);
144+
145+
while (result.length < n) {
205146
const randomIndex = Math.floor(Math.random() * curationSketches.length);
206147
if (!usedIndices.has(randomIndex)) {
207148
result.push(curationSketches[randomIndex]);
@@ -210,4 +151,4 @@ export const getRandomCurationSketches = memoize(async (num = 4) => {
210151
}
211152

212153
return result;
213-
});
154+
}

0 commit comments

Comments
 (0)