diff --git a/src/LexAccessors.tsx b/src/LexAccessors.tsx new file mode 100644 index 0000000..9930836 --- /dev/null +++ b/src/LexAccessors.tsx @@ -0,0 +1,229 @@ +import * as _ from 'lodash'; + +function simpleAccessor(lens: string, def: string = '') { + return { + get: (li: any) => { + let val = _.get(li, lens, def); + return val; + }, + set: (li: any, value: string) => { + let lexItem = _.cloneDeep(li); + let ret = _.set(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(li, lens, def('')); + let prop = _.filter(allProps, (m) => { + return pred(m); + }); + let mcls = _.get(prop, '[0]._', ''); + return mcls; + }, + set: (li: any, value: string) => { + let lexItem = _.cloneDeep(li); + let allProps = _.get(lexItem, lens, []); + if (allProps.length > 0) { + let prop = _.filter(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(li, lens, def); + let morphExps = _.filter(morphProps, (m) => { + return pred(m); + }); + let mEs = morphExps.map((me) => { + return _.get(me, '_', me); + }); + return mEs; + }, + set: (li: any, value: any) => { + let lexItem = _.cloneDeep(li); + let allProps = _.get(lexItem, lens, []); + if (allProps.length > 0) { + let keepProps = _.filter(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(li, lens, def); + let morphExps = _.filter(morphProps, (m) => { + return pred(m); + }); + let mEs = morphExps.map((me) => { + let value = _.get(me, '_', me); + let key = _.get(me, '$.' + attribKey, ''); + return { value, key }; + }); + return mEs; + }, + set: (li: any, value: any) => { + let lexItem = _.cloneDeep(li); + let allProps = _.get(lexItem, lens, []); + if (allProps.length > 0) { + let keepProps = _.filter(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(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('document.lexicon[0].item') + .flatMap((o: any) => _.chain(o) + .get('entry') + .map((p: any) => _.chain(p) + .get('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(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 + }); +}; diff --git a/src/LexEdit.tsx b/src/LexEdit.tsx index 95fdc75..6f6b76c 100644 --- a/src/LexEdit.tsx +++ b/src/LexEdit.tsx @@ -114,6 +114,8 @@ export class LexEdit extends React.Component { private handleOnSave(event: any) { this.props.save(this.state.lexItem, this.props.fieldMetaMap); - this.props.saveXMLBackend(); + if (!_.isEqual(this.state.lexItem, this.props.lexItem)) { + this.props.markDirty(); + } } } diff --git a/src/LexEditor.tsx b/src/LexEditor.tsx index 110dc56..61f09e0 100644 --- a/src/LexEditor.tsx +++ b/src/LexEditor.tsx @@ -4,6 +4,8 @@ import { LexEdit } from './LexEdit'; import { Input, Dropdown, + Container, + Header } from 'semantic-ui-react'; const { Flex } = require('reflexbox'); @@ -16,14 +18,14 @@ export class LexEditor extends React.Component { .filter((q: any) => searchMeta.get(q) === searchText) .take(10) .value(); - let { fieldMetaMap, save, saveXMLBackend } = this.props; + let { fieldMetaMap, save, markDirty } = this.props; return (
{ return { key: i, value: k, text: _.capitalize(k) }; }); return ( -
+ this.handleChange(d, true)} value={this.props.searchState.searchType} compact={true} selection={true} + style={{ width: '10em' }} /> { icon="filter" onChange={(e, d) => this.handleChange(d, false)} /> -
+ ); } @@ -73,10 +76,10 @@ function LexMatches(params: any) { const selectMode = (props: any) => { if (props.searchText === '') { return ( -
-

Empty

+ +
Empty
Type something in the searchbar. -
+ ); } else { let editEntries = props.matchedEntries.map((mObj: any) => { diff --git a/src/LexSetup.tsx b/src/LexSetup.tsx index 48fe60d..99b57e4 100644 --- a/src/LexSetup.tsx +++ b/src/LexSetup.tsx @@ -1,251 +1,26 @@ 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 _ from 'lodash'; +// import * as _ from 'lodash'; +import { xmlToEntries } from './LexAccessors'; import { LexEditor } from './LexEditor'; -function simpleAccessor(lens: string, def: string = '') { - return { - get: (li: any) => { - let val = _.get(li, lens, def); - return val; - }, - set: (li: any, value: string) => { - let lexItem = _.cloneDeep(li); - let ret = _.set(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(li, lens, def('')); - let prop = _.filter(allProps, (m) => { - return pred(m); - }); - let mcls = _.get(prop, '[0]._', ''); - return mcls; - }, - set: (li: any, value: string) => { - let lexItem = _.cloneDeep(li); - let allProps = _.get(lexItem, lens, []); - if (allProps.length > 0) { - let prop = _.filter(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(li, lens, def); - let morphExps = _.filter(morphProps, (m) => { - return pred(m); - }); - let mEs = morphExps.map((me) => { - return _.get(me, '_', me); - }); - return mEs; - }, - set: (li: any, value: any) => { - let lexItem = _.cloneDeep(li); - let allProps = _.get(lexItem, lens, []); - if (allProps.length > 0) { - let keepProps = _.filter(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(li, lens, def); - let morphExps = _.filter(morphProps, (m) => { - return pred(m); - }); - let mEs = morphExps.map((me) => { - let value = _.get(me, '_', me); - let key = _.get(me, '$.' + attribKey, ''); - return { value, key }; - }); - return mEs; - }, - set: (li: any, value: any) => { - let lexItem = _.cloneDeep(li); - let allProps = _.get(lexItem, lens, []); - if (allProps.length > 0) { - let keepProps = _.filter(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(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('document.lexicon[0].item') - .flatMap((o: any) => _.chain(o) - .get('entry') - .map((p: any) => _.chain(p) - .get('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(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 { xmlBuilder = new XML.Builder(); + fileName = 'new_es_orig.xml'; constructor(props: any) { super(props); - this.state = { xmlLoaded: false }; + this.state = { xmlLoaded: false, dirty: false }; } public componentDidMount() { - let fileName = '/new_es_orig.xml'; - fetch(fileName) + fetch(this.fileName) .then((response) => response.text()) .then((xmlString) => { XML.parseString(xmlString, (err, xmlData) => { - let props = this.props; - _.noop(props); + // let props = this.props; + // _.noop(props); this.props.addXmlData(xmlData); this.setState({ xmlLoaded: true }); }); @@ -261,28 +36,38 @@ export class LexSetup extends React.Component { Loading ); + let saveButton = this.state.dirty ? + () : null; let xmlEntries = xmlToEntries(this.props.xmlData); - let saveXMLBackend = () => { - this.setState({ xmlLoaded: false }); - let xmlText = this.xmlBuilder.buildObject(this.props.xmlData); - fetch('/api/save', { - method: 'POST', - body: xmlText - }) - .then((response) => response.text()) - .then((rsptext) => { - console.log('response', rsptext); - this.setState({ xmlLoaded: true }); - }) - .catch((e) => { - // console.log('errored :', e); - this.setState({ xmlLoaded: true }); - }); + // let saveXMLBackend = () => { + // this.setState({ xmlLoaded: false }); + // let xmlText = this.xmlBuilder.buildObject(this.props.xmlData); + // let data = new FormData(); + // data.append('file', xmlText); + // data.append('name', 'new_es_s.xml'); // this.fileName + // fetch('/api/save', { + // method: 'POST', + // body: data + // }) + // .then((response) => response.text()) + // .then((rsptext) => { + // console.log('response', rsptext); + // this.setState({ xmlLoaded: true }); + // }) + // .catch((e) => { + // // console.log('errored :', e); + // this.setState({ xmlLoaded: true }); + // }); + // }; + // _.noop(saveXMLBackend); + let markDirty = () => { + this.setState({ dirty: true }); }; let editor = (
-
+
+ {saveButton} Freespeech Lexicon Editor @@ -292,7 +77,7 @@ export class LexSetup extends React.Component {
); diff --git a/walle_server.py b/walle_server.py index cf8a37c..b48b7f6 100644 --- a/walle_server.py +++ b/walle_server.py @@ -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.wizard_helpers import get_morph_rule,get_frequency import json - +import codecs # from flask_cors import CORS, cross_origin # CORS(app) @@ -25,7 +25,11 @@ def walle_morph(): @app.route('/api/save',methods=['POST']) 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' # hmr streaming