9 Commits

Author SHA1 Message Date
efarooqui
b5d922ef16 Updated view with validation button 2020-08-21 09:38:23 -07:00
efarooqui
1971128b18 Added validation buttons per row in datasheet UI; need to configure correct validations per data field 2020-08-21 09:35:20 -07:00
efarooqui
fba74d8e9c Wrote validation functions and getter to grab appropriate validation function 2020-08-11 21:40:34 -07:00
Lachlan Kermode
f3115007e2 Merge pull request #52 from forensic-architecture/dependabot/npm_and_yarn/lodash-4.17.19
Bump lodash from 4.17.15 to 4.17.19
2020-07-21 09:57:07 +02:00
Lachlan Kermode
70149b905f Merge pull request #51 from christophknoth/patch-1
Fixed internal anchor link headline
2020-07-21 09:56:48 +02:00
dependabot[bot]
afa52bffb6 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-21 04:11:55 +00:00
Lachlan Kermode
533ab6e6f9 update fmt 2020-07-15 09:58:56 +02:00
Christoph Knoth
d06f4a5b68 Fixed internal anchor link headline 2020-06-30 12:30:30 +02:00
Lachlan Kermode
95cb7a6f80 remove explicit ID from events in timemap setup 2020-06-19 12:07:57 +02:00
15 changed files with 952 additions and 1151 deletions

1
.gitignore vendored
View File

@@ -14,4 +14,3 @@ tags
tags.lock tags.lock
tags.temp tags.temp
src/config.js src/config.js
src/local.config.js

Binary file not shown.

1958
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,15 +26,16 @@
"express": "^4.13.3", "express": "^4.13.3",
"express-graphql": "^0.6.12", "express-graphql": "^0.6.12",
"express-handlebars": "^4.0.4", "express-handlebars": "^4.0.4",
"googleapis": "^39.1.0", "googleapis": "^32.0.0",
"graphql": "^0.13.2", "graphql": "^0.13.2",
"moment": "^2.27.0",
"morgan": "^1.8.0", "morgan": "^1.8.0",
"mz": "^2.7.0", "mz": "^2.7.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.3.0",
"node-xlsx": "^0.15.0",
"object-hash": "^1.3.0", "object-hash": "^1.3.0",
"ramda": "^0.25.0", "ramda": "^0.25.0",
"resource-router-middleware": "^0.6.0", "resource-router-middleware": "^0.6.0"
"xlsx": "^0.16.8"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.1.2", "@babel/cli": "^7.1.2",

View File

@@ -58,6 +58,17 @@ export default ({ config, controller }) => {
) )
}) })
// api.get('/:sheet/:tab/:resource/validate', (req, res) => {
// const { sheet, tab, resource } = req.params
// controller
// .retrieve(sheet, tab, resource)
// .then(data => res.json(data))
// .catch(err =>
// res.status(err.status || 404)
// .send({ error: err.message })
// )
// })
// ERROR routes. Note that it is important that these come AFTER routes // ERROR routes. Note that it is important that these come AFTER routes
// like /update, so that the regex does not greedily match these routes. // like /update, so that the regex does not greedily match these routes.

View File

