mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 12:22:47 +00:00 
			
		
		
		
	Add export options to subcorpora
This commit is contained in:
		@@ -1,13 +1,9 @@
 | 
				
			|||||||
from flask import session
 | 
					 | 
				
			||||||
import cqi
 | 
					import cqi
 | 
				
			||||||
import json
 | 
					 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
from app import socketio
 | 
					from app import socketio
 | 
				
			||||||
from app.decorators import socketio_login_required
 | 
					from app.decorators import socketio_login_required
 | 
				
			||||||
from app.models import Corpus
 | 
					 | 
				
			||||||
from . import NAMESPACE as ns
 | 
					from . import NAMESPACE as ns
 | 
				
			||||||
from .utils import cqi_over_socketio, export_subcorpus
 | 
					from .utils import cqi_over_socketio, export_subcorpus, partial_export_subcorpus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@socketio.on('cqi.corpora.corpus.subcorpora.get', namespace=ns)
 | 
					@socketio.on('cqi.corpora.corpus.subcorpora.get', namespace=ns)
 | 
				
			||||||
@@ -109,6 +105,16 @@ def cqi_corpora_corpus_subcorpora_subcorpus_paginate(cqi_client: cqi.CQiClient,
 | 
				
			|||||||
    return {'code': 200, 'msg': 'OK', 'payload': payload}
 | 
					    return {'code': 200, 'msg': 'OK', 'payload': payload}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@socketio.on('cqi.corpora.corpus.subcorpora.subcorpus.partial_export', namespace=ns)
 | 
				
			||||||
 | 
					@socketio_login_required
 | 
				
			||||||
 | 
					@cqi_over_socketio
 | 
				
			||||||
 | 
					def cqi_corpora_corpus_subcorpora_subcorpus_partial_export(cqi_client: cqi.CQiClient, corpus_name: str, subcorpus_name: str, match_id_list: list, context: int = 50):  # noqa
 | 
				
			||||||
 | 
					    cqi_corpus = cqi_client.corpora.get(corpus_name)
 | 
				
			||||||
 | 
					    cqi_subcorpus = cqi_corpus.subcorpora.get(subcorpus_name)
 | 
				
			||||||
 | 
					    cqi_subcorpus_partial_export = partial_export_subcorpus(cqi_subcorpus, match_id_list, context=context)
 | 
				
			||||||
 | 
					    return {'code': 200, 'msg': 'OK', 'payload': cqi_subcorpus_partial_export}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@socketio.on('cqi.corpora.corpus.subcorpora.subcorpus.export', namespace=ns)
 | 
					@socketio.on('cqi.corpora.corpus.subcorpora.subcorpus.export', namespace=ns)
 | 
				
			||||||
@socketio_login_required
 | 
					@socketio_login_required
 | 
				
			||||||
@cqi_over_socketio
 | 
					@cqi_over_socketio
 | 
				
			||||||
@@ -116,8 +122,4 @@ def cqi_corpora_corpus_subcorpora_subcorpus_export(cqi_client: cqi.CQiClient, co
 | 
				
			|||||||
    cqi_corpus = cqi_client.corpora.get(corpus_name)
 | 
					    cqi_corpus = cqi_client.corpora.get(corpus_name)
 | 
				
			||||||
    cqi_subcorpus = cqi_corpus.subcorpora.get(subcorpus_name)
 | 
					    cqi_subcorpus = cqi_corpus.subcorpora.get(subcorpus_name)
 | 
				
			||||||
    cqi_subcorpus_export = export_subcorpus(cqi_subcorpus, context=context)
 | 
					    cqi_subcorpus_export = export_subcorpus(cqi_subcorpus, context=context)
 | 
				
			||||||
    corpus = Corpus.query.get(session['d']['corpus_id'])
 | 
					    return {'code': 200, 'msg': 'OK', 'payload': cqi_subcorpus_export}
 | 
				
			||||||
    file_path = os.path.join(corpus.path, f'{subcorpus_name}.json')
 | 
					 | 
				
			||||||
    with open(file_path, 'w') as file:
 | 
					 | 
				
			||||||
        json.dump(cqi_subcorpus_export, file)
 | 
					 | 
				
			||||||
    return {'code': 200, 'msg': 'OK'}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,7 +68,7 @@ def lookups_by_cpos(corpus, cpos_list):
 | 
				
			|||||||
                cpos_attr_values[i]
 | 
					                cpos_attr_values[i]
 | 
				
			||||||
    for attr in corpus.structural_attributes.list():
 | 
					    for attr in corpus.structural_attributes.list():
 | 
				
			||||||
        # We only want to iterate over non subattributes, identifiable by
 | 
					        # We only want to iterate over non subattributes, identifiable by
 | 
				
			||||||
        # attr.attrs['has_values']==False
 | 
					        # attr.attrs['has_values'] == False
 | 
				
			||||||
        if attr.attrs['has_values']:
 | 
					        if attr.attrs['has_values']:
 | 
				
			||||||
            continue
 | 
					            continue
 | 
				
			||||||
        cpos_attr_ids = attr.ids_by_cpos(cpos_list)
 | 
					        cpos_attr_ids = attr.ids_by_cpos(cpos_list)
 | 
				
			||||||
@@ -93,43 +93,86 @@ def lookups_by_cpos(corpus, cpos_list):
 | 
				
			|||||||
    return lookups
 | 
					    return lookups
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def partial_export_subcorpus(subcorpus, match_id_list, context=25):
 | 
				
			||||||
 | 
					    if subcorpus.attrs['size'] == 0:
 | 
				
			||||||
 | 
					        return {"matches": []}
 | 
				
			||||||
 | 
					    match_boundaries = []
 | 
				
			||||||
 | 
					    for match_id in match_id_list:
 | 
				
			||||||
 | 
					        if match_id < 0 or match_id >= subcorpus.attrs['size']:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        match_boundaries.append(
 | 
				
			||||||
 | 
					            (
 | 
				
			||||||
 | 
					                match_id,
 | 
				
			||||||
 | 
					                subcorpus.dump(subcorpus.attrs['fields']['match'], match_id, match_id)[0],
 | 
				
			||||||
 | 
					                subcorpus.dump(subcorpus.attrs['fields']['matchend'], match_id, match_id)[0]
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    cpos_set = set()
 | 
				
			||||||
 | 
					    matches = []
 | 
				
			||||||
 | 
					    for match_boundary in match_boundaries:
 | 
				
			||||||
 | 
					        match_num, match_start, match_end = match_boundary
 | 
				
			||||||
 | 
					        c = (match_start, match_end)
 | 
				
			||||||
 | 
					        if match_start == 0 or context == 0:
 | 
				
			||||||
 | 
					            lc = None
 | 
				
			||||||
 | 
					            cpos_list_lbound = match_start
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            lc_lbound = max(0, (match_start - context))
 | 
				
			||||||
 | 
					            lc_rbound = match_start - 1
 | 
				
			||||||
 | 
					            lc = (lc_lbound, lc_rbound)
 | 
				
			||||||
 | 
					            cpos_list_lbound = lc_lbound
 | 
				
			||||||
 | 
					        if match_end == (subcorpus.collection.corpus.attrs['size'] - 1) or context == 0:
 | 
				
			||||||
 | 
					            rc = None
 | 
				
			||||||
 | 
					            cpos_list_rbound = match_end
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            rc_lbound = match_end + 1
 | 
				
			||||||
 | 
					            rc_rbound = min(
 | 
				
			||||||
 | 
					                (match_end + context),
 | 
				
			||||||
 | 
					                (subcorpus.collection.corpus.attrs['size'] - 1)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            rc = (rc_lbound, rc_rbound)
 | 
				
			||||||
 | 
					            cpos_list_rbound = rc_rbound
 | 
				
			||||||
 | 
					        match = {'num': match_num, 'lc': lc, 'c': c, 'rc': rc}
 | 
				
			||||||
 | 
					        matches.append(match)
 | 
				
			||||||
 | 
					        cpos_set.update(range(cpos_list_lbound, cpos_list_rbound + 1))
 | 
				
			||||||
 | 
					    lookups = lookups_by_cpos(subcorpus.collection.corpus, list(cpos_set))
 | 
				
			||||||
 | 
					    return {'matches': matches, **lookups}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def export_subcorpus(subcorpus, context=25, cutoff=float('inf'), offset=0):
 | 
					def export_subcorpus(subcorpus, context=25, cutoff=float('inf'), offset=0):
 | 
				
			||||||
    if subcorpus.attrs['size'] == 0:
 | 
					    if subcorpus.attrs['size'] == 0:
 | 
				
			||||||
        return {"matches": []}
 | 
					        return {"matches": []}
 | 
				
			||||||
    first_match = max(0, offset)
 | 
					    first_match = max(0, offset)
 | 
				
			||||||
    last_match = min((offset + cutoff - 1), (subcorpus.attrs['size'] - 1))
 | 
					    last_match = min((offset + cutoff - 1), (subcorpus.attrs['size'] - 1))
 | 
				
			||||||
    match_boundaries = zip(
 | 
					    match_boundaries = zip(
 | 
				
			||||||
        subcorpus.dump(
 | 
					        list(range(first_match, last_match + 1)),
 | 
				
			||||||
            subcorpus.attrs['fields']['match'], first_match, last_match),
 | 
					        subcorpus.dump(subcorpus.attrs['fields']['match'], first_match, last_match),
 | 
				
			||||||
        subcorpus.dump(
 | 
					        subcorpus.dump(subcorpus.attrs['fields']['matchend'], first_match, last_match)
 | 
				
			||||||
            subcorpus.attrs['fields']['matchend'], first_match, last_match)
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    cpos_set = set()
 | 
					    cpos_set = set()
 | 
				
			||||||
    matches = []
 | 
					    matches = []
 | 
				
			||||||
    match_num = offset + 1
 | 
					    for match_num, match_start, match_end in match_boundaries:
 | 
				
			||||||
    for match_start, match_end in match_boundaries:
 | 
					 | 
				
			||||||
        c = (match_start, match_end)
 | 
					        c = (match_start, match_end)
 | 
				
			||||||
        if match_start == 0 or context == 0:
 | 
					        if match_start == 0 or context == 0:
 | 
				
			||||||
            lc = None
 | 
					            lc = None
 | 
				
			||||||
            cpos_list_lbound = match_start
 | 
					            cpos_list_lbound = match_start
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            lc_lbound = max(0, (match_start - 1 - context))
 | 
					            lc_lbound = max(0, (match_start - context))
 | 
				
			||||||
            lc_rbound = match_start - 1
 | 
					            lc_rbound = match_start - 1
 | 
				
			||||||
            lc = (lc_lbound, lc_rbound)
 | 
					            lc = (lc_lbound, lc_rbound)
 | 
				
			||||||
            cpos_list_lbound = lc_lbound
 | 
					            cpos_list_lbound = lc_lbound
 | 
				
			||||||
        if (match_end == (subcorpus.collection.corpus.attrs['size'] - 1)
 | 
					        if match_end == (subcorpus.collection.corpus.attrs['size'] - 1) or context == 0:
 | 
				
			||||||
                or context == 0):
 | 
					 | 
				
			||||||
            rc = None
 | 
					            rc = None
 | 
				
			||||||
            cpos_list_rbound = match_end
 | 
					            cpos_list_rbound = match_end
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            rc_lbound = match_end + 1
 | 
					            rc_lbound = match_end + 1
 | 
				
			||||||
            rc_rbound = min(match_end + 1 + context,
 | 
					            rc_rbound = min(
 | 
				
			||||||
                            subcorpus.collection.corpus.attrs['size'] - 1)
 | 
					                (match_end + context),
 | 
				
			||||||
 | 
					                (subcorpus.collection.corpus.attrs['size'] - 1)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            rc = (rc_lbound, rc_rbound)
 | 
					            rc = (rc_lbound, rc_rbound)
 | 
				
			||||||
            cpos_list_rbound = rc_rbound
 | 
					            cpos_list_rbound = rc_rbound
 | 
				
			||||||
        match = {'num': match_num, 'lc': lc, 'c': c, 'rc': rc}
 | 
					        match = {'num': match_num, 'lc': lc, 'c': c, 'rc': rc}
 | 
				
			||||||
        matches.append(match)
 | 
					        matches.append(match)
 | 
				
			||||||
        cpos_set.update(range(cpos_list_lbound, cpos_list_rbound + 1))
 | 
					        cpos_set.update(range(cpos_list_lbound, cpos_list_rbound + 1))
 | 
				
			||||||
        match_num += 1
 | 
					 | 
				
			||||||
    lookups = lookups_by_cpos(subcorpus.collection.corpus, list(cpos_set))
 | 
					    lookups = lookups_by_cpos(subcorpus.collection.corpus, list(cpos_set))
 | 
				
			||||||
    return {'matches': matches, **lookups}
 | 
					    return {'matches': matches, **lookups}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -401,6 +401,25 @@ class CQiSubcorpus {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  partial_export(matchIdList, context=50) {
 | 
				
			||||||
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					      const args = {
 | 
				
			||||||
 | 
					        corpus_name: this.corpus.name,
 | 
				
			||||||
 | 
					        subcorpus_name: this.name,
 | 
				
			||||||
 | 
					        match_id_list: matchIdList,
 | 
				
			||||||
 | 
					        context: context
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.socket.emit('cqi.corpora.corpus.subcorpora.subcorpus.partial_export', args, response => {
 | 
				
			||||||
 | 
					        if (response.code === 200) {
 | 
				
			||||||
 | 
					          resolve(response.payload);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          reject(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fdst_1(cutoff, field, attribute) {
 | 
					  fdst_1(cutoff, field, attribute) {
 | 
				
			||||||
    return new Promise((resolve, reject) => {
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
      const args = {
 | 
					      const args = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,6 +47,8 @@ class CorpusAnalysisConcordance {
 | 
				
			|||||||
      this.data.corpus.o.query(subcorpusName, query)
 | 
					      this.data.corpus.o.query(subcorpusName, query)
 | 
				
			||||||
        .then(cQiStatus => {
 | 
					        .then(cQiStatus => {
 | 
				
			||||||
          subcorpus.q = query;
 | 
					          subcorpus.q = query;
 | 
				
			||||||
 | 
					          subcorpus.selectedItems = new Set();
 | 
				
			||||||
 | 
					          if (subcorpusName !== 'Last') {this.data.subcorpora.Last = subcorpus;}
 | 
				
			||||||
          return this.data.corpus.o.subcorpora.get(subcorpusName);
 | 
					          return this.data.corpus.o.subcorpora.get(subcorpusName);
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .then(cQiSubcorpus => {
 | 
					        .then(cQiSubcorpus => {
 | 
				
			||||||
@@ -56,8 +58,6 @@ class CorpusAnalysisConcordance {
 | 
				
			|||||||
        .then(
 | 
					        .then(
 | 
				
			||||||
          paginatedSubcorpus => {
 | 
					          paginatedSubcorpus => {
 | 
				
			||||||
            subcorpus.p = paginatedSubcorpus;
 | 
					            subcorpus.p = paginatedSubcorpus;
 | 
				
			||||||
            subcorpus.selectedItems = {};
 | 
					 | 
				
			||||||
            if (subcorpusName !== 'Last') {this.data.subcorpora.Last = subcorpus;}
 | 
					 | 
				
			||||||
            this.data.subcorpora[subcorpusName] = subcorpus;
 | 
					            this.data.subcorpora[subcorpusName] = subcorpus;
 | 
				
			||||||
            this.settings.selectedSubcorpus = subcorpusName;
 | 
					            this.settings.selectedSubcorpus = subcorpusName;
 | 
				
			||||||
            this.renderSubcorpusList();
 | 
					            this.renderSubcorpusList();
 | 
				
			||||||
@@ -154,48 +154,140 @@ class CorpusAnalysisConcordance {
 | 
				
			|||||||
  renderSubcorpusActions() {
 | 
					  renderSubcorpusActions() {
 | 
				
			||||||
    this.clearSubcorpusActions();
 | 
					    this.clearSubcorpusActions();
 | 
				
			||||||
    this.elements.subcorpusActions.innerHTML += `
 | 
					    this.elements.subcorpusActions.innerHTML += `
 | 
				
			||||||
      <a class="btn-floating btn-small tooltipped waves-effect waves-light corpus-analysis-action download-subcorpus-selection-trigger" data-tooltip="Download subcorpus selection">
 | 
					      <a class="btn-floating btn-small tooltipped waves-effect waves-light corpus-analysis-action subcorpus-export-trigger" data-tooltip="Export subcorpus">
 | 
				
			||||||
        <i class="material-icons">playlist_add_check</i>
 | 
					        <i class="material-icons">export</i>
 | 
				
			||||||
      </a>
 | 
					      </a>
 | 
				
			||||||
      <a class="btn-floating btn-small tooltipped waves-effect waves-light corpus-analysis-action download-subcorpus-trigger" data-tooltip="Download subcorpus">
 | 
					      <a class="btn-floating btn-small red tooltipped waves-effect waves-light corpus-analysis-action subcorpus-delete-trigger" data-tooltip="Delete subcorpus">
 | 
				
			||||||
        <i class="material-icons">file_download</i>
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
      <a class="btn-floating btn-small red tooltipped waves-effect waves-light corpus-analysis-action delete-subcorpus-trigger" data-tooltip="Delete subcorpus">
 | 
					 | 
				
			||||||
        <i class="material-icons">delete</i>
 | 
					        <i class="material-icons">delete</i>
 | 
				
			||||||
      </a>
 | 
					      </a>
 | 
				
			||||||
    `.trim();
 | 
					    `.trim();
 | 
				
			||||||
    M.Tooltip.init(this.elements.subcorpusActions.querySelectorAll('.tooltipped'));
 | 
					    M.Tooltip.init(this.elements.subcorpusActions.querySelectorAll('.tooltipped'));
 | 
				
			||||||
    this.elements.subcorpusActions.querySelector('.download-subcorpus-trigger').addEventListener('click', event => {
 | 
					    this.elements.subcorpusActions.querySelector('.subcorpus-export-trigger').addEventListener('click', event => {
 | 
				
			||||||
      event.preventDefault();
 | 
					 | 
				
			||||||
      app.flash('This feature is currently not available', 'error');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    this.elements.subcorpusActions.querySelector('.download-subcorpus-selection-trigger').addEventListener('click', event => {
 | 
					 | 
				
			||||||
      event.preventDefault();
 | 
					      event.preventDefault();
 | 
				
			||||||
      let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
 | 
					      let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
 | 
				
			||||||
      if (JSON.stringify(subcorpus.selectedItems) === '{}') {app.flash('No items selected', 'error'); return;}
 | 
					      let modalElementId = Utils.generateElementId('export-subcorpus-modal-');
 | 
				
			||||||
      let csvContent = 'sep=,\r\n';
 | 
					      let exportFormatSelectElementId = Utils.generateElementId('export-format-select-');
 | 
				
			||||||
      csvContent += '"#Match","Text title","Left context","KWIC","Right context"\r\n';
 | 
					      let exportSelectedMatchesOnlyCheckboxElementId = Utils.generateElementId('export-selected-matches-only-checkbox-');
 | 
				
			||||||
      for (let selectedItem of Object.values(subcorpus.selectedItems)) {
 | 
					      let exportFileNameInputElementId = Utils.generateElementId('export-file-name-input-');
 | 
				
			||||||
        csvContent += `"${selectedItem.num}",`;
 | 
					      let modalElement = Utils.HTMLToElement(
 | 
				
			||||||
        csvContent += `"${selectedItem.textTitle.replace('"', '""')}",`;
 | 
					        `
 | 
				
			||||||
        csvContent += `"${selectedItem.leftContext.replace('"', '""')}",`;
 | 
					          <div class="modal" id="${modalElementId}">
 | 
				
			||||||
        csvContent += `"${selectedItem.kwic.replace('"', '""')}",`;
 | 
					            <div class="modal-content">
 | 
				
			||||||
        csvContent += `"${selectedItem.rightContext.replace('"', '""')}"\r\n`;
 | 
					              <h4>Export subcorpus "${subcorpus.o.name}"</h4>
 | 
				
			||||||
 | 
					              <br>
 | 
				
			||||||
 | 
					              <div class="row">
 | 
				
			||||||
 | 
					                <div class="input-field col s3">
 | 
				
			||||||
 | 
					                  <select id="${exportFormatSelectElementId}">
 | 
				
			||||||
 | 
					                    <option value="csv" selected>CSV</option>
 | 
				
			||||||
 | 
					                    <option value="json">JSON</option>
 | 
				
			||||||
 | 
					                  </select>
 | 
				
			||||||
 | 
					                  <label>Export format</label>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="input-field col s9">
 | 
				
			||||||
 | 
					                  <input id="${exportFileNameInputElementId}" type="text" class="validate" value="export">
 | 
				
			||||||
 | 
					                  <label class="active" for="${exportFileNameInputElementId}">Export filename without filename extension (.csv/.json/...)</label>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <p class="col s12">
 | 
				
			||||||
 | 
					                  <label>
 | 
				
			||||||
 | 
					                    <input id="${exportSelectedMatchesOnlyCheckboxElementId}" type="checkbox" ${subcorpus.selectedItems.size === 0 ? '' : 'checked'}>
 | 
				
			||||||
 | 
					                    <span>Export selected matches only</span>
 | 
				
			||||||
 | 
					                  </label>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="modal-footer">
 | 
				
			||||||
 | 
					              <a class="btn-flat modal-close waves-effect waves-light">Cancel</a>
 | 
				
			||||||
 | 
					              <a class="action-button btn modal-close waves-effect waves-light" data-action="export">Export</a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        `
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      document.querySelector('#modals').appendChild(modalElement);
 | 
				
			||||||
 | 
					      let exportFormatSelectElement = modalElement.querySelector(`#${exportFormatSelectElementId}`);
 | 
				
			||||||
 | 
					      let exportFormatSelect = M.FormSelect.init(exportFormatSelectElement);
 | 
				
			||||||
 | 
					      let exportSelectedMatchesOnlyCheckboxElement = modalElement.querySelector(`#${exportSelectedMatchesOnlyCheckboxElementId}`);
 | 
				
			||||||
 | 
					      let exportFileNameInputElement = modalElement.querySelector(`#${exportFileNameInputElementId}`);
 | 
				
			||||||
 | 
					      let exportButton = modalElement.querySelector('.action-button[data-action="export"]');
 | 
				
			||||||
 | 
					      let modal = M.Modal.init(
 | 
				
			||||||
 | 
					        modalElement,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          dismissible: false,
 | 
				
			||||||
 | 
					          onCloseEnd: () => {
 | 
				
			||||||
 | 
					            exportFormatSelect.destroy();
 | 
				
			||||||
 | 
					            modal.destroy();
 | 
				
			||||||
 | 
					            modalElement.remove();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      exportButton.addEventListener('click', event => {
 | 
				
			||||||
 | 
					        event.preventDefault();
 | 
				
			||||||
 | 
					        this.app.disableActionElements();
 | 
				
			||||||
 | 
					        this.elements.progress.classList.remove('hide');
 | 
				
			||||||
 | 
					        let exportFormat = exportFormatSelectElement.value;
 | 
				
			||||||
 | 
					        let exportFileName = exportFileNameInputElement.value;
 | 
				
			||||||
 | 
					        let exportFileNameExtension = exportFormat === 'csv' ? 'csv' : 'json';
 | 
				
			||||||
 | 
					        let exportFileNameWithExtension = `${exportFileName}.${exportFileNameExtension}`;
 | 
				
			||||||
 | 
					        let exportSelectedMatchesOnly = exportSelectedMatchesOnlyCheckboxElement.checked;
 | 
				
			||||||
 | 
					        let promise;
 | 
				
			||||||
 | 
					        if (exportSelectedMatchesOnly) {
 | 
				
			||||||
 | 
					          if (subcorpus.selectedItems.size === 0) {
 | 
				
			||||||
 | 
					            this.elements.progress.classList.add('hide');
 | 
				
			||||||
 | 
					            this.app.enableActionElements();
 | 
				
			||||||
 | 
					            app.flash('No matches selected', 'error');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          promise = subcorpus.o.partial_export([...subcorpus.selectedItems], 50);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          promise = subcorpus.o.export(50);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        promise.then(
 | 
				
			||||||
 | 
					          data => {
 | 
				
			||||||
 | 
					            let blob;
 | 
				
			||||||
 | 
					            if (exportFormat === 'csv') {
 | 
				
			||||||
 | 
					              let csvContent = 'sep=,\r\n';
 | 
				
			||||||
 | 
					              csvContent += '"#Match","Text title","Left context","KWIC","Right context"';
 | 
				
			||||||
 | 
					              for (let match of data.matches) {
 | 
				
			||||||
 | 
					                csvContent += '\r\n';
 | 
				
			||||||
 | 
					                csvContent += `"${match.num}"`;
 | 
				
			||||||
 | 
					                csvContent += ',';
 | 
				
			||||||
 | 
					                let textIds = new Set();
 | 
				
			||||||
 | 
					                for (let cpos = match.c[0]; cpos <= match.c[1]; cpos++) {
 | 
				
			||||||
 | 
					                  textIds.add(data.cpos_lookup[cpos].text);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                csvContent += '"' + [...textIds].map(x => data.text_lookup[x].title.replace('"', '""')).join(', ') + '"';
 | 
				
			||||||
 | 
					                csvContent += ',';
 | 
				
			||||||
 | 
					                if (match.lc !== null) {
 | 
				
			||||||
 | 
					                  let lc_cpos_list = [];
 | 
				
			||||||
 | 
					                  for (let cpos = match.lc[0]; cpos <= match.lc[1]; cpos++) {lc_cpos_list.push(cpos);}
 | 
				
			||||||
 | 
					                  csvContent += '"' + lc_cpos_list.map(x => data.cpos_lookup[x].word.replace('"', '""')).join(' ') + '"';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                csvContent += ',';
 | 
				
			||||||
 | 
					                let c_cpos_list = [];
 | 
				
			||||||
 | 
					                for (let cpos = match.c[0]; cpos <= match.c[1]; cpos++) {c_cpos_list.push(cpos);}
 | 
				
			||||||
 | 
					                csvContent += '"' + c_cpos_list.map(x => data.cpos_lookup[x].word.replace('"', '""')).join(' ') + '"';
 | 
				
			||||||
 | 
					                csvContent += ',';
 | 
				
			||||||
 | 
					                let rc_cpos_list = [];
 | 
				
			||||||
 | 
					                for (let cpos = match.rc[0]; cpos <= match.rc[1]; cpos++) {rc_cpos_list.push(cpos);}
 | 
				
			||||||
 | 
					                if (match.rc !== null) {
 | 
				
			||||||
 | 
					                  csvContent += '"' + rc_cpos_list.map(x => data.cpos_lookup[x].word.replace('"', '""')).join(' ') + '"';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              blob = new Blob([csvContent], {type: 'text/csv;charset=utf-8;'});
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json;charset=utf-8;'});
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
      // Create a blob
 | 
					 | 
				
			||||||
      let blob = new Blob([csvContent], {type: 'text/csv;charset=utf-8;'});
 | 
					 | 
				
			||||||
            let url = URL.createObjectURL(blob);
 | 
					            let url = URL.createObjectURL(blob);
 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Create a link to download it
 | 
					 | 
				
			||||||
            let pom = document.createElement('a');
 | 
					            let pom = document.createElement('a');
 | 
				
			||||||
            pom.href = url;
 | 
					            pom.href = url;
 | 
				
			||||||
      pom.setAttribute('download', 'export.csv');
 | 
					            pom.setAttribute('download', exportFileNameWithExtension);
 | 
				
			||||||
            pom.click();
 | 
					            pom.click();
 | 
				
			||||||
      // console.log(csvContent);
 | 
					            this.elements.progress.classList.add('hide');
 | 
				
			||||||
      // let encodedUri = encodeURI(csvContent);
 | 
					            this.app.enableActionElements();
 | 
				
			||||||
      // window.open(encodedUri);
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    this.elements.subcorpusActions.querySelector('.delete-subcorpus-trigger').addEventListener('click', event => {
 | 
					      });
 | 
				
			||||||
 | 
					      modal.open();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.elements.subcorpusActions.querySelector('.subcorpus-delete-trigger').addEventListener('click', event => {
 | 
				
			||||||
      event.preventDefault();
 | 
					      event.preventDefault();
 | 
				
			||||||
      let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
 | 
					      let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
 | 
				
			||||||
      subcorpus.o.drop().then(
 | 
					      subcorpus.o.drop().then(
 | 
				
			||||||
@@ -296,18 +388,12 @@ class CorpusAnalysisConcordance {
 | 
				
			|||||||
        event.preventDefault();
 | 
					        event.preventDefault();
 | 
				
			||||||
        let itemElement = selectTriggerElement.closest('.item');
 | 
					        let itemElement = selectTriggerElement.closest('.item');
 | 
				
			||||||
        let itemId = parseInt(itemElement.dataset.id);
 | 
					        let itemId = parseInt(itemElement.dataset.id);
 | 
				
			||||||
        if (itemId in subcorpus.selectedItems) {
 | 
					        if (subcorpus.selectedItems.has(itemId)) {
 | 
				
			||||||
          delete subcorpus.selectedItems[itemId];
 | 
					          subcorpus.selectedItems.delete(itemId);
 | 
				
			||||||
          selectTriggerElement.classList.remove('green');
 | 
					          selectTriggerElement.classList.remove('green');
 | 
				
			||||||
          selectTriggerElement.querySelector('i').textContent = 'add';
 | 
					          selectTriggerElement.querySelector('i').textContent = 'add';
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          subcorpus.selectedItems[itemId] = {
 | 
					          subcorpus.selectedItems.add(itemId);
 | 
				
			||||||
            num: itemId,
 | 
					 | 
				
			||||||
            textTitle: itemElement.querySelector('.text-title').textContent,
 | 
					 | 
				
			||||||
            leftContext: [...itemElement.querySelectorAll('.left-context .p-attr')].map(x => x.textContent).join(' '),
 | 
					 | 
				
			||||||
            kwic: [...itemElement.querySelectorAll('.kwic .p-attr')].map(x => x.textContent).join(' '),
 | 
					 | 
				
			||||||
            rightContext: [...itemElement.querySelectorAll('.right-context .p-attr')].map(x => x.textContent).join(' ')
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          selectTriggerElement.classList.add('green');
 | 
					          selectTriggerElement.classList.add('green');
 | 
				
			||||||
          selectTriggerElement.querySelector('i').textContent = 'check';
 | 
					          selectTriggerElement.querySelector('i').textContent = 'check';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user