Add notes to import csv
This commit is contained in:
parent
dba48e90b7
commit
b426020a76
2 changed files with 46 additions and 7 deletions
51
app.js
51
app.js
|
|
@ -1430,12 +1430,30 @@ function parseCSVLine(line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCSV(text) {
|
function parseCSV(text) {
|
||||||
const lines = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n').filter(l => l.trim());
|
text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||||
if (lines.length < 2) return [];
|
|
||||||
const headers = parseCSVLine(lines[0]).map(h => h.trim().toLowerCase().replace(/\s+/g, '_'));
|
// Split into rows respecting quoted multiline fields
|
||||||
return lines.slice(1)
|
const rows = [];
|
||||||
.map(line => {
|
let cur = '', inQ = false;
|
||||||
const vals = parseCSVLine(line);
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const ch = text[i];
|
||||||
|
if (ch === '"') {
|
||||||
|
if (inQ && text[i + 1] === '"') { cur += '""'; i++; }
|
||||||
|
else { inQ = !inQ; cur += ch; }
|
||||||
|
} else if (ch === '\n' && !inQ) {
|
||||||
|
rows.push(cur); cur = '';
|
||||||
|
} else {
|
||||||
|
cur += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cur.trim()) rows.push(cur);
|
||||||
|
|
||||||
|
if (rows.length < 2) return [];
|
||||||
|
const headers = parseCSVLine(rows[0]).map(h => h.trim().toLowerCase().replace(/\s+/g, '_'));
|
||||||
|
return rows.slice(1)
|
||||||
|
.filter(r => r.trim())
|
||||||
|
.map(r => {
|
||||||
|
const vals = parseCSVLine(r);
|
||||||
const row = {};
|
const row = {};
|
||||||
headers.forEach((h, i) => { row[h] = (vals[i] ?? '').trim(); });
|
headers.forEach((h, i) => { row[h] = (vals[i] ?? '').trim(); });
|
||||||
return row;
|
return row;
|
||||||
|
|
@ -1491,11 +1509,13 @@ async function importCSVRows(rows) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add instrument entry (ignore 409 = already exists for that instrument)
|
// Add instrument entry (ignore 409 = already exists for that instrument)
|
||||||
|
// Capture the returned entry so we can attach an instrument note
|
||||||
|
let entryId = null;
|
||||||
if (instId) {
|
if (instId) {
|
||||||
const status = ['callable', 'review', 'to_learn'].includes(row.status?.trim()) ? row.status.trim() : null;
|
const status = ['callable', 'review', 'to_learn'].includes(row.status?.trim()) ? row.status.trim() : null;
|
||||||
const difficulty = ['easy', 'hard'].includes(row.difficulty?.trim()?.toLowerCase()) ? row.difficulty.trim().toLowerCase() : null;
|
const difficulty = ['easy', 'hard'].includes(row.difficulty?.trim()?.toLowerCase()) ? row.difficulty.trim().toLowerCase() : null;
|
||||||
try {
|
try {
|
||||||
await apiFetch(`/tunes/${tuneId}/instruments`, {
|
const entry = await apiFetch(`/tunes/${tuneId}/instruments`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
instrument_id: instId,
|
instrument_id: instId,
|
||||||
|
|
@ -1507,11 +1527,28 @@ async function importCSVRows(rows) {
|
||||||
difficulty,
|
difficulty,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
entryId = entry.id;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!e.message.startsWith('409')) throw e;
|
if (!e.message.startsWith('409')) throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tune-level note
|
||||||
|
const tuneNote = row.tune_note?.trim();
|
||||||
|
if (tuneNote) {
|
||||||
|
await apiFetch(`/tunes/${tuneId}/notes`, {
|
||||||
|
method: 'POST', body: JSON.stringify({ note: tuneNote }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instrument-level note (only if we just created the entry)
|
||||||
|
const instNote = row.instrument_note?.trim();
|
||||||
|
if (instNote && entryId) {
|
||||||
|
await apiFetch(`/tunes/${tuneId}/instruments/${entryId}/notes`, {
|
||||||
|
method: 'POST', body: JSON.stringify({ note: instNote }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
results.created++;
|
results.created++;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
results.errors.push({ name: tuneName, error: e.message });
|
results.errors.push({ name: tuneName, error: e.message });
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,8 @@
|
||||||
<tr><td>status</td><td></td><td>callable / review / to_learn</td></tr>
|
<tr><td>status</td><td></td><td>callable / review / to_learn</td></tr>
|
||||||
<tr><td>difficulty</td><td></td><td>easy / hard</td></tr>
|
<tr><td>difficulty</td><td></td><td>easy / hard</td></tr>
|
||||||
<tr><td>term</td><td></td><td>Created if new</td></tr>
|
<tr><td>term</td><td></td><td>Created if new</td></tr>
|
||||||
|
<tr><td>tune_note</td><td></td><td>One note on the tune</td></tr>
|
||||||
|
<tr><td>instrument_note</td><td></td><td>One note on the instrument entry</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="csv-format-note">If a tune with the same name already exists a new instrument entry is added to it. Sources, tunings, and musicians are created automatically if they don’t exist.</p>
|
<p class="csv-format-note">If a tune with the same name already exists a new instrument entry is added to it. Sources, tunings, and musicians are created automatically if they don’t exist.</p>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue