refactored setup and added save button

master
Malar Kannan 2017-07-28 18:57:40 +05:30
parent a959b8a0a1
commit 707f151bdc
5 changed files with 286 additions and 263 deletions

229
src/LexAccessors.tsx Normal file
View File

@ -0,0 +1,229 @@
import * as _ from 'lodash';
function simpleAccessor(lens: string, def: string = '') {
return {
get: (li: any) => {
let val = _.get<any>(li, lens, def);
return val;
},
set: (li: any, value: string) => {
let lexItem = _.cloneDeep(li);
let ret = _.set<any>(lexItem, lens, value);
return ret;
}
};
}
function simpleAttrAccessor(attrPred: any) {
let { attribVal, attribKey, pred, lens } = attrPred;
let def = (value: any) => ({ _: value, $: { [attribKey]: attribVal } });
return {
get: (li: any) => {
let allProps = _.get<any>(li, lens, def(''));
let prop = _.filter<any>(allProps, (m) => {
return pred(m);
});
let mcls = _.get<any>(prop, '[0]._', '');
return mcls;
},
set: (li: any, value: string) => {
let lexItem = _.cloneDeep(li);
let allProps = _.get<any>(lexItem, lens, []);
if (allProps.length > 0) {
let prop = _.filter<any>(allProps, (m) => {
return pred(m);
});
_.set(prop, '[0]._', value);
} else {
_.set(lexItem, lens, [def(value)]);
}
return lexItem;
}
};
}
function listAttrAccessor(attrPred: any) {
let { pred, lens } = attrPred;
return {
get: (li: any) => {
let def: any = [];
let morphProps = _.get<any>(li, lens, def);
let morphExps = _.filter<any>(morphProps, (m) => {
return pred(m);
});
let mEs = morphExps.map((me) => {
return _.get<any>(me, '_', me);
});
return mEs;
},
set: (li: any, value: any) => {
let lexItem = _.cloneDeep(li);
let allProps = _.get<any>(lexItem, lens, []);
if (allProps.length > 0) {
let keepProps = _.filter<any>(allProps, (m) => {
return !pred(m);
});
_.set(lexItem, lens, _.concat(keepProps, value));
} else {
_.set(lexItem, lens, value);
}
return lexItem;
}
};
}
function propListAttrAccessor(attrPred: any) {
let { attribKey, pred, lens } = attrPred;
return {
get: (li: any) => {
let def: any = [];
let morphProps = _.get<any>(li, lens, def);
let morphExps = _.filter<any>(morphProps, (m) => {
return pred(m);
});
let mEs = morphExps.map((me) => {
let value = _.get<any>(me, '_', me);
let key = _.get<any>(me, '$.' + attribKey, '');
return { value, key };
});
return mEs;
},
set: (li: any, value: any) => {
let lexItem = _.cloneDeep(li);
let allProps = _.get<any>(lexItem, lens, []);
if (allProps.length > 0) {
let keepProps = _.filter<any>(allProps, (m) => {
return !pred(m);
});
_.set(lexItem, lens, _.concat(keepProps, value));
} else {
_.set(lexItem, lens, value);
}
return lexItem;
}
};
}
const attrPredGen = (lens: string, key: string, val: string, comp: any) => {
const pred = (m: any) => comp(_.get<any>(m, '$.' + key, ''), val);
return { lens, attribKey: key, attribVal: val, pred };
};
const fieldMetaMap = {
label: {
type: 'text',
...simpleAccessor('label[0]'),
},
unl: { type: 'text', ...simpleAccessor('unl[0]'), },
synset: { type: 'text', ...simpleAccessor('lexprops[0].wnsynset[0]'), },
guid: { type: 'text', ...simpleAccessor('guid[0]'), },
pos: { type: 'select', ...simpleAccessor('pos[0]'), },
image: { type: 'preview', ...simpleAccessor('image[0]'), },
relations: { type: 'text', ...simpleAccessor('relations[0]'), },
frame: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'syntacticprops[0].property',
'id',
'frame',
_.isEqual
)),
},
cat: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'uiprops[0].property',
'id',
'cat',
_.isEqual
)),
},
subcat: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'uiprops[0].property',
'id',
'subcat',
_.isEqual
)),
},
morphclass: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'lexprops[0].morphology[0].morph',
'form',
'morphclass',
_.isEqual
))
},
morphexceptions: {
type: 'proplist',
...propListAttrAccessor(attrPredGen(
'lexprops[0].morphology[0].morph',
'form',
'morphclass',
_.negate(_.isEqual)
))
},
stats: { type: 'text', ...simpleAccessor('stats[0].property[0]._'), },
lang: {
type: 'select',
options: ['en', 'es'], ...simpleAccessor('$.id', 'en'),
},
syntacticprops: {
type: 'list',
...listAttrAccessor(attrPredGen(
'syntacticprops[0].property',
'id',
'frame',
_.negate(_.isEqual)
))
},
groups: {
type: 'list',
...listAttrAccessor(attrPredGen(
'groups[0].property',
'id',
'frame',
_.negate(_.isEqual)
))
},
};
export const xmlToEntries = (xmlData: any) => {
let allEntries = _.chain(xmlData)
.get<any>('document.lexicon[0].item')
.flatMap((o: any) => _.chain(o)
.get<any>('entry')
.map((p: any) => _.chain(p)
.get<any>('lang[0]')
.set('guid[0]', o.$.guid)
.value())
.value()
)
.value();
let langReducer = ((result: any, q: any) => {
let lang = fieldMetaMap.lang.get(q);
(result[lang] || (result[lang] = [])).push(q);
return result;
});
let langEntries = _.reduce(allEntries, langReducer, {});
let langs = _.keys(langEntries);
let selectFields = _.fromPairs(langs.map((lang) => {
let langOpts = _.fromPairs(_.keys(fieldMetaMap).filter((s) => {
return _.includes(['select', 'list'], fieldMetaMap[s].type);
}).map((s) => {
let entries = _.get<any>(langEntries, lang, 'en');
let allOpts = entries.map((q: any) => {
return fieldMetaMap[s].get(q);
});
let select = _.isEqual(fieldMetaMap[s].type, 'select');
let selectOptions = select ? _.uniq(allOpts) : _.uniq(_.flatten(allOpts));
return [s, selectOptions];
}));
return [lang, langOpts];
}));
return ({
allEntries, selectFields, fieldMetaMap
});
};

