Skip to content

Commit fe66f36

Browse files
feat: enhance GTFS file upload handling with improved error checks and processing logic
1 parent a860798 commit fe66f36

4 files changed

Lines changed: 243 additions & 120 deletions

File tree

src/lib/components/ComparatorPanel.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@
426426
Object.entries(params).forEach(([key, value]) => {
427427
if (endpoint.params.find((p) => p.name === key && p.inPath)) {
428428
path = path.replace(`{${key}}`, value);
429-
} else {
429+
} else if (value !== '' && value !== undefined && value !== null) {
430430
queryParams.push(`${key}=${encodeURIComponent(value)}`);
431431
}
432432
});

src/lib/components/DiffViewer.svelte

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,25 @@
6363
if (!obj || typeof obj !== 'object') return;
6464
6565
if (Array.isArray(obj) && obj.length > 0) {
66-
const firstItem = obj[0];
67-
if (firstItem && typeof firstItem === 'object') {
68-
let detectedId: string | null = null;
66+
const isPrimitiveArray = typeof obj[0] === 'string' || typeof obj[0] === 'number';
67+
let detectedId: string | null = null;
68+
69+
if (!isPrimitiveArray && obj[0] && typeof obj[0] === 'object') {
6970
for (const field of idFields) {
70-
if ((firstItem as Record<string, unknown>)[field] !== undefined) {
71+
if ((obj[0] as Record<string, unknown>)[field] !== undefined) {
7172
detectedId = field;
7273
break;
7374
}
7475
}
75-
const pathLabel = path || 'Root Array';
76-
arrays.push({
77-
path,
78-
pathLabel,
79-
count: obj.length,
80-
detectedIdField: detectedId
81-
});
8276
}
77+
78+
const pathLabel = path || 'Root Array';
79+
arrays.push({
80+
path,
81+
pathLabel,
82+
count: obj.length,
83+
detectedIdField: detectedId
84+
});
8385
}
8486
8587
if (!Array.isArray(obj)) {

src/lib/components/GtfsStaticViewer.svelte

Lines changed: 42 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -434,94 +434,38 @@
434434
gtfsState.selectedFileId = null;
435435
gtfsState.selectedFileName = null;
436436
gtfsState.rows = [];
437-
gtfsState.uploadProgressPercent = 5;
438-
gtfsState.currentProcessingFile = 'Starting...';
439-
gtfsState.uploadProgress = 'Initializing...';
437+
gtfsState.uploadProgressPercent = 10;
438+
gtfsState.uploadProgress = 'Processing on server...';
440439
441440
await yieldToUI();
442441
443442
try {
444-
console.log('Processing zip file, size:', file.size);
445-
gtfsState.uploadProgress = 'Extracting ZIP file...';
446-
gtfsState.currentProcessingFile = 'Reading ZIP file...';
447-
gtfsState.uploadProgressPercent = 10;
443+
gtfsState.uploadProgressPercent = 30;
444+
gtfsState.uploadProgress = 'Sending file to server...';
448445
await yieldToUI();
449446
450-
const JSZip = (await import('jszip')).default;
451-
const zip = await JSZip.loadAsync(file);
452-
453-
const zipFileNames = Object.keys(zip.files);
454-
455-
if (zipFileNames.length === 0) {
456-
throw new Error('The ZIP file appears to be empty');
457-
}
458-
459-
const txtFiles = Object.entries(zip.files).filter(([filename, entry]) => {
460-
if (entry.dir) return false;
461-
const baseName = filename.split('/').pop() || filename;
462-
return baseName.endsWith('.txt');
463-
});
464-
465-
const totalFiles = txtFiles.length;
466-
const rawFiles: Array<{ filename: string; content: string }> = [];
467-
468-
for (let i = 0; i < txtFiles.length; i++) {
469-
const [filename, zipEntry] = txtFiles[i];
470-
const baseName = filename.split('/').pop() || filename;
471-
472-
gtfsState.currentProcessingFile = baseName;
473-
gtfsState.uploadProgressPercent = Math.round(((i + 1) / totalFiles) * 40);
474-
gtfsState.uploadProgress = `Reading ${baseName} (${i + 1}/${totalFiles})...`;
475-
476-
await yieldToUI();
477-
478-
try {
479-
const content = await zipEntry.async('string');
480-
if (content.trim()) {
481-
rawFiles.push({
482-
filename: baseName,
483-
content
484-
});
485-
}
486-
} catch (readError) {
487-
console.warn(`Failed to read ${baseName}:`, readError);
488-
}
489-
}
490-
491-
if (rawFiles.length === 0) {
492-
throw new Error('No valid GTFS files found in the ZIP');
493-
}
447+
const formData = new FormData();
448+
formData.append('file', file);
494449
495-
gtfsState.uploadProgress = 'Uploading to server for processing...';
496-
gtfsState.uploadProgressPercent = 50;
497-
gtfsState.currentProcessingFile = 'Sending to server...';
498-
await yieldToUI();
499-
500-
const response = await fetch('/api/gtfs-static-db', {
450+
const response = await fetch('/api/gtfs-static', {
501451
method: 'POST',
502-
headers: { 'Content-Type': 'application/json' },
503-
body: JSON.stringify({
504-
action: 'uploadRaw',
505-
name: sourceUrl || 'Uploaded GTFS',
506-
sourceUrl,
507-
files: rawFiles
508-
})
452+
body: formData
509453
});
510454
511-
gtfsState.uploadProgressPercent = 90;
455+
gtfsState.uploadProgressPercent = 80;
512456
await yieldToUI();
513457
514458
const result = await response.json();
515459
516460
if (!response.ok || result.error) {
517-
throw new Error(result.error || 'Failed to upload to database');
461+
throw new Error(result.error || 'Failed to process GTFS file');
518462
}
519463
520464
gtfsState.gtfsFiles = result.files.map(
521-
(f: { id: number; filename: string; columns: string[]; rowCount: number }) => ({
465+
(f: { id: number; filename: string; rowCount: number }) => ({
522466
id: f.id,
523467
name: f.filename,
524-
columns: f.columns,
468+
columns: [],
525469
rowCount: f.rowCount
526470
})
527471
);
@@ -543,14 +487,12 @@
543487
544488
gtfsState.uploadProgress = '';
545489
gtfsState.uploadProgressPercent = 0;
546-
gtfsState.currentProcessingFile = '';
547490
gtfsState.showGtfsInput = false;
548491
} catch (e) {
549492
console.error('Error processing zip:', e);
550493
gtfsState.error = e instanceof Error ? e.message : 'Failed to process GTFS file';
551494
gtfsState.uploadProgress = '';
552495
gtfsState.uploadProgressPercent = 0;
553-
gtfsState.currentProcessingFile = '';
554496
} finally {
555497
gtfsState.loading = false;
556498
}
@@ -596,61 +538,62 @@
596538
gtfsState.selectedFileId = null;
597539
gtfsState.selectedFileName = null;
598540
gtfsState.rows = [];
599-
gtfsState.uploadProgressPercent = 5;
600-
gtfsState.currentProcessingFile = 'Starting...';
601-
gtfsState.uploadProgress = 'Fetching GTFS file...';
541+
gtfsState.uploadProgressPercent = 10;
542+
gtfsState.uploadProgress = 'Processing on server...';
602543
603544
await yieldToUI();
604545
605546
try {
606-
gtfsState.uploadProgress = 'Fetching GTFS file...';
607-
gtfsState.uploadProgressPercent = 10;
608-
gtfsState.currentProcessingFile = 'Downloading from URL...';
547+
gtfsState.uploadProgressPercent = 30;
548+
gtfsState.uploadProgress = 'Fetching and processing...';
609549
await yieldToUI();
610550
611-
console.log('Fetching from URL:', gtfsState.feedUrl);
612-
613551
const response = await fetch('/api/gtfs-static', {
614552
method: 'POST',
615553
headers: { 'Content-Type': 'application/json' },
616554
body: JSON.stringify({ url: gtfsState.feedUrl })
617555
});
618556
619-
console.log('Response received');
620-
gtfsState.uploadProgressPercent = 30;
621-
await yieldToUI();
622-
623-
const contentType = response.headers.get('content-type') || '';
557+
const result = await response.json();
624558
625-
if (contentType.includes('application/json')) {
626-
const data = await response.json();
627-
throw new Error(data.error || 'Failed to fetch GTFS file');
559+
if (!response.ok || result.error) {
560+
throw new Error(result.error || 'Failed to fetch GTFS file');
628561
}
629562
630-
if (!response.ok) {
631-
throw new Error(`Failed to fetch GTFS file: ${response.status} ${response.statusText}`);
632-
}
563+
gtfsState.gtfsFiles = result.files.map(
564+
(f: { id: number; filename: string; rowCount: number }) => ({
565+
id: f.id,
566+
name: f.filename,
567+
columns: [],
568+
rowCount: f.rowCount
569+
})
570+
);
633571
634-
gtfsState.uploadProgress = 'Downloading file...';
635-
gtfsState.uploadProgressPercent = 40;
636-
await yieldToUI();
572+
gtfsState.gtfsFiles.sort((a, b) => {
573+
const aRequired = GTFS_FILES[a.name]?.required ?? false;
574+
const bRequired = GTFS_FILES[b.name]?.required ?? false;
575+
if (aRequired !== bRequired) return bRequired ? 1 : -1;
576+
return a.name.localeCompare(b.name);
577+
});
637578
638-
const blob = await response.blob();
639-
console.log('Blob received, size:', blob.size);
579+
gtfsState.uploadProgressPercent = 100;
580+
gtfsState.uploadProgress = 'Complete!';
581+
saveUrl(gtfsState.feedUrl);
640582
641-
if (blob.size === 0) {
642-
throw new Error('Received empty file from server');
583+
if (gtfsState.gtfsFiles.length > 0) {
584+
await selectFile(gtfsState.gtfsFiles[0]);
643585
}
644586
645-
await processZipFile(blob, gtfsState.feedUrl);
646-
saveUrl(gtfsState.feedUrl);
587+
gtfsState.uploadProgress = '';
588+
gtfsState.uploadProgressPercent = 0;
589+
gtfsState.showGtfsInput = false;
647590
} catch (e) {
648591
console.error('fetchFromUrl error:', e);
649592
gtfsState.error = e instanceof Error ? e.message : 'Failed to fetch GTFS file';
593+
} finally {
650594
gtfsState.loading = false;
651595
gtfsState.uploadProgress = '';
652596
gtfsState.uploadProgressPercent = 0;
653-
gtfsState.currentProcessingFile = '';
654597
}
655598
}
656599

0 commit comments

Comments
 (0)