@@ -1,4 +1,5 @@
import { fmtObj } from '../lib/util' import { fmtObj } from '../lib/util'
import { getColumnValidation } from '../lib/validation'
/** /**
* Each resource item is an object with values labelled according * Each resource item is an object with values labelled according
@@ -35,7 +36,6 @@ export default (data) => {
} }
} }
}) })
// generate the value for deep labels using the structure created // generate the value for deep labels using the structure created
data.forEach((row, idx) => { data.forEach((row, idx) => {
if (idx === 0) return if (idx === 0) return
@@ -61,12 +61,11 @@ export default (data) => {
// move values for flat labels over from base // move values for flat labels over from base
structure.__flat.forEach(label => { structure.__flat.forEach(label => {
const validatedLabel = getColumnValidation(label, baseRow[label])
console.log(validatedLabel)
deepRow[label] = baseRow[label] deepRow[label] = baseRow[label]
}) })
if (!Object.keys(deepRow).every(k => ( if (!Object.keys(deepRow).every(k => deepRow[k] === '')) {
(deepRow[k] === '') ||
(Array.isArray(deepRow[k]) && deepRow[k].length === 0)
))) {
output.push(deepRow) output.push(deepRow)
} }
}) })

View File

@@ -1,7 +1,13 @@
import { timemap } from './lib' import { timemap } from './lib'
export default { export default {
gsheets: [], gsheets: [
{
name: 'us2020',
id: '1I_pgyTQJlIorTIEHBxw1mM1STn-SrIi66FKYxut61iM',
tabs: timemap.default
}
],
xlsx: [ xlsx: [
{ {
name: 'timemap_data', name: 'timemap_data',

View File

@@ -1,5 +1,4 @@
import http from 'http' import http from 'http'
import path from 'path'
import express from 'express' import express from 'express'
import initialize from './initialize' import initialize from './initialize'
import middleware from './middleware' import middleware from './middleware'
@@ -41,7 +40,7 @@ initialize(controller => {
}) })
) )
app.use(express.static(path.join(__dirname, 'public'))) app.use(express.static(__dirname + '/public'))
app.server.listen(process.env.PORT || 4040, () => { app.server.listen(process.env.PORT || 4040, () => {
console.log('===========================================') console.log('===========================================')

View File

@@ -1,6 +1,7 @@
import StoreJson from './models/StoreJson' import StoreJson from './models/StoreJson'
import fetchers from './lib/Fetcher' import fetchers from './lib/Fetcher'
import Controller from './lib/Controller' import Controller from './lib/Controller'
import config from './config'
import R from 'ramda' import R from 'ramda'
const isntNull = n => n !== null const isntNull = n => n !== null
@@ -8,13 +9,6 @@ const filterNull = ls => R.filter(isntNull, ls)
const flattenfilterNull = ls => filterNull(R.flatten(ls)) const flattenfilterNull = ls => filterNull(R.flatten(ls))
let themFetchers let themFetchers
let config
try {
config = require('./local.config.js').default
} catch (_) {
config = require('./config.js').default
}
export default callback => { export default callback => {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
return Object.keys(config).map(fType => { return Object.keys(config).map(fType => {

View File

@@ -5,7 +5,9 @@ function prefixedTabs (prefix, cfg) {
const prf = key => cfg[key] ? `${prefix}_` : '' const prf = key => cfg[key] ? `${prefix}_` : ''
return { return {
[`${prf('events')}export_events`]: BP.deeprows, [`${prf('events')}export_events`]: BP.deeprows,
[`${prf('associations')}export_associations`]: BP.deeprows, [`${prf('categories')}export_categories`]: [BP.groups, BP.rows],
[`${prf('filters')}export_filters`]: BP.tree,
[`${prf('narratives')}export_narratives`]: BP.rows,
[`${prf('sources')}export_sources`]: BP.deepids, [`${prf('sources')}export_sources`]: BP.deepids,
[`${prf('sites')}export_sites`]: BP.rows [`${prf('sites')}export_sites`]: BP.rows
} }

View File

@@ -1,16 +1,15 @@
import R from 'ramda' // FetcherTwo class interfaces with Google Sheet, and saves to a specified db
import { createHash } from 'crypto' import { google } from 'googleapis'
import { buildDesaturated } from './blueprinters' import { buildDesaturated } from './blueprinters'
import { import {
fmtName, fmtName,
fmtBlueprinterTitles, fmtBlueprinterTitles,
isFunction isFunction
} from './util' } from './util'
import { createHash } from 'crypto'
/* GsheetFetcher deps */ import R from 'ramda'
import { google } from 'googleapis' import xlsx from 'node-xlsx'
/* LocalFetcher deps */ import fs from 'fs'
import X from 'xlsx'
class Fetcher { class Fetcher {
constructor (db, name, bps) { constructor (db, name, bps) {
@@ -139,6 +138,7 @@ class Fetcher {
/** Run on startup. Should be overridden if explicit auth is required **/ /** Run on startup. Should be overridden if explicit auth is required **/
authenticate (env) { authenticate (env) {
console.log(`Connected to ${this.sheetName}. No explicit authentication required for ${this.type}s.`)
return Promise.resolve(this) return Promise.resolve(this)
} }
} }
@@ -221,22 +221,27 @@ class GsheetFetcher extends Fetcher {
} }
} }
class LocalFetcher extends Fetcher { class XlsxFetcher extends Fetcher {
constructor (db, name, bps, path) { constructor (db, name, bps, path) {
super(db, name, bps) super(db, name, bps)
this.type = 'XLSX File'
this.path = path this.path = path
this.update().then(res => this.isRemote = false
console.log(`${res ? 'Successful' : 'Couldn\'t'} update ${name}`)
) if (this.path.startsWith('https')) {
this.isRemote = true
}
} }
update () { update () {
const wb = X.readFile(this.path) const data = xlsx.parse(fs.readFileSync(this.path))
wb.SheetNames.forEach(name => { data.forEach(tab => {
const sh = wb.Sheets[name] const stringyData = tab.data.map(row =>
const csv = X.utils.sheet_to_csv(sh, { FS: '\t' }) row.map(d =>
const ll = csv.split('\n').map(line => line.split('\t')) typeof (d) === 'number' ? d.toString() : d
this.save(name, ll) )
)
this.save(tab.name, stringyData)
}) })
return Promise.resolve(true) return Promise.resolve(true)
} }
@@ -244,7 +249,5 @@ class LocalFetcher extends Fetcher {
export default { export default {
'gsheets': GsheetFetcher, 'gsheets': GsheetFetcher,
'xlsx': LocalFetcher, 'xlsx': XlsxFetcher
'ods': LocalFetcher,
'local': LocalFetcher
} }

View File

@@ -1,4 +1,4 @@
import R from 'ramda' import R from 'ramda';
/* eslint-disable */ /* eslint-disable */
String.prototype.replaceAll = function (search, replacement) { String.prototype.replaceAll = function (search, replacement) {

20
src/lib/validation.js Normal file
View File

@@ -0,0 +1,20 @@
import moment from 'moment';
const DATE_FORMAT = "MM/DD/YYYY";
const TIME_REGEX = new RegExp("^([01]\d|2[0-3]):?([0-5]\d)$")
export const getColumnValidation = (colName, value) => {
switch(colName) {
case 'longitude':
return isFinite(value) && Math.abs(value) <= 180;
case 'latitude':
return isFinite(value) && Math.abs(value) <= 90;
case 'date':
return moment(value, DATE_FORMAT, true).isValid();
case 'time':
return TIME_REGEX.test(value);
default:
return true
}
}

View File

@@ -54,7 +54,14 @@ test('should launch', t => {
}) })
const passUrls = [ const passUrls = [
// /
'/api/', '/api/',
// /blueprints
'/api/blueprints',
// /:sheet/:tab/:resource
'/api/example/export_events/rows',
// /:sheet/:tab/:resource/:frag
'/api/example/export_events/rows/1'
] ]
const failUrls = [ const failUrls = [
@@ -70,7 +77,6 @@ passUrls.forEach(function (url) {
.then(checkStatus) .then(checkStatus)
.then(res => res.json()) .then(res => res.json())
.then(json => { .then(json => {
console.info('JSON: ', json)
t.pass() t.pass()
}) })
}) })

View File

@@ -9,6 +9,7 @@
</div> </div>
{{#each urls}} {{#each urls}}
<div><a target="_blank" href="http://localhost:4040{{ this }}">{{ this }}</a></div> <div><a target="_blank" href="http://localhost:4040{{ this }}">{{ this }}</a></div>
<div class="bp-validate-button" target="_blank" href="http://localhost:4040{{ this }}">Validate</div>
{{/each}} {{/each}}
</div> </div>
{{ else }} {{ else }}
@@ -25,6 +26,7 @@
:root { :root {
--grey: #8a8a8a; --grey: #8a8a8a;
--btn-width: 200px; --btn-width: 200px;
--validate-btn-width: 150px;
} }
.main-container { .main-container {
display: flex; display: flex;
@@ -79,6 +81,21 @@
color: black; color: black;
} }
.bp-validate-button {
transition: all 0.3s ease;
text-transform: uppercase;
font-size: 12pt;
font-weight: bold;
width: var(--validate-btn-width);
display: flex;
justify-content: center;
align-items: center;
border: 3px solid red;
padding: .5em;
text-decoration: none;
color: red;
}
.bp-update-container:hover { .bp-update-container:hover {
background-color: black; background-color: black;
transition: all 0.3s ease; transition: all 0.3s ease;