View File

@ -114,6 +114,8 @@ export class LexEdit extends React.Component<any, any> {
private handleOnSave(event: any) { private handleOnSave(event: any) {
this.props.save(this.state.lexItem, this.props.fieldMetaMap); this.props.save(this.state.lexItem, this.props.fieldMetaMap);
this.props.saveXMLBackend(); if (!_.isEqual(this.state.lexItem, this.props.lexItem)) {
this.props.markDirty();
}
} }
} }

View File

@ -4,6 +4,8 @@ import { LexEdit } from './LexEdit';
import { import {
Input, Input,
Dropdown, Dropdown,
Container,
Header
} from 'semantic-ui-react'; } from 'semantic-ui-react';
const { Flex } = require('reflexbox'); const { Flex } = require('reflexbox');
@ -16,14 +18,14 @@ export class LexEditor extends React.Component<any, any> {
.filter((q: any) => searchMeta.get(q) === searchText) .filter((q: any) => searchMeta.get(q) === searchText)
.take(10) .take(10)
.value(); .value();
let { fieldMetaMap, save, saveXMLBackend } = this.props; let { fieldMetaMap, save, markDirty } = this.props;
return ( return (
<div> <div>
<LexSearch <LexSearch
{...this.props} {...this.props}
/> />
<LexMatches <LexMatches
{...{ fieldMetaMap, save, saveXMLBackend }} {...{ fieldMetaMap, save, markDirty }}
matchedEntries={matchedEntries} matchedEntries={matchedEntries}
selectionMeta={this.props.selectFields} selectionMeta={this.props.selectFields}
searchText={searchText} searchText={searchText}
@ -40,13 +42,14 @@ class LexSearch extends React.Component<any, any> {
return { key: i, value: k, text: _.capitalize(k) }; return { key: i, value: k, text: _.capitalize(k) };
}); });
return ( return (
<div> <Container textAlign="center">
<Dropdown <Dropdown
options={dropOptions} options={dropOptions}
onChange={(e, d) => this.handleChange(d, true)} onChange={(e, d) => this.handleChange(d, true)}
value={this.props.searchState.searchType} value={this.props.searchState.searchType}
compact={true} compact={true}
selection={true} selection={true}
style={{ width: '10em' }}
/> />
<Input <Input
type="text" type="text"
@ -56,7 +59,7 @@ class LexSearch extends React.Component<any, any> {
icon="filter" icon="filter"
onChange={(e, d) => this.handleChange(d, false)} onChange={(e, d) => this.handleChange(d, false)}
/> />
</div> </Container>
); );
} }
@ -73,10 +76,10 @@ function LexMatches(params: any) {
const selectMode = (props: any) => { const selectMode = (props: any) => {
if (props.searchText === '') { if (props.searchText === '') {
return ( return (
<div> <Container>
<h4>Empty</h4> <Header size="mini">Empty</Header>
Type something in the searchbar. Type something in the searchbar.
</div> </Container>
); );
} else { } else {
let editEntries = props.matchedEntries.map((mObj: any) => { let editEntries = props.matchedEntries.map((mObj: any) => {

View File

@ -1,251 +1,26 @@
import * as React from 'react'; import * as React from 'react';
import { Dimmer, Loader, Header, Icon, Segment } from 'semantic-ui-react'; import {
Dimmer, Loader, Header, Icon, Segment, Button
} from 'semantic-ui-react';
import * as XML from 'xml2js'; import * as XML from 'xml2js';
import * as _ from 'lodash'; // import * as _ from 'lodash';
import { xmlToEntries } from './LexAccessors';
import { LexEditor } from './LexEditor'; import { LexEditor } from './LexEditor';
function simpleAccessor(lens: string, def: string = '') {
return {
get: (li: any) => {
let val = _.get<any>(li, lens, def);
return val;
},
set: (li: any, value: string) => {
let lexItem = _.cloneDeep(li);
let ret = _.set<any>(lexItem, lens, value);
return ret;
}
};
}
function simpleAttrAccessor(attrPred: any) {
let { attribVal, attribKey, pred, lens } = attrPred;
let def = (value: any) => ({ _: value, $: { [attribKey]: attribVal } });
return {
get: (li: any) => {
let allProps = _.get<any>(li, lens, def(''));
let prop = _.filter<any>(allProps, (m) => {
return pred(m);
});
let mcls = _.get<any>(prop, '[0]._', '');
return mcls;
},
set: (li: any, value: string) => {
let lexItem = _.cloneDeep(li);
let allProps = _.get<any>(lexItem, lens, []);
if (allProps.length > 0) {
let prop = _.filter<any>(allProps, (m) => {
return pred(m);
});
_.set(prop, '[0]._', value);
} else {
_.set(lexItem, lens, [def(value)]);
}
return lexItem;
}
};
}
function listAttrAccessor(attrPred: any) {
let { pred, lens } = attrPred;
return {
get: (li: any) => {
let def: any = [];
let morphProps = _.get<any>(li, lens, def);
let morphExps = _.filter<any>(morphProps, (m) => {
return pred(m);
});
let mEs = morphExps.map((me) => {
return _.get<any>(me, '_', me);
});
return mEs;
},
set: (li: any, value: any) => {
let lexItem = _.cloneDeep(li);
let allProps = _.get<any>(lexItem, lens, []);
if (allProps.length > 0) {
let keepProps = _.filter<any>(allProps, (m) => {
return !pred(m);
});
_.set(lexItem, lens, _.concat(keepProps, value));
} else {
_.set(lexItem, lens, value);
}
return lexItem;
}
};
}
function propListAttrAccessor(attrPred: any) {
let { attribKey, pred, lens } = attrPred;
return {
get: (li: any) => {
let def: any = [];
let morphProps = _.get<any>(li, lens, def);
let morphExps = _.filter<any>(morphProps, (m) => {
return pred(m);
});
let mEs = morphExps.map((me) => {
let value = _.get<any>(me, '_', me);
let key = _.get<any>(me, '$.' + attribKey, '');
return { value, key };
});
return mEs;
},
set: (li: any, value: any) => {
let lexItem = _.cloneDeep(li);
let allProps = _.get<any>(lexItem, lens, []);
if (allProps.length > 0) {
let keepProps = _.filter<any>(allProps, (m) => {
return !pred(m);
});
_.set(lexItem, lens, _.concat(keepProps, value));
} else {
_.set(lexItem, lens, value);
}
return lexItem;
}
};
}
const attrPredGen = (lens: string, key: string, val: string, comp: any) => {
const pred = (m: any) => comp(_.get<any>(m, '$.' + key, ''), val);
return { lens, attribKey: key, attribVal: val, pred };
};
const fieldMetaMap = {
label: {
type: 'text',
...simpleAccessor('label[0]'),
},
unl: { type: 'text', ...simpleAccessor('unl[0]'), },
synset: { type: 'text', ...simpleAccessor('lexprops[0].wnsynset[0]'), },
guid: { type: 'text', ...simpleAccessor('guid[0]'), },
pos: { type: 'select', ...simpleAccessor('pos[0]'), },
image: { type: 'preview', ...simpleAccessor('image[0]'), },
relations: { type: 'text', ...simpleAccessor('relations[0]'), },
frame: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'syntacticprops[0].property',
'id',
'frame',
_.isEqual
)),
},
cat: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'uiprops[0].property',
'id',
'cat',
_.isEqual
)),
},
subcat: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'uiprops[0].property',
'id',
'subcat',
_.isEqual
)),
},
morphclass: {
type: 'select',
...simpleAttrAccessor(attrPredGen(
'lexprops[0].morphology[0].morph',
'form',
'morphclass',
_.isEqual
))
},
morphexceptions: {
type: 'proplist',
...propListAttrAccessor(attrPredGen(
'lexprops[0].morphology[0].morph',
'form',
'morphclass',
_.negate(_.isEqual)
))
},
stats: { type: 'text', ...simpleAccessor('stats[0].property[0]._'), },
lang: {
type: 'select',
options: ['en', 'es'], ...simpleAccessor('$.id', 'en'),
},
syntacticprops: {
type: 'list',
...listAttrAccessor(attrPredGen(
'syntacticprops[0].property',
'id',
'frame',
_.negate(_.isEqual)
))
},
groups: {
type: 'list',
...listAttrAccessor(attrPredGen(
'groups[0].property',
'id',
'frame',
_.negate(_.isEqual)
))
},
};
const xmlToEntries = (xmlData: any) => {
let allEntries = _.chain(xmlData)
.get<any>('document.lexicon[0].item')
.flatMap((o: any) => _.chain(o)
.get<any>('entry')
.map((p: any) => _.chain(p)
.get<any>('lang[0]')
.set('guid[0]', o.$.guid)
.value())
.value()
)
.value();
let langReducer = ((result: any, q: any) => {
let lang = fieldMetaMap.lang.get(q);
(result[lang] || (result[lang] = [])).push(q);
return result;
});
let langEntries = _.reduce(allEntries, langReducer, {});
let langs = _.keys(langEntries);
let selectFields = _.fromPairs(langs.map((lang) => {
let langOpts = _.fromPairs(_.keys(fieldMetaMap).filter((s) => {
return _.includes(['select', 'list'], fieldMetaMap[s].type);
}).map((s) => {
let entries = _.get<any>(langEntries, lang, 'en');
let allOpts = entries.map((q: any) => {
return fieldMetaMap[s].get(q);
});
let select = _.isEqual(fieldMetaMap[s].type, 'select');
let selectOptions = select ? _.uniq(allOpts) : _.uniq(_.flatten(allOpts));
return [s, selectOptions];
}));
return [lang, langOpts];
}));
return ({
allEntries, selectFields, fieldMetaMap
});
};
export class LexSetup extends React.Component<any, any> { export class LexSetup extends React.Component<any, any> {
xmlBuilder = new XML.Builder(); xmlBuilder = new XML.Builder();
fileName = 'new_es_orig.xml';
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.state = { xmlLoaded: false }; this.state = { xmlLoaded: false, dirty: false };
} }
public componentDidMount() { public componentDidMount() {
let fileName = '/new_es_orig.xml'; fetch(this.fileName)
fetch(fileName)
.then((response) => response.text()) .then((response) => response.text())
.then((xmlString) => { .then((xmlString) => {
XML.parseString(xmlString, (err, xmlData) => { XML.parseString(xmlString, (err, xmlData) => {
let props = this.props; // let props = this.props;
_.noop(props); // _.noop(props);
this.props.addXmlData(xmlData); this.props.addXmlData(xmlData);
this.setState({ xmlLoaded: true }); this.setState({ xmlLoaded: true });
}); });
@ -261,28 +36,38 @@ export class LexSetup extends React.Component<any, any> {
<Loader inverted={true}>Loading</Loader> <Loader inverted={true}>Loading</Loader>
</Dimmer> </Dimmer>
); );
let saveButton = this.state.dirty ?
(<Button floated="right" size="mini">Save</Button>) : null;
let xmlEntries = xmlToEntries(this.props.xmlData); let xmlEntries = xmlToEntries(this.props.xmlData);
let saveXMLBackend = () => { // let saveXMLBackend = () => {
this.setState({ xmlLoaded: false }); // this.setState({ xmlLoaded: false });
let xmlText = this.xmlBuilder.buildObject(this.props.xmlData); // let xmlText = this.xmlBuilder.buildObject(this.props.xmlData);
fetch('/api/save', { // let data = new FormData();
method: 'POST', // data.append('file', xmlText);
body: xmlText // data.append('name', 'new_es_s.xml'); // this.fileName
}) // fetch('/api/save', {
.then((response) => response.text()) // method: 'POST',
.then((rsptext) => { // body: data
console.log('response', rsptext); // })
this.setState({ xmlLoaded: true }); // .then((response) => response.text())
}) // .then((rsptext) => {
.catch((e) => { // console.log('response', rsptext);
// console.log('errored :', e); // this.setState({ xmlLoaded: true });
this.setState({ xmlLoaded: true }); // })
}); // .catch((e) => {
// // console.log('errored :', e);
// this.setState({ xmlLoaded: true });
// });
// };
// _.noop(saveXMLBackend);
let markDirty = () => {
this.setState({ dirty: true });
}; };
let editor = ( let editor = (
<div> <div>
<Segment inverted={true} size="tiny" attached={true}> <Segment inverted={true} size="tiny" attached={true}>
<Header inverted={true} color="teal" size="mini"> <Header color="teal" size="mini">
{saveButton}
<Icon name="edit" size="small" /> <Icon name="edit" size="small" />
<Header.Content> <Header.Content>
Freespeech Lexicon Editor Freespeech Lexicon Editor
@ -292,7 +77,7 @@ export class LexSetup extends React.Component<any, any> {
<LexEditor <LexEditor
{...xmlEntries} {...xmlEntries}
{...this.props} {...this.props}
saveXMLBackend={saveXMLBackend} markDirty={markDirty}
/> />
</div> </div>
); );

View File

@ -3,7 +3,7 @@ app = Flask(__name__,static_url_path='',static_folder='build')
from freespeech_walle.get_morph_rule import get_morph from freespeech_walle.get_morph_rule import get_morph
# from freespeech_walle.wizard_helpers import get_morph_rule,get_frequency # from freespeech_walle.wizard_helpers import get_morph_rule,get_frequency
import json import json
import codecs
# from flask_cors import CORS, cross_origin # from flask_cors import CORS, cross_origin
# CORS(app) # CORS(app)
@ -25,7 +25,11 @@ def walle_morph():
@app.route('/api/save',methods=['POST']) @app.route('/api/save',methods=['POST'])
def walle_save(): def walle_save():
print request.form xmlData = request.form.get('file',None);
xmlFileName = request.form.get('name','new_es_saved.xml');
if xmlData:
with codecs.open('build/'+xmlFileName,'wb','utf-8') as xmlF:
xmlF.write(xmlData);
return 'ok' return 'ok'
# hmr streaming # hmr streaming