Skip to content

Commit

Permalink
do svelte things (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjaudiomv authored Feb 17, 2024
1 parent 7d2f5e7 commit 5e9735f
Show file tree
Hide file tree
Showing 10 changed files with 381 additions and 415 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ You can preview the production build with `npm run preview`.

## Deploying

New deploys are down with every push to the main branch. You can deploy one manually by running:
New deploys are done with every push to the main branch. You can deploy one manually by running:

```bash
npm run deploy
Expand Down
167 changes: 167 additions & 0 deletions src/lib/DataConverter.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<script lang="ts">
import { writable } from 'svelte/store';
import { fetchData, exportCSV, exportKML } from './DataUtils';
let query = ''; // User input for data query
const processing = writable(false);
const errorMessage = writable('');
let csvDownloadUrl = '';
let kmlDownloadUrl = '';
async function handleExport() {
processing.set(true);
errorMessage.set('');
csvDownloadUrl = '';
kmlDownloadUrl = '';
try {
const data = await fetchData(query);
if (data && data.length > 0) {
// Process data for CSV
csvDownloadUrl = exportCSV(data);
// Process data for KML
kmlDownloadUrl = query.includes('GetSearchResults') ? exportKML(data) : '';
} else {
throw new Error('No data available for export.');
}
} catch (error) {
if (error instanceof Error) {
errorMessage.set(error.message || 'Failed to export data.');
} else {
errorMessage.set('Failed to export data.');
}
} finally {
processing.set(false);
}
}
</script>

<section>
<div id="export-form">
<h1>BMLT Data Converter</h1>
<div id="inner-box">
<input
type="text"
bind:value={query}
on:keydown={(event) => event.key === 'Enter' && handleExport()}
placeholder="BMLT URL query..."
/>
<button on:click={handleExport} disabled={$processing}>Generate Export Data</button>

{#if $errorMessage}
<p class="error" id="errorMessages">{$errorMessage}</p>
{/if}

{#if csvDownloadUrl}
<a href={csvDownloadUrl} class="download-links" download="exported_data.csv">Download CSV</a
><br />
{/if}
{#if kmlDownloadUrl}
<a href={kmlDownloadUrl} class="download-links" download="exported_data.kml">Download KML</a
>
{/if}
<div id="description">Converts BMLT data from JSON to CSV or KML</div>
</div>
<div id="footer">
<a
href="https://github.com/bmlt-enabled/bmlt-data-converter/issues"
class="footer-link"
target="_blank">Issues?</a
>
</div>
</div>
</section>

<style>
.error {
color: red;
}
h1 {
text-align: center;
}
#export-form {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto 0;
padding: 1px 20px 20px;
background-color: #f7f7f7;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
#errorMessages {
color: red;
text-align: center;
padding-top: 15px;
}
input[type='text'] {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 3px;
box-sizing: border-box;
}
button {
background-color: #007bff;
width: 100%;
color: #fff;
border: none;
padding: 10px 20px;
margin-bottom: 15px;
border-radius: 3px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
.download-links {
text-align: center;
text-decoration: none;
color: #007bff;
}
.footer-link {
display: block;
text-align: center;
text-decoration: none;
color: #007bff;
}
a:hover {
text-decoration: underline;
}
#inner-box {
padding: 20px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 3px;
box-sizing: border-box;
text-align: center;
margin-bottom: 20px;
}
#description {
text-align: center;
margin-top: 20px;
font-size: 1em;
color: #333;
}
#footer {
padding: 10px;
background-color: #e0d8d8;
border: 1px solid #e0d8d8;
border-radius: 3px;
box-sizing: border-box;
text-align: center;
}
</style>
155 changes: 155 additions & 0 deletions src/lib/DataUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import fetchJsonp from 'fetch-jsonp';

export async function fetchData(query: string): Promise<any[]> {
try {
if (!query.includes('/client_interface/json')) {
throw 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) {
throw new Error('No data found');
}
return data;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message || 'Error loading data');
} else {
throw new Error('Error loading data');
}
}
}

export function exportCSV(data: any[]): string {
if (!Array.isArray(data) || data.length === 0) {
throw new Error('No data found');
}
const header = Object.keys(data[0]).join(',');
const csvRows = data.map((row) => {
const values = Object.values(row).map((value) => {
if (typeof value === 'string') {
let stringValue = value.replace(/"/g, '""'); // Escape double quotes
if (stringValue.includes(',') || stringValue.includes('\n')) {
stringValue = `"${value}"`; // Quote fields containing commas or newlines
}
return stringValue;
}
return String(value);
});
return values.join(',');
});
csvRows.unshift(header); // Add header row at the beginning

const csvString = csvRows.join('\n');
const blob = new Blob([csvString], { type: 'text/csv' });
return URL.createObjectURL(blob);
}

export function exportKML(data: any[]): string {
let kmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>`;

// Add place marks
data.forEach((meeting) => {
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);

kmlContent += `
<Placemark>
<name>${name}</name>
${address ? `<address>${address}</address>` : ''}
${description ? `<description>${description}</description>` : ''}
<Point>
<coordinates>${lng},${lat}</coordinates>
</Point>
</Placemark>
`;
});

kmlContent += `
</Document>
</kml>`;

const blob = new Blob([kmlContent], { type: 'application/vnd.google-earth.kml+xml' });
return URL.createObjectURL(blob);
}

function prepareSimpleLine(meeting: { [x: string]: any }, withDate = true): string {
const getLocationInfo = () => {
const locationInfo: any[] = [];
const addInfo = (property: string) => {
if (property in 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 weekday_strings = [
'All',
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday'
];
const weekday = parseInt(meeting['weekday_tinyint'].trim());
const weekdayString = weekday_strings[weekday];

const startTime = `2000-01-01 ${meeting['start_time']}`;
const time = new Date(startTime);

if (weekdayString && withDate) {
let dateString = weekdayString;

if (!isNaN(time.getTime())) {
dateString += `, ${time.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: 'numeric',
hour12: true
})}`;
}

return dateString;
}

return '';
};

const locationInfo = getLocationInfo();
const dateString = getDateString();

if (withDate && dateString && locationInfo) {
return `${dateString}, ${locationInfo}`;
} else if (dateString) {
return dateString;
} else {
return locationInfo;
}
}
Loading

0 comments on commit 5e9735f

Please sign in to comment.