From ad66bdd9056dd97a7de11b1bf6d748dd3e6cc9fe Mon Sep 17 00:00:00 2001 From: pjaudiomv <34245618+pjaudiomv@users.noreply.github.com> Date: Sun, 9 Jun 2024 11:31:42 -0400 Subject: [PATCH] seperate for each export type (#76) --- eslint.config.js | 2 +- src/components/DownloadLinks.svelte | 23 ---- src/components/ExportCSV.svelte | 21 ++++ src/components/ExportForm.svelte | 43 ++++--- src/components/ExportKML.svelte | 92 ++++++++++++++ src/components/ExportXLSX.svelte | 21 ++++ src/components/ExportXML.svelte | 18 +++ src/components/ExportYAML.svelte | 18 +++ src/lib/DataUtils.ts | 179 ---------------------------- src/utils/DataUtils.ts | 59 +++++++++ 10 files changed, 254 insertions(+), 222 deletions(-) delete mode 100644 src/components/DownloadLinks.svelte create mode 100644 src/components/ExportCSV.svelte create mode 100644 src/components/ExportKML.svelte create mode 100644 src/components/ExportXLSX.svelte create mode 100644 src/components/ExportXML.svelte create mode 100644 src/components/ExportYAML.svelte delete mode 100644 src/lib/DataUtils.ts create mode 100644 src/utils/DataUtils.ts diff --git a/eslint.config.js b/eslint.config.js index 9e032e6..69b8a62 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -31,7 +31,7 @@ export default [ ignores: ['build/', '.svelte-kit/', 'dist/', '*.lock'] }, { - files: ['**/*.ts'], + files: ['**/*.ts', '**/*.svelte'], rules: { '@typescript-eslint/no-explicit-any': 'off' } diff --git a/src/components/DownloadLinks.svelte b/src/components/DownloadLinks.svelte deleted file mode 100644 index 8054c4b..0000000 --- a/src/components/DownloadLinks.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - -{#if csvDownloadUrl} - Download CSV
-{/if} -{#if xlsxDownloadUrl} - Download XLSX
-{/if} -{#if xmlDownloadUrl} - Download XML
-{/if} -{#if kmlDownloadUrl} - Download KML
-{/if} -{#if yamlDownloadUrl} - Download YAML -{/if} diff --git a/src/components/ExportCSV.svelte b/src/components/ExportCSV.svelte new file mode 100644 index 0000000..6a06462 --- /dev/null +++ b/src/components/ExportCSV.svelte @@ -0,0 +1,21 @@ + + +Download CSV diff --git a/src/components/ExportForm.svelte b/src/components/ExportForm.svelte index 923d9a8..e376267 100644 --- a/src/components/ExportForm.svelte +++ b/src/components/ExportForm.svelte @@ -1,34 +1,27 @@ + +Download KML diff --git a/src/components/ExportXLSX.svelte b/src/components/ExportXLSX.svelte new file mode 100644 index 0000000..66c76e8 --- /dev/null +++ b/src/components/ExportXLSX.svelte @@ -0,0 +1,21 @@ + + +Download XLSX diff --git a/src/components/ExportXML.svelte b/src/components/ExportXML.svelte new file mode 100644 index 0000000..65023bc --- /dev/null +++ b/src/components/ExportXML.svelte @@ -0,0 +1,18 @@ + + +Download XML diff --git a/src/components/ExportYAML.svelte b/src/components/ExportYAML.svelte new file mode 100644 index 0000000..78368fb --- /dev/null +++ b/src/components/ExportYAML.svelte @@ -0,0 +1,18 @@ + + +Download YAML diff --git a/src/lib/DataUtils.ts b/src/lib/DataUtils.ts deleted file mode 100644 index 04fb928..0000000 --- a/src/lib/DataUtils.ts +++ /dev/null @@ -1,179 +0,0 @@ -import fetchJsonp from 'fetch-jsonp'; -import * as XLSX from 'xlsx'; -import * as js2xmlparser from 'js2xmlparser'; -import * as yaml from 'js-yaml'; - -interface Meeting { - meeting_name: string; - longitude: string; - latitude: string; - weekday_tinyint: string; - start_time: string; - lang_enum: string; - location_text: string; - location_street: string; - location_city_subsection: string; - location_municipality: string; - location_neighborhood: string; - location_province: string; - location_postal_code_1: string; - location_nation: string; - location_info: string; -} - -export async function fetchData(query: string): Promise { - try { - if (!query.includes('/client_interface/json')) { - return Promise.reject(new Error('Query does not contain a valid json endpoint.')); - } - const updatedQuery = query.replace('/client_interface/json/', '/client_interface/jsonp/'); - const response = await fetchJsonp(updatedQuery, { - jsonpCallback: 'callback', - timeout: 10000 // 10 seconds timeout - }); - const data = await response.json(); - if (!Array.isArray(data) || data.length === 0) { - return Promise.reject(new Error('No data found')); - } - return data; - } catch (error) { - throw new Error(error instanceof Error ? error.message : 'Error loading data'); - } -} - -export function exportCSV(data: any[]): string { - const processedData = processExportData(data); - const wb = XLSX.utils.book_new(); - const ws = XLSX.utils.json_to_sheet(processedData); - XLSX.utils.book_append_sheet(wb, ws, 'Data'); - const csvString = XLSX.write(wb, { bookType: 'csv', type: 'string' }); - const blob = new Blob([csvString], { type: 'text/csv' }); - return URL.createObjectURL(blob); -} - -export function exportYAML(data: any[]): string { - const processedData = processExportData(data); - const yamlString = yaml.dump(processedData); - const blob = new Blob([yamlString], { type: 'application/x-yaml' }); - return URL.createObjectURL(blob); -} - -export function exportXML(data: any[]): string { - const processedData = processExportData(data); - const xmlResult = js2xmlparser.parse('root', processedData); - const blob = new Blob([xmlResult], { type: 'text/xml' }); - return URL.createObjectURL(blob); -} - -export function exportXLSX(data: any[]): string { - const processedData = processExportData(data); - const wb = XLSX.utils.book_new(); - const ws = XLSX.utils.json_to_sheet(processedData); - XLSX.utils.book_append_sheet(wb, ws, 'Data'); - const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' }); - const blob = new Blob([s2ab(wbout)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); - return URL.createObjectURL(blob); -} - -export function exportKML(data: Meeting[]): string { - const placemarks = data.map(createPlacemark).filter(Boolean).join('\n'); - const kmlContent = ` - - - ${placemarks} - -`; - const blob = new Blob([kmlContent], { type: 'application/vnd.google-earth.kml+xml' }); - return URL.createObjectURL(blob); -} - -function s2ab(s: string): ArrayBuffer { - const buf = new ArrayBuffer(s.length); - const view = new Uint8Array(buf); - for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xff; - return buf; -} - -// This is for Bus/Train/Custom fields -function processExportData(data: any[]): any[] { - return data.map((row) => - Object.keys(row).reduce((acc, key) => { - let value: string | number = row[key]; - if (typeof value === 'string' && value.includes('#@-@#')) { - [, value] = value.split('#@-@#'); - } - acc[key] = value; - return acc; - }, {} as any) - ); -} - -function createPlacemark(meeting: Meeting): string { - const name = meeting.meeting_name.trim() || 'NA Meeting'; - const lng = parseFloat(meeting.longitude); - const lat = parseFloat(meeting.latitude); - if (!lng || !lat) return ''; - const description = prepareSimpleLine(meeting); - const address = prepareSimpleLine(meeting, false); - return ` - - ${name} - ${address ? `
${address}
` : ''} - ${description ? `${description}` : ''} - - ${lng},${lat} - -
- `.trim(); -} - -function prepareSimpleLine(meeting: Meeting, withDate: boolean = true): string { - const getLocationInfo = () => { - const locationInfo: string[] = []; - const addInfo = (property: keyof Meeting) => { - const value = meeting[property]?.trim() ?? ''; - if (value) locationInfo.push(value); - }; - addInfo('location_text'); - addInfo('location_street'); - addInfo('location_city_subsection'); - addInfo('location_municipality'); - addInfo('location_neighborhood'); - addInfo('location_province'); - addInfo('location_postal_code_1'); - addInfo('location_nation'); - addInfo('location_info'); - return locationInfo.join(', '); - }; - - const getDateString = () => { - const dayOfWeekInt = parseInt(meeting.weekday_tinyint?.trim() ?? '0'); - const adjustedDay = dayOfWeekInt % 7; - // January 1, 2023, was a Sunday. - const baseDate = new Date('2023-01-01'); - baseDate.setDate(baseDate.getDate() + adjustedDay); - const lang = meeting.lang_enum ? (meeting.lang_enum === 'dk' ? 'da' : meeting.lang_enum) : window.navigator.language; - const twelveHrLangs: string[] = ['en', 'es']; - if (dayOfWeekInt && withDate) { - let dateString = baseDate.toLocaleDateString(lang, { weekday: 'long' }); - if (!isNaN(baseDate.getTime())) { - dateString += `, ${baseDate.toLocaleTimeString(lang, { - hour: 'numeric', - minute: 'numeric', - hour12: twelveHrLangs.includes(lang) - })}`; - } - return dateString; - } - return ''; - }; - const locationInfo = getLocationInfo(); - const dateString = getDateString(); - if (withDate && dateString && locationInfo) { - return `${dateString}, ${locationInfo}`; - } else if (dateString) { - return dateString; - } else { - return locationInfo; - } -} diff --git a/src/utils/DataUtils.ts b/src/utils/DataUtils.ts new file mode 100644 index 0000000..f1437c6 --- /dev/null +++ b/src/utils/DataUtils.ts @@ -0,0 +1,59 @@ +import fetchJsonp from 'fetch-jsonp'; + +export interface Meeting { + meeting_name: string; + longitude: string; + latitude: string; + weekday_tinyint: string; + start_time: string; + lang_enum: string; + location_text: string; + location_street: string; + location_city_subsection: string; + location_municipality: string; + location_neighborhood: string; + location_province: string; + location_postal_code_1: string; + location_nation: string; + location_info: string; +} + +export async function fetchData(query: string): Promise { + try { + if (!query.includes('/client_interface/json')) { + return Promise.reject(new Error('Query does not contain a valid json endpoint.')); + } + const updatedQuery = query.replace('/client_interface/json/', '/client_interface/jsonp/'); + const response = await fetchJsonp(updatedQuery, { + jsonpCallback: 'callback', + timeout: 10000 // 10 seconds timeout + }); + const data = await response.json(); + if (!Array.isArray(data) || data.length === 0) { + return Promise.reject(new Error('No data found')); + } + return data; + } catch (error) { + throw new Error(error instanceof Error ? error.message : 'Error loading data'); + } +} + +export function processExportData(data: any[]): any[] { + return data.map((row) => + Object.keys(row).reduce((acc, key) => { + let value: string | number = row[key]; + if (typeof value === 'string' && value.includes('#@-@#')) { + [, value] = value.split('#@-@#'); + } + acc[key] = value; + return acc; + }, {} as any) + ); +} + +export function s2ab(s: string): ArrayBuffer { + const buf = new ArrayBuffer(s.length); + const view = new Uint8Array(buf); + for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xff; + return buf; +}