Add batch API endpoints for cents calculation, client fetches cents from server

This commit is contained in:
Michael Winter 2026-04-01 16:44:20 +02:00
parent c6bb3a12ce
commit 8f332fac52
2 changed files with 69 additions and 19 deletions

View file

@ -581,13 +581,37 @@
// Load from Flask API - get chords and compute graphs client-side
async function loadAllGraphs() {
try {
// Step 1: Get raw chord data
const response = await fetch("/api/chords");
if (!response.ok) throw new Error("API not available");
const data = await response.json();
// Compute graphs from raw chord data client-side
// Step 2: Collect all fractions from all chords
const allFractions = [];
const chordFractions = []; // Track which fractions belong to which chord
for (const chord of data.chords) {
const fractions = [];
for (const pitch of chord) {
fractions.push(pitch.fraction || "1");
}
chordFractions.push(fractions);
allFractions.push(...fractions);
}
// Step 3: Batch fetch cents from server (avoid N+1 problem)
const centsResponse = await fetch("/api/batch-calculate-cents", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ fractions: allFractions })
});
const centsData = await centsResponse.json();
const allCents = centsData.results.map(r => r.cents);
// Step 4: Build graphs with cached cents values
let centsIndex = 0;
const graphs = data.chords.map((chord, index) => {
return calculateGraph(chord, index);
return calculateGraph(chord, index, () => allCents[centsIndex++]);
});
allGraphsData = {
@ -611,18 +635,17 @@
}
}
// Compute graph (nodes + edges) from raw chord data - client-side version
function calculateGraph(chord, index) {
// Compute graph (nodes + edges) from raw chord data - using API for cents
function calculateGraph(chord, index, getNextCent) {
if (!chord) return { nodes: [], edges: [] };
const nodes = [];
const dims = [2, 3, 5, 7];
// Calculate cents for each pitch
// Calculate cents for each pitch (fetched from server)
for (let i = 0; i < chord.length; i++) {
const pitch = chord[i];
const fraction = parseFraction(pitch.fraction || "1");
const cents = fraction > 0 ? 1200 * Math.log2(fraction) : 0;
const cents = getNextCent();
nodes.push({
id: i,
@ -689,18 +712,6 @@
return { nodes, edges, index };
}
// Parse fraction string to number
function parseFraction(fracStr) {
if (typeof fracStr === 'number') return fracStr;
if (!fracStr) return 1;
if (fracStr.includes('/')) {
const [num, den] = fracStr.split('/').map(Number);
return num / den;
}
return Number(fracStr);
}
// Update UI elements
function updateUI() {
hasPrev = currentIndex > 0;

View file

@ -406,6 +406,45 @@ def load_file():
return jsonify({"error": f"Invalid JSON in: {filepath}"}), 400
@app.route("/api/parse-fraction", methods=["POST"])
def parse_fraction_api():
"""Parse a fraction string to float."""
data = request.json
fraction = data.get("fraction", "1")
try:
value = parse_fraction(fraction)
return jsonify({"fraction": fraction, "value": value})
except Exception as e:
return jsonify({"error": str(e)}), 400
@app.route("/api/calculate-cents", methods=["POST"])
def calculate_cents_api():
"""Calculate cents from fraction string."""
data = request.json
fraction = data.get("fraction", "1")
try:
cents = calculate_cents(fraction)
return jsonify({"fraction": fraction, "cents": cents})
except Exception as e:
return jsonify({"error": str(e)}), 400
@app.route("/api/batch-calculate-cents", methods=["POST"])
def batch_calculate_cents_api():
"""Calculate cents for multiple fractions at once."""
data = request.json
fractions = data.get("fractions", [])
try:
results = []
for fraction in fractions:
cents = calculate_cents(fraction)
results.append({"fraction": fraction, "cents": cents})
return jsonify({"results": results})
except Exception as e:
return jsonify({"error": str(e)}), 400
if __name__ == "__main__":
print("Starting Path Navigator server...")
print(f"Loading chords from: {get_chords_file()}")