mirror of
https://github.com/garraflavatra/rolens.git
synced 2025-01-18 13:07:58 +00:00
First commit in a long time
This commit is contained in:
parent
b15fde11db
commit
a84b1498a3
16
frontend/src/lib/progress.js
Normal file
16
frontend/src/lib/progress.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { StartProgressBar, StopProgressBar } from '$wails/go/ui/UI';
|
||||||
|
|
||||||
|
let taskCounter = 0;
|
||||||
|
|
||||||
|
export function startProgress(taskDescription = 'Loading…') {
|
||||||
|
const taskIndex = ++taskCounter;
|
||||||
|
StartProgressBar(taskIndex, taskDescription);
|
||||||
|
|
||||||
|
const task = {
|
||||||
|
id: taskIndex,
|
||||||
|
description: taskDescription,
|
||||||
|
end: () => StopProgressBar(taskIndex),
|
||||||
|
};
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
@ -1,33 +0,0 @@
|
|||||||
import { StartProgressBar, StopProgressBar } from '$wails/go/ui/UI';
|
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
const { update, subscribe } = writable(0);
|
|
||||||
|
|
||||||
let timer;
|
|
||||||
let progressBarShown = false;
|
|
||||||
subscribe(isBusy => {
|
|
||||||
if (isBusy) {
|
|
||||||
document.body.classList.add('busy');
|
|
||||||
if (!progressBarShown) {
|
|
||||||
progressBarShown = true;
|
|
||||||
timer = setTimeout(() => StartProgressBar(''), 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
timer = undefined;
|
|
||||||
}
|
|
||||||
progressBarShown = false;
|
|
||||||
document.body.classList.remove('busy');
|
|
||||||
StopProgressBar();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const busy = {
|
|
||||||
start: () => update(v => ++v),
|
|
||||||
end: () => update(v => --v),
|
|
||||||
subscribe,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default busy;
|
|
@ -0,0 +1,61 @@
|
|||||||
|
<script>
|
||||||
|
import Icon from '$components/icon.svelte';
|
||||||
|
import Modal from '$components/modal.svelte';
|
||||||
|
import { startProgress } from '$lib/progress';
|
||||||
|
import views from '$lib/stores/views';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
export let info;
|
||||||
|
export let collection;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
let viewKey = collection.viewKey;
|
||||||
|
$: viewKey = collection.viewKey;
|
||||||
|
|
||||||
|
async function performExport() {
|
||||||
|
const progress = startProgress('Performing export…');
|
||||||
|
info.view = $views[viewKey];
|
||||||
|
//...
|
||||||
|
progress.end();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal bind:show={info} title="Export results" width="400px">
|
||||||
|
<form on:submit|preventDefault={performExport}>
|
||||||
|
<label class="field">
|
||||||
|
<span class="label">Export</span>
|
||||||
|
<select bind:value={info.contents}>
|
||||||
|
<option value="all">all records</option>
|
||||||
|
<option value="query">all records matching query</option>
|
||||||
|
<option value="querylimitskip">all records matching query, considering limit and skip</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span class="label">Format</span>
|
||||||
|
<select bind:value={info.format}>
|
||||||
|
<option value="jsonarray">JSON array</option>
|
||||||
|
<option value="jsonnewline">JSON: newline-separated objects</option>
|
||||||
|
<option value="csv">CSV</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span class="label">View to use</span>
|
||||||
|
<select bind:value={viewKey}>
|
||||||
|
{#each Object.entries(views.forCollection(collection.hostKey, collection.dbKey, collection.key)) as [key, { name }]}
|
||||||
|
<option value={key}>{name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<button class="btn" type="button" on:click={() => dispatch('openViewConfig')} title="Edit view">
|
||||||
|
<Icon name="cog" />
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,19 +1,18 @@
|
|||||||
<script>
|
<script>
|
||||||
// import CodeExample from '$components/code-example.svelte';
|
|
||||||
import Grid from '$components/grid.svelte';
|
import Grid from '$components/grid.svelte';
|
||||||
import Icon from '$components/icon.svelte';
|
import Icon from '$components/icon.svelte';
|
||||||
import ObjectGrid from '$components/objectgrid.svelte';
|
import ObjectGrid from '$components/objectgrid.svelte';
|
||||||
import input from '$lib/actions/input';
|
import input from '$lib/actions/input';
|
||||||
import { deepClone } from '$lib/objects';
|
import { deepClone } from '$lib/objects';
|
||||||
import busy from '$lib/stores/busy';
|
import { startProgress } from '$lib/progress';
|
||||||
import queries from '$lib/stores/queries';
|
import queries from '$lib/stores/queries';
|
||||||
import applicationSettings from '$lib/stores/settings';
|
import applicationSettings from '$lib/stores/settings';
|
||||||
import views from '$lib/stores/views';
|
import views from '$lib/stores/views';
|
||||||
import { FindItems, RemoveItemById } from '$wails/go/app/App';
|
import { FindItems, RemoveItemById } from '$wails/go/app/App';
|
||||||
import { EJSON } from 'bson';
|
import { EJSON } from 'bson';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
|
import ExportInfo from './components/export.svelte';
|
||||||
import QueryChooser from './components/querychooser.svelte';
|
import QueryChooser from './components/querychooser.svelte';
|
||||||
// import ObjectViewer from '$components/objectviewer.svelte';
|
|
||||||
|
|
||||||
export let collection;
|
export let collection;
|
||||||
export let hosts = {};
|
export let hosts = {};
|
||||||
@ -35,6 +34,7 @@
|
|||||||
let objectViewerData;
|
let objectViewerData;
|
||||||
let queryToSave;
|
let queryToSave;
|
||||||
let showQueryChooser = false;
|
let showQueryChooser = false;
|
||||||
|
let exportInfo;
|
||||||
|
|
||||||
$: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key);
|
$: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key);
|
||||||
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
|
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
|
||||||
@ -42,7 +42,7 @@
|
|||||||
$: activePage = (submittedForm.limit && submittedForm.skip && result?.results?.length) ? submittedForm.skip / submittedForm.limit : 0;
|
$: activePage = (submittedForm.limit && submittedForm.skip && result?.results?.length) ? submittedForm.skip / submittedForm.limit : 0;
|
||||||
|
|
||||||
async function submitQuery() {
|
async function submitQuery() {
|
||||||
busy.start();
|
const progress = startProgress('Performing query…');
|
||||||
activePath = [];
|
activePath = [];
|
||||||
const newResult = await FindItems(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form));
|
const newResult = await FindItems(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form));
|
||||||
if (newResult) {
|
if (newResult) {
|
||||||
@ -50,7 +50,7 @@
|
|||||||
result = newResult;
|
result = newResult;
|
||||||
submittedForm = deepClone(form);
|
submittedForm = deepClone(form);
|
||||||
}
|
}
|
||||||
busy.end();
|
progress.end();
|
||||||
resetFocus();
|
resetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@
|
|||||||
<button class="btn" type="button" on:click={saveQuery}>
|
<button class="btn" type="button" on:click={saveQuery}>
|
||||||
<Icon name="save" /> Save as…
|
<Icon name="save" /> Save as…
|
||||||
</button>
|
</button>
|
||||||
<button class="btn" type="button" on:click={saveQuery}>
|
<button class="btn" type="button" on:click={() => exportInfo = {}}>
|
||||||
<Icon name="save" /> Export results…
|
<Icon name="save" /> Export results…
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn" title="Run query">
|
<button type="submit" class="btn" title="Run query">
|
||||||
@ -245,6 +245,8 @@
|
|||||||
{collection}
|
{collection}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ExportInfo on:openViewConfig bind:collection bind:info={exportInfo} />
|
||||||
|
|
||||||
<!-- <ObjectViewer bind:data={objectViewerData} /> -->
|
<!-- <ObjectViewer bind:data={objectViewerData} /> -->
|
||||||
|
|
||||||
<datalist id="limits">
|
<datalist id="limits">
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
// import CodeExample from '$components/code-example.svelte';
|
|
||||||
import Icon from '$components/icon.svelte';
|
import Icon from '$components/icon.svelte';
|
||||||
import input from '$lib/actions/input';
|
import input from '$lib/actions/input';
|
||||||
import { RemoveItems } from '$wails/go/app/App';
|
import { RemoveItems } from '$wails/go/app/App';
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
// import CodeExample from '$components/code-example.svelte';
|
|
||||||
import ObjectGrid from '$components/objectgrid.svelte';
|
import ObjectGrid from '$components/objectgrid.svelte';
|
||||||
|
|
||||||
export let collection;
|
export let collection;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
// import CodeExample from '$components/code-example.svelte';
|
|
||||||
import Icon from '$components/icon.svelte';
|
import Icon from '$components/icon.svelte';
|
||||||
import input from '$lib/actions/input';
|
import input from '$lib/actions/input';
|
||||||
import { atomicUpdateOperators } from '$lib/mongo';
|
import { atomicUpdateOperators } from '$lib/mongo';
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import DirectoryChooser from '$components/directorychooser.svelte';
|
import DirectoryChooser from '$components/directorychooser.svelte';
|
||||||
import Grid from '$components/grid.svelte';
|
import Grid from '$components/grid.svelte';
|
||||||
import Modal from '$components/modal.svelte';
|
import Modal from '$components/modal.svelte';
|
||||||
import busy from '$lib/stores/busy';
|
import { startProgress } from '$lib/progress';
|
||||||
import { connections } from '$lib/stores/connections';
|
import { connections } from '$lib/stores/connections';
|
||||||
import applicationSettings from '$lib/stores/settings';
|
import applicationSettings from '$lib/stores/settings';
|
||||||
import { OpenConnection, OpenDatabase, PerformDump } from '$wails/go/app/App';
|
import { OpenConnection, OpenDatabase, PerformDump } from '$wails/go/app/App';
|
||||||
@ -21,7 +21,7 @@
|
|||||||
info.collKeys = [];
|
info.collKeys = [];
|
||||||
|
|
||||||
if (hostKey) {
|
if (hostKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Opening connection to host "${hostKey}"`);
|
||||||
const databases = await OpenConnection(hostKey);
|
const databases = await OpenConnection(hostKey);
|
||||||
|
|
||||||
if (databases && !$connections[hostKey]) {
|
if (databases && !$connections[hostKey]) {
|
||||||
@ -31,7 +31,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,14 +40,14 @@
|
|||||||
info.dbKey = dbKey;
|
info.dbKey = dbKey;
|
||||||
|
|
||||||
if (dbKey) {
|
if (dbKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Opening database "${dbKey}"`);
|
||||||
const collections = await OpenDatabase(info.hostKey, dbKey);
|
const collections = await OpenDatabase(info.hostKey, dbKey);
|
||||||
|
|
||||||
for (const collKey of collections?.sort() || []) {
|
for (const collKey of collections?.sort() || []) {
|
||||||
$connections[info.hostKey].databases[dbKey].collections[collKey] = {};
|
$connections[info.hostKey].databases[dbKey].collections[collKey] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import Grid from '$components/grid.svelte';
|
import Grid from '$components/grid.svelte';
|
||||||
import busy from '$lib/stores/busy';
|
import { startProgress } from '$lib/progress';
|
||||||
import { connections } from '$lib/stores/connections';
|
import { connections } from '$lib/stores/connections';
|
||||||
import { WindowSetTitle } from '$wails/runtime/runtime';
|
import { WindowSetTitle } from '$wails/runtime/runtime';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openConnection(hostKey) {
|
async function openConnection(hostKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Connecting to "${hostKey}"…`);
|
||||||
const databases = await OpenConnection(hostKey);
|
const databases = await OpenConnection(hostKey);
|
||||||
|
|
||||||
if (databases) {
|
if (databases) {
|
||||||
@ -41,47 +41,47 @@
|
|||||||
WindowSetTitle(`${hosts[activeHostKey].name} - Rolens`);
|
WindowSetTitle(`${hosts[activeHostKey].name} - Rolens`);
|
||||||
}
|
}
|
||||||
|
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openDatabase(dbKey) {
|
async function openDatabase(dbKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Opening database "${dbKey}"…`);
|
||||||
const collections = await OpenDatabase(activeHostKey, dbKey);
|
const collections = await OpenDatabase(activeHostKey, dbKey);
|
||||||
|
|
||||||
for (const collKey of collections || []) {
|
for (const collKey of collections || []) {
|
||||||
$connections[activeHostKey].databases[dbKey].collections[collKey] = {};
|
$connections[activeHostKey].databases[dbKey].collections[collKey] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dropDatabase(dbKey) {
|
async function dropDatabase(dbKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Dropping database "${dbKey}"…`);
|
||||||
await DropDatabase(activeHostKey, dbKey);
|
await DropDatabase(activeHostKey, dbKey);
|
||||||
await reload();
|
await reload();
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openCollection(collKey) {
|
async function openCollection(collKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Opening collection "${collKey}"…`);
|
||||||
const stats = await OpenCollection(activeHostKey, activeDbKey, collKey);
|
const stats = await OpenCollection(activeHostKey, activeDbKey, collKey);
|
||||||
$connections[activeHostKey].databases[activeDbKey].collections[collKey] = $connections[activeHostKey].databases[activeDbKey].collections[collKey] || {};
|
$connections[activeHostKey].databases[activeDbKey].collections[collKey] = $connections[activeHostKey].databases[activeDbKey].collections[collKey] || {};
|
||||||
$connections[activeHostKey].databases[activeDbKey].collections[collKey].stats = stats;
|
$connections[activeHostKey].databases[activeDbKey].collections[collKey].stats = stats;
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function truncateCollection(dbKey, collKey) {
|
async function truncateCollection(dbKey, collKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Truncating collection "${collKey}"…`);
|
||||||
await TruncateCollection(activeHostKey, dbKey, collKey);
|
await TruncateCollection(activeHostKey, dbKey, collKey);
|
||||||
await reload();
|
await reload();
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dropCollection(dbKey, collKey) {
|
async function dropCollection(dbKey, collKey) {
|
||||||
busy.start();
|
const progress = startProgress(`Dropping collection "${collKey}"…`);
|
||||||
await DropCollection(activeHostKey, dbKey, collKey);
|
await DropCollection(activeHostKey, dbKey, collKey);
|
||||||
await reload();
|
await reload();
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import busy from '$lib/stores/busy';
|
import { startProgress } from '$lib/progress';
|
||||||
import { connections } from '$lib/stores/connections';
|
import { connections } from '$lib/stores/connections';
|
||||||
import { Hosts, RenameCollection } from '$wails/go/app/App';
|
import { Hosts, RenameCollection } from '$wails/go/app/App';
|
||||||
import { EnterText } from '$wails/go/ui/UI';
|
import { EnterText } from '$wails/go/ui/UI';
|
||||||
@ -44,13 +44,13 @@
|
|||||||
async function renameCollection(oldCollKey) {
|
async function renameCollection(oldCollKey) {
|
||||||
const newCollKey = await EnterText('Rename collection', `Enter a new name for collection ${oldCollKey}.`, oldCollKey);
|
const newCollKey = await EnterText('Rename collection', `Enter a new name for collection ${oldCollKey}.`, oldCollKey);
|
||||||
if (newCollKey && (newCollKey !== oldCollKey)) {
|
if (newCollKey && (newCollKey !== oldCollKey)) {
|
||||||
busy.start();
|
const progress = startProgress(`Renaming collection "${oldCollKey}" to "${newCollKey}"…`);
|
||||||
const ok = await RenameCollection(activeHostKey, activeDbKey, oldCollKey, newCollKey);
|
const ok = await RenameCollection(activeHostKey, activeDbKey, oldCollKey, newCollKey);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
activeCollKey = newCollKey;
|
activeCollKey = newCollKey;
|
||||||
await hostTree.reload();
|
await hostTree.reload();
|
||||||
}
|
}
|
||||||
busy.end();
|
progress.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
frontend/wailsjs/go/app/App.d.ts
vendored
2
frontend/wailsjs/go/app/App.d.ts
vendored
@ -39,6 +39,8 @@ export function OpenDatabase(arg1:string,arg2:string):Promise<Array<string>>;
|
|||||||
|
|
||||||
export function PerformDump(arg1:string):Promise<boolean>;
|
export function PerformDump(arg1:string):Promise<boolean>;
|
||||||
|
|
||||||
|
export function PerformFindExport(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>;
|
||||||
|
|
||||||
export function PurgeLogDirectory():Promise<void>;
|
export function PurgeLogDirectory():Promise<void>;
|
||||||
|
|
||||||
export function RemoveHost(arg1:string):Promise<void>;
|
export function RemoveHost(arg1:string):Promise<void>;
|
||||||
|
@ -66,6 +66,10 @@ export function PerformDump(arg1) {
|
|||||||
return window['go']['app']['App']['PerformDump'](arg1);
|
return window['go']['app']['App']['PerformDump'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PerformFindExport(arg1, arg2, arg3, arg4) {
|
||||||
|
return window['go']['app']['App']['PerformFindExport'](arg1, arg2, arg3, arg4);
|
||||||
|
}
|
||||||
|
|
||||||
export function PurgeLogDirectory() {
|
export function PurgeLogDirectory() {
|
||||||
return window['go']['app']['App']['PurgeLogDirectory']();
|
return window['go']['app']['App']['PurgeLogDirectory']();
|
||||||
}
|
}
|
||||||
|
4
frontend/wailsjs/go/ui/UI.d.ts
vendored
4
frontend/wailsjs/go/ui/UI.d.ts
vendored
@ -10,8 +10,8 @@ export function OpenDirectory(arg1:string,arg2:string):Promise<string>;
|
|||||||
|
|
||||||
export function Reveal(arg1:string):Promise<void>;
|
export function Reveal(arg1:string):Promise<void>;
|
||||||
|
|
||||||
export function StartProgressBar(arg1:string):Promise<void>;
|
export function StartProgressBar(arg1:number,arg2:string):Promise<void>;
|
||||||
|
|
||||||
export function Startup(arg1:context.Context):Promise<void>;
|
export function Startup(arg1:context.Context):Promise<void>;
|
||||||
|
|
||||||
export function StopProgressBar():Promise<void>;
|
export function StopProgressBar(arg1:number):Promise<void>;
|
||||||
|
@ -18,14 +18,14 @@ export function Reveal(arg1) {
|
|||||||
return window['go']['ui']['UI']['Reveal'](arg1);
|
return window['go']['ui']['UI']['Reveal'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StartProgressBar(arg1) {
|
export function StartProgressBar(arg1, arg2) {
|
||||||
return window['go']['ui']['UI']['StartProgressBar'](arg1);
|
return window['go']['ui']['UI']['StartProgressBar'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Startup(arg1) {
|
export function Startup(arg1) {
|
||||||
return window['go']['ui']['UI']['Startup'](arg1);
|
return window['go']['ui']['UI']['Startup'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StopProgressBar() {
|
export function StopProgressBar(arg1) {
|
||||||
return window['go']['ui']['UI']['StopProgressBar']();
|
return window['go']['ui']['UI']['StopProgressBar'](arg1);
|
||||||
}
|
}
|
||||||
|
178
internal/app/collection_find_export.go
Normal file
178
internal/app/collection_find_export.go
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ncruces/zenity"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExportContents string
|
||||||
|
type ExportFormat string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExportContentsAll ExportContents = "all"
|
||||||
|
ExportContentsQuery ExportContents = "query"
|
||||||
|
ExportContentsQueryLimitSkip ExportContents = "querylimitskip"
|
||||||
|
|
||||||
|
ExportFormatJsonArray ExportFormat = "jsonarray"
|
||||||
|
ExportFormatJsonNewline ExportFormat = "jsonnewline"
|
||||||
|
ExportFormatCsv ExportFormat = "csv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExportSettings struct {
|
||||||
|
Contents ExportContents `json:"contents"`
|
||||||
|
Format ExportFormat `json:"format"`
|
||||||
|
ViewKey string `json:"viewKey"`
|
||||||
|
QueryJson string `json:"query"`
|
||||||
|
Limit int64 `json:"limit"`
|
||||||
|
Skip int64 `json:"skip"`
|
||||||
|
OutFile string `json:"outfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) PerformFindExport(hostKey, dbKey, collKey, settingsJson string) bool {
|
||||||
|
var settings ExportSettings
|
||||||
|
if err := json.Unmarshal([]byte(settingsJson), &settings); err != nil {
|
||||||
|
runtime.LogWarning(a.ctx, "Could not parse export settings:")
|
||||||
|
runtime.LogWarning(a.ctx, err.Error())
|
||||||
|
zenity.Error(err.Error(), zenity.Title("Couldn't parse export settings!"), zenity.ErrorIcon)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(settings.OutFile); err == nil {
|
||||||
|
zenity.Error(fmt.Sprintf("File %s already exists, export aborted.", settings.OutFile), zenity.ErrorIcon)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
views, err := a.Views()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
view, found := views[settings.ViewKey]
|
||||||
|
if !found {
|
||||||
|
zenity.Error(fmt.Sprintf("View %s is not known", settings.ViewKey), zenity.ErrorIcon)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var query bson.M
|
||||||
|
if settings.Contents != ExportContentsAll {
|
||||||
|
if err = bson.UnmarshalExtJSON([]byte(settings.QueryJson), true, &query); err != nil {
|
||||||
|
runtime.LogInfo(a.ctx, "Invalid find query (exporting):")
|
||||||
|
runtime.LogInfo(a.ctx, err.Error())
|
||||||
|
zenity.Error(err.Error(), zenity.Title("Invalid query"), zenity.ErrorIcon)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client, ctx, close, err := a.connectToHost(hostKey)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
projection := bson.M{}
|
||||||
|
|
||||||
|
if settings.ViewKey != "list" {
|
||||||
|
for _, col := range view.Columns {
|
||||||
|
projection[col.Key] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur, err := client.Database(dbKey).Collection(collKey).Find(ctx, query, &options.FindOptions{
|
||||||
|
Skip: &settings.Skip,
|
||||||
|
Limit: &settings.Limit,
|
||||||
|
Projection: projection,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
runtime.LogInfo(a.ctx, "Unable to get cursor while exporting:")
|
||||||
|
runtime.LogInfo(a.ctx, err.Error())
|
||||||
|
zenity.Error(err.Error(), zenity.Title("Unable to get cursor"), zenity.ErrorIcon)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(settings.OutFile, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
zenity.Error(fmt.Sprintf(err.Error(), zenity.Title("Error while opening file"), settings.OutFile), zenity.ErrorIcon)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var csvWriter *csv.Writer
|
||||||
|
var csvColumnKeys []any
|
||||||
|
|
||||||
|
switch settings.Format {
|
||||||
|
case ExportFormatJsonArray:
|
||||||
|
file.WriteString("[\n")
|
||||||
|
case ExportFormatCsv:
|
||||||
|
csvWriter = csv.NewWriter(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
index := -1
|
||||||
|
for cur.Next(ctx) {
|
||||||
|
index++
|
||||||
|
var item map[any]interface{}
|
||||||
|
|
||||||
|
if err := bson.Unmarshal(cur.Current, &item); err != nil {
|
||||||
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Unable to unmarshal item %d while exporting", index))
|
||||||
|
runtime.LogInfo(a.ctx, err.Error())
|
||||||
|
zenity.Error(err.Error(), zenity.Title("Unable to unmarshal item %d"), zenity.ErrorIcon)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch settings.Format {
|
||||||
|
case ExportFormatCsv:
|
||||||
|
csvItem := make([]string, 0)
|
||||||
|
|
||||||
|
switch settings.ViewKey {
|
||||||
|
case "list":
|
||||||
|
if csvColumnKeys == nil {
|
||||||
|
csvColumnKeys = make([]any, 0)
|
||||||
|
for k := range item {
|
||||||
|
csvColumnKeys = append(csvColumnKeys, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range csvColumnKeys {
|
||||||
|
csvItem = append(csvItem, item[k].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
for _, v := range item {
|
||||||
|
csvItem = append(csvItem, v.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := csvWriter.Write(csvItem); err != nil {
|
||||||
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Unable to write item %d to CSV while exporting", index))
|
||||||
|
runtime.LogInfo(a.ctx, err.Error())
|
||||||
|
zenity.Error(err.Error(), zenity.Title("Unable to write item %d to CSV"), zenity.ErrorIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ExportFormatJsonArray, ExportFormatJsonNewline:
|
||||||
|
itemJson, err := json.Marshal(item)
|
||||||
|
if err != nil {
|
||||||
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Unable to marshal item %d to JSON while exporting", index))
|
||||||
|
runtime.LogInfo(a.ctx, err.Error())
|
||||||
|
zenity.Error(err.Error(), zenity.Title("Unable to marshal item %d to JSON"), zenity.ErrorIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Write(itemJson)
|
||||||
|
if settings.Format == ExportFormatJsonArray {
|
||||||
|
file.WriteString(",")
|
||||||
|
}
|
||||||
|
file.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Format == ExportFormatJsonArray {
|
||||||
|
file.WriteString("]\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
@ -1,28 +1,33 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import "github.com/ncruces/zenity"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
func (u *UI) StartProgressBar(title string) {
|
"github.com/ncruces/zenity"
|
||||||
if u.progress != nil {
|
)
|
||||||
// already loading
|
|
||||||
return
|
func (u *UI) StartProgressBar(id uint, title string) {
|
||||||
}
|
|
||||||
if title == "" {
|
if title == "" {
|
||||||
// default title
|
// default title
|
||||||
title = "Loading"
|
title = "Loading…"
|
||||||
}
|
}
|
||||||
p, err := zenity.Progress(zenity.Title(title), zenity.Pulsate(), zenity.NoCancel(), zenity.Modal())
|
p, err := zenity.Progress(zenity.Title(title), zenity.Pulsate(), zenity.Modal())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u.progress = p
|
u.progressBars[id] = p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UI) StopProgressBar() {
|
func (u *UI) StopProgressBar(id uint) {
|
||||||
if u.progress == nil {
|
for try := 0; try < 10; try++ {
|
||||||
return
|
p := u.progressBars[id]
|
||||||
|
if p != nil {
|
||||||
|
p.Complete()
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("Progress dialog not found:", id, try)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
u.progress.Complete()
|
|
||||||
u.progress.Close()
|
|
||||||
u.progress = nil
|
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UI struct {
|
type UI struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
progress zenity.ProgressDialog
|
progressBars map[uint]zenity.ProgressDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *UI {
|
func New() *UI {
|
||||||
return &UI{}
|
return &UI{
|
||||||
|
progressBars: make(map[uint]zenity.ProgressDialog),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UI) Startup(ctx context.Context) {
|
func (u *UI) Startup(ctx context.Context) {
|
||||||
|
Loading…
Reference in New Issue
Block a user