Skip to content

Commit

Permalink
Closes #206: Ensure locked, not toggled for import. Add Tab behaviour…
Browse files Browse the repository at this point in the history
… to lock. Separated logic from main's url to frictionless for easier re-use. Added submenu items for both file and url. Improved style to suit open. Allowed title to change. Closes #742. Closes #731. Added Import Column Properties to file disable/enable list. Closes #924: Enabled difference between case-insensitive (with diacritics) and case-sensitive (without diacritics).

Signed-off-by: Matthew Mulholland <matt@redboxresearchdata.com.au>
  • Loading branch information
Matthew Mulholland committed Jun 30, 2019
1 parent d637218 commit 20b15fe
Show file tree
Hide file tree
Showing 19 changed files with 353 additions and 244 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@
"vue-async-computed": "^3.3.1",
"vue-directive-tooltip": "^1.4.5",
"vue-electron": "^1.0.6",
"vue-focus": "^2.1.0",
"vue-good-table": "^2.15.3",
"vue-router": "^3.0.1",
"vue-rx": "^5.0.0",
Expand Down
26 changes: 24 additions & 2 deletions src/main/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Fs from 'fs'
import { createWindowTabWithFormattedDataFile, focusMainWindow } from './windows'
import _ from 'lodash'
import { disableOpenFileItems, enableOpenFileItems } from './menuUtils.js'
import { loadResourceSchemaFromJson } from './loadFrictionless'

