Changeset 7221


Ignore:
Timestamp:
10/21/18 18:24:09 (6 years ago)
Author:
Leif-Jöran
Message:

Updates to aggreagtor page.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • SRUAggregator/trunk/src/main/resources/assets/js/pages/aggregatorpage.jsx

    r7148 r7221  
    1717var AggregatorPage = createReactClass({
    1818// fixme! - class AggregatorPage extends React.Component {
    19         propTypes: {
    20                 ajax: PT.func.isRequired,
    21                 error: PT.func.isRequired,
    22                 embedded: PT.bool.isRequired
    23         },
     19    propTypes: {
     20        ajax: PT.func.isRequired,
     21        error: PT.func.isRequired,
     22        embedded: PT.bool.isRequired
     23    },
    2424
    2525        nohits: {
     
    2929
    3030        getInitialState: function () {
     31            var aggrContext = getQueryVariable('x-aggregation-context');
     32            aggrContext = aggrContext && JSON.parse(aggrContext);
     33           
    3134                return {
    3235                        corpora: new Corpora([], this.updateCorpora),
    3336                        languageMap: {},
    3437                        weblichtLanguages: [],
    35                         queryTypeId: getQueryVariable('queryType') || 'cql',
    36                         query: getQueryVariable('query') || '',
    37                         aggregationContext: getQueryVariable('x-aggregation-context') || '',
     38            queryTypeId: getQueryVariable('queryType') || 'cql',
     39                        cqlQuery: ((getQueryVariable('queryType') || 'cql') === 'cql') && getQueryVariable('query') || '',
     40                        fcsQuery: ((getQueryVariable('queryType') || 'cql') === 'fcs') && getQueryVariable('query') || '',
     41                        aggregationContext: aggrContext || null,
    3842                        language: this.anyLanguage,
    3943                        languageFilter: 'byMeta',
     
    4549
    4650                        zoomedCorpusHit: null,
    47                         _isMounted: false
    4851                };
    4952        },
    5053
    5154        componentDidMount: function() {
    52                 this.setState({_isMounted: true});
     55            this._isMounted = true;
    5356
    5457                this.props.ajax({
    5558                        url: 'rest/init',
    5659                        success: function(json, textStatus, jqXHR) {
    57                                 if (this.state._isMounted) {
     60                                if (this._isMounted) {
    5861                                        var corpora = new Corpora(json.corpora, this.updateCorpora);
    59                                         window.MyAggregator.corpora = json.corpora;
    60                                         this.setState({
    61                                                 corpora : corpora,
    62                                                 languageMap: json.languages,
    63                                                 weblichtLanguages: json.weblichtLanguages,
    64                                                 query: this.state.query || json.query || '',
    65                                         });
     62                                       
    6663                                        // // for testing aggregation context
    6764                                        // json['x-aggregation-context'] = {
     
    6966                                        // };
    7067
    71                                     if (this.state.aggregationContext && !json['x-aggregation-context']) {
    72                                         json['x-aggregation-context'] = JSON.parse(this.state.aggregationContext);
    73                                         console.log(json['x-aggregation-context']);
    74                                     }
    75                                     if (json['x-aggregation-context']) {
    76                                         window.MyAggregator.xAggregationContext = json["x-aggregation-context"];
    77                                         corpora.setAggregationContext(json["x-aggregation-context"]);
    78                                                 if (!corpora.getSelectedIds().length) {
    79                                                         this.props.error("Cannot find the required collection, will search all collections instead");
    80                                                         corpora.recurse(function(corpus) { corpus.selected = true; });
    81                                                 }
    82                                                 corpora.update();
    83                                         }
     68                    var aggregationContext = json['x-aggregation-context'] || this.state.aggregationContext;
     69                                   
     70                                    window.MyAggregator.mode = getQueryVariable('mode') || json.mode;
     71                                    window.MyAggregator.corpora = json.corpora;
     72                        window.MyAggregator.xAggregationContext = aggregationContext;
     73                                       
    8474                                    // Setting visibility, e.g. only corpora
    85                                     // from v2.0 endpoints for fcs v2.0
    86                                     this.state.corpora.setVisibility(this.state.queryTypeId, this.state.language[0]);
    87                                     corpora.update();
    88 
    89                                         if (getQueryVariable('mode') === 'search' ||
    90                                                 json.mode === 'search') {
    91                                                         window.MyAggregator.mode = 'search';
    92                                                         this.search();
    93                                         }
     75                    // from v2.0 endpoints for fcs v2.0
     76                    corpora.setVisibility(this.state.queryTypeId, this.state.language[0]);
     77
     78                    if (aggregationContext) {
     79                        const contextCorporaInfo = corpora.setAggregationContext(aggregationContext);
     80                        const unavailableCorporaHandles = contextCorporaInfo.unavailable; // list of unavailable aggregationContext
     81                        if (unavailableCorporaHandles.length > 0) {
     82                            this.props.error("Could not find requested collection handles:\n" + unavailableCorporaHandles.join('\n'));
     83                        }
     84                   
     85                        const actuallySelectedCorpora = corpora.getSelectedIds();
     86                       
     87                        if (contextCorporaInfo.selected.length !== actuallySelectedCorpora.length) {
     88                            if (actuallySelectedCorpora.length === 0) {
     89                                this.props.error("This search does not support the required collection(s), will search all collections instead"); // TODO give detailed reason its not supported.
     90                                corpora.recurse(function(corpus) { corpus.selected = true; });
     91                            } else {
     92                                var err = "Some required context collections are not supported for this search:\n"
     93                                err = err + contextCorpora.filter((c) => {
     94                                    if (actuallySelectedCorpora.indexOf(c) === -1) {
     95                                        console.warn("Requested corpus but not available for selection", c);
     96                                        return true;
     97                                    }
     98                                    return false;
     99                                }).map((c) => c.title).join('\n')
     100                                this.props.error(err);
     101                            }
     102                        }
     103                    }
     104                    else {
     105                        // no context set all visibl to selected as default.
     106                        console.log("no context set, selecting all available");
     107                        corpora.recurse(c => {c.visible ? c.selected=true : null})
     108                    }
     109                   
     110                    this.setState({
     111                            corpora : corpora,
     112                            languageMap: json.languages,
     113                            weblichtLanguages: json.weblichtLanguages,
     114                            aggregationContext: aggregationContext,
     115                        }, this.postInit);
     116                                }
     117                                else {
     118                                    console.warn("Got Aggregator init response, but not mounted!");
    94119                                }
    95120                        }.bind(this),
    96121                });
    97122        },
     123       
     124        postInit() {
     125             if (window.MyAggregator.mode === 'search') {
     126            this.search();
     127        }
     128        },
    98129
    99130        updateCorpora: function(corpora) {
    100131                this.setState({corpora:corpora});
    101132        },
    102 
    103         search: function() {
    104                 var query = this.state.query;
     133       
     134        getCurrentQuery() {
     135            return  this.state.queryTypeId === 'fcs' ? this.state.fcsQuery : this.state.cqlQuery;
     136        },
     137
     138        search() {
     139                var query = this.getCurrentQuery();
    105140                var queryTypeId = this.state.queryTypeId;
    106                 if (!query || this.props.embedded) {
     141                if (!query || (this.props.embedded && window.MyAggregator.mode !== 'search')) {
    107142                        this.setState({ hits: this.nohits, searchId: null });
    108143                        return;
     
    120155                        type: "POST",
    121156                        data: {
    122                                 query: query,
    123                                 queryType: queryTypeId,
     157                        query: query,
     158                queryType: queryTypeId,
    124159                                language: this.state.language[0],
    125160                                numberOfResults: this.state.numberOfResults,
     
    142177                });
    143178        },
     179       
    144180        nextResults: function(corpusId) {
    145181                // console.log("searching next results in corpus:", corpusId);
     
    161197
    162198        refreshSearchResults: function() {
    163                 if (!this.state.searchId || !this.state._isMounted) {
     199                if (!this.state.searchId || !this._isMounted) {
    164200                        return;
    165201                }
     
    225261        setQueryType: function(queryTypeId) {
    226262                this.state.corpora.setVisibility(queryTypeId, this.state.language[0]);
     263                setQueryVariable('queryType', queryTypeId);
     264                setQueryVariable('query', queryTypeId === 'cql' ? this.state.cqlQuery : this.state.fcsQuery)
    227265                this.setState({
    228266                        queryTypeId: queryTypeId,
    229267                        hits: this.nohits,
    230268                        searchId: null,
    231                         displayADV: queryTypeId == "fcs" ? true : false,
    232                         corpora: this.state.corpora, // === this.state.corpora.update();
    233269                });
    234270        },
     
    308344        },
    309345
    310         onQuery: function(event) {
    311                 this.setState({query: event.target.value});
    312         },
    313 
    314         onADVQuery: function(fcsql) {
    315             this.setState({query: fcsql.target.value});
     346        onQueryChange: function(queryStr) {
     347            if (this.state.queryTypeId === 'cql') {
     348                this.setState({
     349                        cqlQuery: queryStr || '',
     350                    });
     351            } else {
     352                this.setState({
     353                        fcsQuery: queryStr || '',
     354                    });
     355            }
     356                setQueryVariable('query', queryStr);
    316357        },
    317358
     
    320361                        this.search();
    321362                }
    322         },
    323 
    324         handleADVKey: function(event) {
    325             if (event.keyCode==13) {
    326                 this.addADVToken();
    327             }
    328363        },
    329364
     
    343378        renderSearchButtonOrLink: function() {
    344379                if (this.props.embedded) {
    345                         var query = this.state.query;
     380                        var query = this.getCurrentQuery();
    346381                        var queryTypeId = this.state.queryTypeId;
    347                         var btnClass = classNames({
     382                var btnClass = classNames({
    348383                            'btn': true,
    349384                            'btn-default': queryTypeId === 'cql',
     385                            'btn-primary': true,
    350386                            'input-lg': true
    351387                        });
     
    364400                );
    365401        },
     402       
     403       
     404        renderQueryInput() {
     405            var queryType = queryTypeMap[this.state.queryTypeId];
     406            return (
     407                <QueryInput
     408                            searchedLanguages={this.state.searchedLanguages || [multipleLanguageCode]}
     409                            corpora={this.props.corpora}
     410                            queryTypeId={this.state.queryTypeId}
     411                            query={this.getCurrentQuery()===undefined ? queryType.searchPlaceholder : this.getCurrentQuery()}
     412                            embedded={this.props.embedded}
     413                            placeholder={queryType.searchPlaceholder}
     414                onQueryChange={this.onQueryChange}
     415                            onKeyDown={this.handleKey} />
     416                );
     417        },
     418       
     419        renderEmbed () {
     420            var queryType = queryTypeMap[this.state.queryTypeId];
     421           
     422            return <div className="aligncenter" style={{marginLeft:16, marginRight:16}}>
     423                <div className={"input-group"}>
     424                            <span className="input-group-addon" style={{backgroundColor:queryType.searchLabelBkColor}}>
     425                                    {queryType.searchLabel}
     426                            </span>
     427                       
     428                { this.renderQueryInput() }
     429                           
     430                <div className="input-group-btn">
     431                                    {this.renderSearchButtonOrLink()}
     432                            </div>
     433                    </div>
     434                </div>
     435        },
     436       
     437        renderGQB () {
     438            var queryType = queryTypeMap[this.state.queryTypeId];
     439           
     440            return <div style={{marginLeft:16, marginRight:16}}>
     441                <div className="panel panel-default">
     442                            <div className="panel-heading" style={{backgroundColor:queryType.searchLabelBkColor, fontSize: "120%"}}>
     443                                    {queryType.searchLabel}
     444                            </div>
     445                           
     446                            <div className="panel-body">
     447                                { this.renderQueryInput() }
     448                             </div>
     449                         
     450                <div className="panel-footer">
     451                    <div className="input-group">
     452                                   
     453                                    <pre className="adv-query-preview aligncenter input-control input-lg">{this.getCurrentQuery()}</pre>
     454                                   
     455                                        <div className="input-group-btn">
     456                                            {this.renderSearchButtonOrLink()}
     457                                        </div>
     458                                    </div>
     459                            </div>
     460                    </div>
     461                  </div>
     462        },
     463       
     464        renderUnavailableCorporaMessage() {
     465            if (!this.state.corpora) {
     466                return;
     467            }
     468        const unavailable = [];
     469        this.state.corpora.recurse((c) => {
     470            if (c.selected && ! c.visible) {
     471                unavailable.push(c);
     472            }
     473            if (c.selected) {
     474                // apparently a selected corpus
     475            }
     476        });
     477       
     478        if (unavailable.length) {
     479            return <div id="unavailable-corpora-message" className="text-muted">
     480                <div id="unavailable-corpora-message-message">
     481                    <a role="button" data-toggle="dropdown">{unavailable.length} selected collection{unavailable.length > 1 ? 's are' : ' is'} disabled in this search mode.</a>
     482                </div>
     483                <ul id="unavailable-corpora-message-list" className="dropdown-menu">
     484                                {
     485                                    unavailable.map((c) => <li className="unavailable-corpora-message-item">{c.name}</li>)
     486                                }
     487                                </ul>
     488            </div>
     489        }
     490        },
    366491
    367492        render: function() {
     493             
    368494                var queryType = queryTypeMap[this.state.queryTypeId];
    369495                return  (
    370496                        <div className="top-gap">
    371497                                <div className="row">
    372                                         <div className="aligncenter" style={{marginLeft:16, marginRight:16}}>
    373                                                 <div className="input-group">
    374                                                         <span className="input-group-addon" style={{backgroundColor:queryType.searchLabelBkColor}}>
    375                                                                 {queryType.searchLabel}
    376                                                         </span>
    377                                                         <QueryInput
    378                                                             searchedLanguages={this.state.searchedLanguages || [multipleLanguageCode]}
    379                                                             queryTypeId={this.state.queryTypeId}
    380                                                             query={this.state.query}
    381                                                             embedded={this.props.embedded}
    382                                                             placeholder={queryType.searchPlaceholder}
    383                                                             onChange={this.onADVQuery}
    384                                                             onQuery={this.onQuery}
    385                                                             onKeyDown={this.handleKey} />
    386 
    387                                                         <div className="input-group-btn">
    388                                                                 {this.renderSearchButtonOrLink()}
    389                                                         </div>
    390                                                 </div>
    391                                         </div>
     498                                        { (!this.props.embedded && this.state.queryTypeId == "fcs") ? this.renderGQB() : this.renderEmbed() }
    392499                                </div>
    393500
    394                                 <div className="wel" style={{marginTop:20}}>
     501                                <div className="well" style={{marginTop:20}}>
    395502                                        <div className="aligncenter" >
     503                                        {
     504                                            //this.renderUnavailableCorporaMessage()
     505                                        }
    396506                                                <form className="form-inline" role="form">
    397507
     
    448558                                </div>
    449559
    450                                 <Modal ref="corporaModal" title={<span>Collections</span>}>
     560                                <Modal ref="corporaModal" title={<span>Collections <small className="text-muted">{this.props.corpora && this.props.corpora.getSelectedMessage()}</small></span>}>
    451561                                        <CorpusView corpora={this.state.corpora} languageMap={this.state.languageMap} />
    452562                                </Modal>
     
    504614        this.recurse(function(corpus, index) {
    505615                corpus.visible = true; // visible in the corpus view
    506                 corpus.selected = true; // selected in the corpus view
     616                corpus.selected = false; // not selected in the corpus view, assign later
    507617                corpus.expanded = false; // not expanded in the corpus view
    508618                corpus.priority = 1; // used for ordering search results in corpus view
     
    586696        this.corpora.forEach(selectSubTree.bind(this, false));
    587697
     698    var handlesNotFound = [];
    588699        var corporaToSelect = [];
    589         _.pairs(endpoints2handles).forEach(function(endp){
     700        _.pairs(endpoints2handles).forEach((endp) => {
    590701                var endpoint = endp[0];
    591702                var handles = endp[1];
    592             console.log(endp);
    593             console.log(handles);
    594                 handles.forEach(function(handle){
    595                         this.recurse(function(corpus){
     703            console.log('setAggregationContext: endpoint', endpoint);
     704            console.log('setAggregationContext: handles', handles);
     705                handles.forEach((handle) => {
     706                    var found = false;
     707                        this.recurse((corpus) => {
    596708                                if (corpus.handle === handle) {
     709                                    found = true;
    597710                                        corporaToSelect.push(corpus);
    598711                                }
    599                         }.bind(this));
    600                 }.bind(this));
    601         }.bind(this));
     712                        })
     713                        if (!found) {
     714                            console.warn("Handle not found in corpora", handle);
     715                            handlesNotFound.push(handle);
     716                        }
     717                })
     718        })
    602719
    603720        corporaToSelect.forEach(selectSubTree.bind(this, true));
     721        return {'selected': corporaToSelect, 'unavailable': handlesNotFound};
    604722};
    605723
     
    609727                if (corpus.visible && corpus.selected) {
    610728                        ids.push(corpus.id);
    611                         return false; // top-most collection in tree, don't delve deeper
     729                        //eturn false; // top-most collection in tree, don't delve deeper
     730                        // But subcollections are also selectable on their own?...
    612731                }
    613732                return true;
     
    628747};
    629748
    630 function Corpora(corpora, updateFn) {
    631         var that = this;
    632         this.corpora = corpora;
    633         this.update = function() {
    634                 updateFn(that);
    635         };
    636 
    637         var sortFn = function(x, y) {
    638                 var r = x.institution.name.localeCompare(y.institution.name);
    639                 if (r !== 0) {
    640                         return r;
    641                 }
    642                 return x.title.toLowerCase().localeCompare(y.title.toLowerCase());
    643         };
    644 
    645         this.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); });
    646         this.corpora.sort(sortFn);
    647 
    648         this.recurse(function(corpus, index) {
    649                 corpus.visible = true; // visible in the corpus view
    650                 corpus.selected = true; // selected in the corpus view
    651                 corpus.expanded = false; // not expanded in the corpus view
    652                 corpus.priority = 1; // used for ordering search results in corpus view
    653                 corpus.index = index; // original order, used for stable sort
    654         });
    655 }
    656 
    657749function getQueryVariable(variable) {
    658750    var query = window.location.search.substring(1);
    659751    var vars = query.split('&');
    660     console.log("vars: ", vars);
    661752    for (var i = 0; i < vars.length; i++) {
    662753        var pair = vars[i].split('=');
     
    669760}
    670761
    671 function Corpora(corpora, updateFn) {
    672         var that = this;
    673         this.corpora = corpora;
    674         this.update = function() {
    675                 updateFn(that);
    676         };
    677 
    678         var sortFn = function(x, y) {
    679                 var r = x.institution.name.localeCompare(y.institution.name);
    680                 if (r !== 0) {
    681                         return r;
    682                 }
    683                 return x.title.toLowerCase().localeCompare(y.title.toLowerCase());
    684         };
    685 
    686         this.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); });
    687         this.corpora.sort(sortFn);
    688 
    689         this.recurse(function(corpus, index) {
    690                 corpus.visible = true; // visible in the corpus view
    691                 corpus.selected = true; // selected in the corpus view
    692                 corpus.expanded = false; // not expanded in the corpus view
    693                 corpus.priority = 1; // used for ordering search results in corpus view
    694                 corpus.index = index; // original order, used for stable sort
    695         });
     762/* setter opposite of getQueryVariable*/
     763function setQueryVariable(qvar, value) {
     764    var query = window.location.search.substring(1);
     765    var vars = query.split('&');
     766    var d = {};
     767    d[qvar] = value;
     768    var found = false;
     769    for (var i = 0; i < vars.length; i++) {
     770        var pair = vars[i].split('=');
     771        if (decodeURIComponent(pair[0]) === qvar) {
     772           
     773            vars[i] = encodeQueryData(d);
     774            found=true;
     775            break;
     776        }
     777    }
     778   
     779    if (!found) {
     780        // add to end of url
     781        vars.push(encodeQueryData(d));
     782    }
     783   
     784    var searchPart = vars.join('&');
     785    var newUrl = window.location.origin + window.location.pathname+'?'+searchPart;
     786    console.log("set url", newUrl);
     787    window.history.replaceState(window.history.state, null, newUrl);
    696788}
     789
    697790
    698791function encodeQueryData(data)
Note: See TracChangeset for help on using the changeset viewer.