Skip to content

Commit

Permalink
feat: add drag and drop output
Browse files Browse the repository at this point in the history
Closes #21
  • Loading branch information
stdavis committed Oct 4, 2021
1 parent b979bd8 commit 5612078
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 17 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
"cSpell.words": [
"agrc",
"apos",
"draganddrop",
"Dropzone",
"fulladd",
"heroicons",
"loglevel",
"noreferrer",
"relocator",
Expand Down
Binary file added src/assets/draganddrop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 38 additions & 16 deletions src/pages/Geocoding.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
import React, { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import humanizeDuration from 'humanize-duration';
import { DocumentTextIcon } from '@heroicons/react/outline';
import { useGeocodeContext } from '../components/GeocodeContext.js';

const percentFormatter = new Intl.NumberFormat('en-US', { style: 'percent' });

export default function Geocoding() {
const [status, setStatus] = useState({
const [stats, setStats] = useState({
rowsProcessed: 0,
totalRows: 0,
activeMatchRate: 0,
averageScore: 0,
status: 'idle',
});
const startTime = useRef(new Date());
const abortController = useRef(new AbortController());
const geocodeContext = useGeocodeContext()[0];
const draggable = useRef(null);

const onDragStart = (event) => {
event.preventDefault();
window.ugrc.startDrag('ugrc_geocode_results.csv');
};

useEffect(() => {
window.ugrc.onGeocodingUpdate((_, data) => {
setStatus(data);
setStats(data);
});

window.ugrc.geocode({
Expand All @@ -29,10 +37,10 @@ export default function Geocoding() {
});
}, [geocodeContext]);

const progress = status.rowsProcessed / status.totalRows || 0;
const progress = stats.rowsProcessed / stats.totalRows || 0;
const elapsedTime = new Date().getTime() - startTime.current.getTime();
const timePerRow = elapsedTime / status.rowsProcessed;
const estimatedTimeRemaining = timePerRow * (status.totalRows - status.rowsProcessed);
const timePerRow = elapsedTime / stats.rowsProcessed;
const estimatedTimeRemaining = timePerRow * (stats.totalRows - stats.rowsProcessed);

const cancel = () => {
window.ugrc.cancelGeocode();
Expand All @@ -51,21 +59,21 @@ export default function Geocoding() {
<dl>
<div className="px-4 py-5 bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="font-medium text-gray-500">Rows processed</dt>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">{status.rowsProcessed}</dd>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">{stats.rowsProcessed}</dd>
</div>
<div className="px-4 py-5 bg-white sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="font-medium text-gray-500">Total Rows</dt>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">{status.totalRows}</dd>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">{stats.totalRows}</dd>
</div>
<div className="px-4 py-5 bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="font-medium text-gray-500">Active match rate</dt>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">
{percentFormatter.format(status.activeMatchRate)}
{percentFormatter.format(stats.activeMatchRate)}
</dd>
</div>
<div className="px-4 py-5 bg-white sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="font-medium text-gray-500">Average match score</dt>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">{status.averageScore}</dd>
<dd className="mt-1 text-gray-900 sm:mt-0 sm:col-span-2">{stats.averageScore}</dd>
</div>
<div className="px-4 py-5 bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="font-medium text-gray-500">Time elapsed</dt>
Expand All @@ -80,14 +88,28 @@ export default function Geocoding() {
</dd>
</div>
</dl>
{stats.status === 'complete' && (
<div
className="p-12 mx-auto text-center bg-gray-100 border rounded-lg shadow cursor-grab"
ref={draggable}
draggable={true}
onDragStart={onDragStart}
>
<p>Drag and drop this file to save the results.</p>
<DocumentTextIcon className="w-32 mx-auto" />
<span className="mx-auto font-mono text-sm">ugrc_geocode_results.csv</span>
</div>
)}
</section>
<button
type="button"
onClick={cancel}
disabled={status.rowsProcessed > 0 && status.totalRows === status.rowsProcessed}
>
Cancel
</button>
{stats.status === 'running' ? (
<button
type="button"
onClick={cancel}
disabled={stats.rowsProcessed > 0 && stats.totalRows === stats.rowsProcessed}
>
Cancel
</button>
) : null}
</article>
);
}
27 changes: 26 additions & 1 deletion src/services/csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@ export const cancelGeocode = () => {
cancelled = true;
};

const output = 'ugrc_geocode_results.csv';
export const geocode = async (event, { filePath, fields, apiKey }) => {
cancelled = false;
const parser = fs.createReadStream(filePath).pipe(parse({ columns: true, skipEmptyLines: true }));
const columns = await getFields(filePath);
const writer = fs.createWriteStream(path.join(app.getPath('userData'), 'output.csv'));
const writer = fs.createWriteStream(path.join(app.getPath('userData'), output));
const stringifier = stringify({ columns: [...columns, 'x', 'y', 'score', 'match_address'], header: true });
stringifier.pipe(writer);

Expand All @@ -84,8 +85,17 @@ export const geocode = async (event, { filePath, fields, apiKey }) => {

for await (const record of parser) {
if (cancelled) {
event.reply('onGeocodingUpdate', {
totalRows,
rowsProcessed,
averageScore: Math.round(totalScore / (rowsProcessed - failures)),
activeMatchRate: (rowsProcessed - failures) / rowsProcessed,
status: 'cancelled',
});

return;
}

const newRecord = { ...record, x: null, y: null, score: 0, match_address: null };

const street = cleanseStreet(record[fields.street]);
Expand Down Expand Up @@ -141,12 +151,21 @@ export const geocode = async (event, { filePath, fields, apiKey }) => {
rowsProcessed,
averageScore: Math.round(totalScore / (rowsProcessed - failures)),
activeMatchRate: (rowsProcessed - failures) / rowsProcessed,
status: 'running',
});

await coolYourJets();
}

stringifier.end();

event.reply('onGeocodingUpdate', {
totalRows,
rowsProcessed,
averageScore: Math.round(totalScore / (rowsProcessed - failures)),
activeMatchRate: (rowsProcessed - failures) / rowsProcessed,
status: 'complete',
});
};

ipcMain.handle('getFieldsFromFile', (_, content) => {
Expand All @@ -161,3 +180,9 @@ ipcMain.on('geocode', (event, content) => {
ipcMain.on('cancelGeocode', () => {
return cancelGeocode();
});
ipcMain.on('ondragstart', (event) => {
event.sender.startDrag({
file: path.join(app.getPath('userData'), output),
icon: path.join(process.cwd(), 'src', 'assets', 'draganddrop.png'),
});
});
1 change: 1 addition & 0 deletions src/services/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ contextBridge.exposeInMainWorld('ugrc', {
onGeocodingUpdate: (event, arg) => ipcRenderer.on('onGeocodingUpdate', event, arg),
geocode: (content) => ipcRenderer.send('geocode', content),
cancelGeocode: (content) => ipcRenderer.send('cancelGeocode', content),
startDrag: (content) => ipcRenderer.send('ondragstart', content),
});
3 changes: 3 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ module.exports = {
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
cursor: {
grab: 'grab',
},
},
variants: {
extend: {},
Expand Down

0 comments on commit 5612078

Please sign in to comment.