export function saveFileAs(format) {
let currentWindow = focusMainWindow()
Expand Down Expand Up @@ -43,7 +44,7 @@ export function saveFile() {
currentWindow.webContents.send('saveData', currentWindow.format, global.tab.activeFilename)
}

export function importDataPackage() {
export function importDataPackageFromFile() {
disableOpenFileItems()
let window = focusMainWindow()
Dialog.showOpenDialog({
Expand All @@ -62,7 +63,28 @@ export function importDataPackage() {
if (_.isArray(filename)) {
filename = filename[0]
}
window.webContents.send('importDataPackage', filename)
window.webContents.send('importDataPackageFromFile', filename)
})
}

export function importTableResourceSchemaFromFile() {
let window = focusMainWindow()
Dialog.showOpenDialog({
filters: [
{
name: '*',
extensions: ['json']
}
],
properties: ['openFile']
}, function(filename) {
if (filename === undefined) {
return
}
if (_.isArray(filename)) {
filename = filename[0]
}
loadResourceSchemaFromJson(filename)
})
}

Expand Down
115 changes: 115 additions & 0 deletions src/main/loadFrictionless.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// datapackage-js does not support loading url in browser
// atm, 'types' are only : Data Package or Resource Schema
import { focusMainWindow } from './windows'
import _ from 'lodash'
import { dataResourceToFormat } from '../renderer/file-formats'
import { dialog } from 'electron'
import { Package } from 'datapackage'
import { Schema } from 'tableschema'

export async function loadPackageFromJson (json) {
const mainWindow = focusMainWindow()
const dataPackageJson = await loadGenericFrictionlessFromJsonSource(json, loadPackageJson, 'Data Package')
try {
await loadResources(dataPackageJson, mainWindow)
} catch (error) {
console.error('There was a problem loading package from json', error)
}
}

export async function loadResourceSchemaFromJson (json) {
try {
const resourceSchema = await loadGenericFrictionlessFromJsonSource(json, loadTableResourceSchemaJson, 'Table Resource Schema')
const mainWindow = focusMainWindow()
mainWindow.webContents.send('closeLoadingScreen')
if (resourceSchema && resourceSchema.descriptor) {
const mainWindow = focusMainWindow()
mainWindow.webContents.send('addSchemaToTabAndLock', resourceSchema.descriptor)
}
} catch (error) {
console.error('There was a problem loading resource schema from json', error)
}
}

async function loadGenericFrictionlessFromJsonSource (jsonSource, callback, frictionlessType) {
const mainWindow = focusMainWindow()
mainWindow.webContents.send('closeAndshowLoadingScreen', `Loading ${frictionlessType}..`)
const frictionlessTypeJson = await callback(jsonSource, mainWindow)
if (!frictionlessTypeJson) {
dialog.showMessageBox(mainWindow, {
type: 'warning',
title: `Unable to load ${frictionlessType}`,
message:
`The ${frictionlessType}, ${jsonSource}, could not be loaded.
If the ${frictionlessType} is a URL or file, please check that it exists in JSON format.`
})
mainWindow.webContents.send('closeLoadingScreen')
return
}
if (!frictionlessTypeJson.valid) {
showInvalidMessage(jsonSource, mainWindow, frictionlessType)
mainWindow.webContents.send('closeLoadingScreen')
return
}
return frictionlessTypeJson
}

function showInvalidMessage (source, mainWindow, frictionlessType) {
dialog.showMessageBox(mainWindow, {
type: 'warning',
title: `Invalid ${frictionlessType}`,
message:
`The ${frictionlessType}, at ${source}, is not valid. Please refer to
https://frictionlessdata.io/specs/
for more information.`
})
}

export async function loadPackageJson (source) {
try {
const dataPackage = await Package.load(source)
return dataPackage
} catch (error) {
console.error(`There was a problem loading the package: ${source}`, error)
}
}

async function loadResources (dataPackageJson) {
const mainWindow = focusMainWindow()
let packageProperties = _.assign({}, dataPackageJson.descriptor)
_.unset(packageProperties, 'resources')
mainWindow.webContents.send('resetPackagePropertiesToObject', packageProperties)
for (const resource of dataPackageJson.resourceNames) {
mainWindow.webContents.send('closeAndshowLoadingScreen', 'Loading next resource...')
const dataResource = dataPackageJson.getResource(resource)
const format = dataResourceToFormat(dataResource.descriptor)
let data = await dataResource.read()
mainWindow.webContents.send('closeLoadingScreen')
// datapackage-js separates headers - add back to use default DC behaviour
let dataWithHeaders = _.concat([dataResource.headers], data)
mainWindow.webContents.send('addTabWithFormattedDataAndDescriptor', dataWithHeaders, format, dataResource.descriptor)
}
}

async function loadTableResourceSchemaJson (source) {
try {
const resourceSchema = await Schema.load(source)
return resourceSchema
} catch (error) {
console.error(`There was a problem loading the table resource schema: ${source}`, error)
}
}

export async function loadResourceDataFromPackageSource (source, resourceName) {
const dataPackage = await loadPackageJson(source)
const rowOfObjects = []
if (dataPackage && _.indexOf(dataPackage.resourceNames, resourceName) > -1) {
const dataResource = dataPackage.getResource(resourceName)
const data = await dataResource.read()
const headers = dataResource.headers
for (const row of data) {
rowOfObjects.push(_.zipObject(headers, row))
}
}
return rowOfObjects
}
22 changes: 16 additions & 6 deletions src/main/menu.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { openFile, saveFileAs, saveFile, importDataPackage } from './file.js'
import { openFile, saveFileAs, saveFile, importDataPackageFromFile, importTableResourceSchemaFromFile } from './file.js'
import { showUrlDialogForPackage, showUrlDialogForResourceSchema } from './url.js'
import { createWindowTab, focusMainWindow } from './windows.js'
import { importExcel } from './excel.js'
Expand Down Expand Up @@ -57,10 +57,20 @@ class AppMenu {
type: 'separator'
}, {
label: 'Import Column Properties',
accelerator: 'Shift+CmdOrCtrl+I',
click () {
showUrlDialogForResourceSchema()
}
submenu: [
{
label: 'json from URL...',
click () {
showUrlDialogForResourceSchema()
}
},
{
label: 'json from file...',
click () {
importTableResourceSchemaFromFile()
}
}
]
}, {
type: 'separator'
}, {
Expand Down Expand Up @@ -348,7 +358,7 @@ class AppMenu {
label: 'zip from file...',
enabled: true,
click () {
importDataPackage()
importDataPackageFromFile()
}
}, {
label: 'json from URL...',
Expand Down
4 changes: 2 additions & 2 deletions src/main/menuUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ export function getSubMenuLabelsFromMenu (menuLabel) {
}

export function disableOpenFileItems() {
disableMenuItems('File', ['Open Excel Sheet...', 'Open', 'Open Data Package'])
disableMenuItems('File', ['Open Excel Sheet...', 'Open', 'Open Data Package', 'Import Column Properties'])
}

export function disableMenuItems(menuLabel, subMenuLabels) {
applyFnsToLabelsFromMenuLabel(menuLabel, subMenuLabels, disableAllSubMenuItemsFromMenuObject, disableMenuItem)
}

export function enableOpenFileItems() {
enableMenuItems('File', ['Open Excel Sheet...', 'Open', 'Open Data Package'])
enableMenuItems('File', ['Open Excel Sheet...', 'Open', 'Open Data Package', 'Import Column Properties'])
}

export function enableMenuItems(menuLabel, subMenuLabels) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/rendererToMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
disableEnableBasedOnAttributeAndConditionFromLabels
} from './menuUtils.js'
import { focusMainWindow, closeSecondaryWindow } from './windows.js'
import { loadPackageJson, loadResourceDataFromPackageUrl } from './url.js'
import { loadPackageJson, loadResourceDataFromPackageSource } from './loadFrictionless'

ipc.on('toggleSaveMenu', (event, arg) => {
let saveSubMenu = getSubMenuFromMenu('File', 'Save')
Expand Down Expand Up @@ -78,7 +78,7 @@ ipc.on('loadPackageUrl', async function(event, index, hotId, url) {

ipc.on('loadPackageUrlResourcesAsFkRelations', async function(event, url, resourceName) {
try {
const rows = await loadResourceDataFromPackageUrl(url, resourceName)
const rows = await loadResourceDataFromPackageSource(url, resourceName)
event.returnValue = rows
} catch (error) {
const errorMessage = 'There was a problem collating data from url resources'
Expand Down
Loading

0 comments on commit 20b15fe

Please sign in to comment.