See More

/* * Project Name : Visual Python * Description : GUI-based Python code generator * File Name : Bind.js * Author : Black Logic * Note : Apps > Bind * License : GNU GPLv3 with Visual Python special exception * Date : 2021. 11. 18 * Change Date : */ //============================================================================ // [CLASS] Bind //============================================================================ define([ __VP_TEXT_LOADER__('vp_base/html/m_apps/bind.html'), // INTEGRATION: unified version of text loader __VP_CSS_LOADER__('vp_base/css/m_apps/bind'), // INTEGRATION: unified version of css loader 'vp_base/js/com/com_util', 'vp_base/js/com/com_String', 'vp_base/js/com/component/PopupComponent', 'vp_base/js/com/component/SuggestInput', 'vp_base/js/com/component/MultiSelector' ], function(bindHtml, bindCss, com_util, com_String, PopupComponent, SuggestInput, MultiSelector) { /** * Bind */ class Bind extends PopupComponent { _init() { super._init(); /** Write codes executed before rendering */ this.config.sizeLevel = 2; this.config.checkModules = ['pd']; this.config.helpview = true; this.howList = [ { label: 'Inner', value: 'inner', desc: 'Inner join' }, { label: 'Full outer', value: 'outer', desc: 'Full outer join' }, { label: 'Left outer', value: 'left', desc: 'Left outer join' }, { label: 'Right outer', value: 'right', desc: 'Right outer join' }, { label: 'Cross', value: 'cross', desc: 'Cartesian product' }, ] this.state = { type: 'concat', concat: { variable: [], // DataFrame, Series join: 'outer', axis: 0, // 0: index, 1: column sort: false }, merge: { left: { variable: '', on: [], useIndex: false, suffix: '' }, right: { variable: '', on: [], useIndex: false, suffix: '' }, on: [], how: 'inner', }, userOption: '', allocateTo: '', resetIndex: false, withoutColumn: 'True', ...this.state } this.popup = { type: '', targetSelector: '', MultiSelector: undefined } } _unbindEvent() { super._unbindEvent(); $(document).off('change', this.wrapSelector('#vp_bdType')); $(document).off('change', this.wrapSelector('#vp_bdVariable')); $(document).off('click', this.wrapSelector('#vp_bdVariable')); $(document).off('change', this.wrapSelector('#vp_bdJoin')); $(document).off('change', this.wrapSelector('#vp_bdAxis')); $(document).off('change', this.wrapSelector('#vp_bdSort')); $(document).off('change', this.wrapSelector('#vp_bdLeftDataframe')); $(document).off('change', this.wrapSelector('#vp_bdRightDataframe')); $(document).off('click', this.wrapSelector('.vp-bd-df-refresh')); $(document).off('change', this.wrapSelector('#vp_bdHow')); $(document).off('change', this.wrapSelector('#vp_bdOn')); $(document).off('click', this.wrapSelector('#vp_bdOn')); $(document).off('change', this.wrapSelector('#vp_bdLeftOn')); $(document).off('click', this.wrapSelector('#vp_bdLeftOn')); $(document).off('change', this.wrapSelector('#vp_gbLeftIndex')); $(document).off('change', this.wrapSelector('#vp_bdRightOn')); $(document).off('click', this.wrapSelector('#vp_bdRightOn')); $(document).off('change', this.wrapSelector('#vp_gbRightIndex')); $(document).off('change', this.wrapSelector('#vp_bdLeftSuffix')); $(document).off('change', this.wrapSelector('#vp_bdRightSuffix')); $(document).off('change', this.wrapSelector('#vp_bdUserOption')); $(document).off('change', this.wrapSelector('#vp_bdAllocateTo')); $(document).off('change', this.wrapSelector('#vp_bdResetIndex')); } _bindEvent() { super._bindEvent(); var that = this; //==================================================================== // User operation Events //==================================================================== $(document).on('change', this.wrapSelector('#vp_bdType'), function() { var type = $(this).val(); that.state.type = type; if (type == 'concat') { $(that.wrapSelector('.vp-bd-type-box.concat')).show(); $(that.wrapSelector('.vp-bd-type-box.merge')).hide(); $(that.wrapSelector('#vp_bdWithoutColumn')).hide(); } else { $(that.wrapSelector('.vp-bd-type-box.merge')).show(); $(that.wrapSelector('.vp-bd-type-box.concat')).hide(); $(that.wrapSelector('#vp_bdWithoutColumn')).show(); } // clear user option $(that.wrapSelector('#vp_bdUserOption')).val(''); that.state.userOption = ''; that.setHelpContent('_vp_pd.' + type); }); //==================================================================== // Concat box Events //====================================================================\ // variable change event $(document).on('change', this.wrapSelector('#vp_bdVariable'), function(event) { var varList = event.dataList; that.state.concat.variable = varList; }); // variable select button event $(document).on('click', this.wrapSelector('#vp_bdVariable'), function() { that.openVariablePopup($(that.wrapSelector('#vp_bdVariable'))); }); // join $(document).on('change', this.wrapSelector('#vp_bdJoin'), function() { that.state.concat.join = $(this).val(); }); // axis $(document).on('change', this.wrapSelector('#vp_bdAxis'), function() { that.state.concat.axis = $(this).val(); }); // sort $(document).on('change', this.wrapSelector('#vp_bdSort'), function() { that.state.concat.sort = $(this).val() == 'yes'; }); //==================================================================== // Merge box Events //==================================================================== // Left variable change event $(document).on('change', this.wrapSelector('#vp_bdLeftDataframe'), function() { // if variable changed, clear groupby, display var newVal = $(this).val(); if (newVal != that.state.merge.left.variable) { $(that.wrapSelector('#vp_bdOn')).val(''); $(that.wrapSelector('#vp_bdLeftOn')).val(''); that.state.merge.left.variable = newVal; that.state.merge.left.on = []; that.state.merge.on = []; } }); // Right variable change event $(document).on('change', this.wrapSelector('#vp_bdRightDataframe'), function() { // if variable changed, clear groupby, display var newVal = $(this).val(); if (newVal != that.state.merge.right.variable) { $(that.wrapSelector('#vp_bdOn')).val(''); $(that.wrapSelector('#vp_bdRightOn')).val(''); that.state.merge.right.variable = newVal; that.state.merge.right.on = []; that.state.merge.on = []; } }); // variable refresh event $(document).on('click', this.wrapSelector('.vp-bd-df-refresh'), function() { that.loadVariableList(); }); // how $(document).on('change', this.wrapSelector('#vp_bdHow'), function() { that.state.merge.how = $(this).val(); }); // on change event $(document).on('change', this.wrapSelector('#vp_bdOn'), function(event) { var colList = event.dataList; that.state.merge.on = colList; if (colList && colList.length > 0) { $(that.wrapSelector('#vp_bdLeftOn')).attr('disabled', true); $(that.wrapSelector('#vp_bdRightOn')).attr('disabled', true); $(that.wrapSelector('#vp_bdLeftIndex')).attr('disabled', true); $(that.wrapSelector('#vp_bdRightIndex')).attr('disabled', true); } else { $(that.wrapSelector('#vp_bdLeftOn')).attr('disabled', false); $(that.wrapSelector('#vp_bdRightOn')).attr('disabled', false); $(that.wrapSelector('#vp_bdLeftIndex')).attr('disabled', false); $(that.wrapSelector('#vp_bdRightIndex')).attr('disabled', false); } }); // on select button event $(document).on('click', this.wrapSelector('#vp_bdOn'), function() { var targetVariable = [ that.state.merge.left.variable, that.state.merge.right.variable ]; that.openColumnPopup(targetVariable, $(that.wrapSelector('#vp_bdOn')), 'Select columns from both dataframe'); }); // Left on change event $(document).on('change', this.wrapSelector('#vp_bdLeftOn'), function(event) { var colList = event.dataList; that.state.merge.left.on = colList; if ((colList && colList.length > 0) || that.state.merge.right.on && that.state.merge.right.on.length > 0) { $(that.wrapSelector('#vp_bdOn')).attr('disabled', true); } else { $(that.wrapSelector('#vp_bdOn')).attr('disabled', false); } }); // Left on select button event $(document).on('click', this.wrapSelector('#vp_bdLeftOn'), function() { var targetVariable = [ that.state.merge.left.variable ]; that.openColumnPopup(targetVariable, $(that.wrapSelector('#vp_bdLeftOn')), 'Select columns from left dataframe'); }); // Left use index $(document).on('change', this.wrapSelector('#vp_bdLeftIndex'), function() { var useIndex = $(this).prop('checked'); that.state.merge.left.useIndex = useIndex; if (useIndex || that.state.merge.right.useIndex) { $(that.wrapSelector('#vp_bdOn')).attr('disabled', true); $(that.wrapSelector('#vp_bdLeftOn')).attr('disabled', true); } else { $(that.wrapSelector('#vp_bdOn')).attr('disabled', false); $(that.wrapSelector('#vp_bdLeftOn')).attr('disabled', false); } }); // Right on change event $(document).on('change', this.wrapSelector('#vp_bdRightOn'), function(event) { var colList = event.dataList; that.state.merge.right.on = colList; if ((colList && colList.length > 0) || that.state.merge.left.on && that.state.merge.left.on.length > 0) { $(that.wrapSelector('#vp_bdOn')).attr('disabled', true); } else { $(that.wrapSelector('#vp_bdOn')).attr('disabled', false); } }); // Right on select button event $(document).on('click', this.wrapSelector('#vp_bdRightOn'), function() { var targetVariable = [ that.state.merge.right.variable ]; that.openColumnPopup(targetVariable, $(that.wrapSelector('#vp_bdRightOn')), 'Select columns from right dataframe'); }); // Right use index $(document).on('change', this.wrapSelector('#vp_bdRightIndex'), function() { var useIndex = $(this).prop('checked'); that.state.merge.right.useIndex = useIndex; if (useIndex || that.state.merge.left.useIndex) { $(that.wrapSelector('#vp_bdOn')).attr('disabled', true); $(that.wrapSelector('#vp_bdRightOn')).attr('disabled', true); } else { $(that.wrapSelector('#vp_bdOn')).attr('disabled', false); $(that.wrapSelector('#vp_bdRightOn')).attr('disabled', false); } }); // Left suffix change event $(document).on('change', this.wrapSelector('#vp_bdLeftSuffix'), function() { that.state.merge.left.suffix = $(this).val(); }); // Right suffix change event $(document).on('change', this.wrapSelector('#vp_bdRightSuffix'), function() { that.state.merge.right.suffix = $(this).val(); }); // userOption event $(document).on('change', this.wrapSelector('#vp_bdUserOption'), function() { that.state.userOption = $(this).val(); }); // allocateTo event $(document).on('change', this.wrapSelector('#vp_bdAllocateTo'), function() { that.state.allocateTo = $(this).val(); }); // reset index checkbox event $(document).on('change', this.wrapSelector('#vp_bdResetIndex'), function() { let isChecked = $(this).prop('checked'); $(that.wrapSelector('#vp_bdWithoutColumn')).prop('disabled', !isChecked); that.state.resetIndex = isChecked; }); // with/without column select event $(this.wrapSelector('#vp_bdWithoutColumn')).on('change', function() { that.state.withoutColumn = $(this).val(); }); } templateForBody() { return bindHtml; } templateForDataView() { return ''; } renderDataView() { super.renderDataView(); this.loadDataPage(); $(this.wrapSelector('.vp-popup-dataview-box')).css('height', '300px'); } renderDataPage(renderedText, isHtml = true) { var tag = new com_String(); tag.appendFormatLine('

', 'vp_rendered_html'); // 'rendered_html' style from jupyter output area if (isHtml) { tag.appendLine(renderedText); } else { tag.appendFormatLine('
{0}
', renderedText); } tag.appendLine('
'); $(this.wrapSelector('.vp-popup-dataview-box')).html(tag.toString()); } loadDataPage() { var that = this; var code = this.generateCode(); // if not, get output of all data in selected pandasObject vpKernel.execute(code).then(function(resultObj) { let { msg } = resultObj; if (msg.content && msg.content.data) { var htmlText = String(msg.content.data["text/html"]); var codeText = String(msg.content.data["text/plain"]); if (htmlText != 'undefined') { that.renderDataPage(htmlText); } else if (codeText != 'undefined') { // plain text as code that.renderDataPage(codeText, false); } else { that.renderDataPage(''); } } else { var errorContent = ''; if (msg.content && msg.content.ename) { errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content?.evalue); } vpLog.display(VP_LOG_TYPE.ERROR, msg.content?.ename, msg.content?.evalue, msg.content); that.renderDataPage(errorContent); } }).catch(function(resultObj) { let { msg } = resultObj; var errorContent = ''; if (msg.content && msg.content.ename) { errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content?.evalue); } vpLog.display(VP_LOG_TYPE.ERROR, msg.content?.ename, msg.content?.evalue, msg.content); that.renderDataPage(errorContent); }); } render() { super.render(); // check its type if (this.state.type === 'concat') { $(this.wrapSelector('#vp_bdWithoutColumn')).hide(); } else { $(this.wrapSelector('#vp_bdWithoutColumn')).hide(); } this.setHelpContent('_vp_pd.' + this.state.type); this.loadVariableList(); } /** * Render variable list (for dataframe) * @param {Array} varList * @param {string} defaultValue previous value */ renderVariableList(id, varList, defaultValue='') { let mappedList = varList.map(obj => { return { label: obj.varName, value: obj.varName, dtype: obj.varType } }); var variableInput = new SuggestInput(); variableInput.setComponentID(id); variableInput.addClass('vp-state'); variableInput.setPlaceholder('Select variable'); variableInput.setSuggestList(function () { return mappedList; }); variableInput.setNormalFilter(true); variableInput.setValue(defaultValue); variableInput.addAttribute('required', true); $(this.wrapSelector('#' + id)).replaceWith(function() { return variableInput.toTagString(); }); } /** * Load variable list (dataframe) */ loadVariableList() { var that = this; // load using kernel var dataTypes = ['DataFrame']; vpKernel.getDataList(dataTypes).then(function(resultObj) { let { result } = resultObj; try { var varList = JSON.parse(result); // render variable list // get prevvalue var prevValue = that.state.merge.left.variable; // replace that.renderVariableList('vp_bdLeftDataframe', varList, prevValue); $(that.wrapSelector('#vp_bdLeftDataframe')).trigger('change'); prevValue = that.state.merge.right.variable; that.renderVariableList('vp_bdRightDataframe', varList, prevValue); $(that.wrapSelector('#vp_bdRightDataframe')).trigger('change'); } catch (ex) { vpLog.display(VP_LOG_TYPE.ERROR, 'Bind:', result); } }); } generateCode() { var code = new com_String(); var { type, concat, merge, allocateTo, resetIndex, withoutColumn, userOption } = this.state; //==================================================================== // Allocation //==================================================================== if (allocateTo && allocateTo != '') { code.appendFormat('{0} = ', allocateTo); } //==================================================================== // Dataframe variables //==================================================================== code.appendFormat('pd.{0}(', type); if (type == 'concat') { //==================================================================== // Concat //==================================================================== // FIXME: consider default code.appendFormat("[{0}], join='{1}', axis={2}", concat.variable.map(data=>data.code).join(','), concat.join, concat.axis); //==================================================================== // Sort //==================================================================== if (concat.sort) { code.append(', sort=True'); } //==================================================================== // Reset index //==================================================================== if (resetIndex) { code.append(', ignore_index=True'); } //==================================================================== // User option //==================================================================== if (userOption && userOption != '') { code.appendFormat(", {0}", userOption); } code.append(')'); } else { //==================================================================== // Merge //==================================================================== code.appendFormat('{0}, {1}', merge.left.variable, merge.right.variable); if (merge.on && merge.on.length > 0) { //================================================================ // On columns //================================================================ code.appendFormat(', on=[{0}]', merge.on.map(col => col.code)); } else { //==================================================================== // Left & Right On columns //==================================================================== if (merge.left.useIndex) { code.append(', left_index=True'); } else { if (merge.left.on && merge.left.on.length > 0) { code.appendFormat(', left_on=[{0}]', merge.left.on.map(col => col.code)); } } if (merge.right.useIndex) { code.append(', right_index=True'); } else { if (merge.right.on && merge.right.on.length > 0) { code.appendFormat(', right_on=[{0}]', merge.right.on.map(col => col.code)); } } } //==================================================================== // How //==================================================================== if (merge.how) { code.appendFormat(", how='{0}'", merge.how); } //==================================================================== // Suffixes //==================================================================== if (merge.left.suffix != '' || merge.right.suffix != '') { code.appendFormat(", suffixes=('{0}', '{1}')", merge.left.suffix, merge.right.suffix); } //==================================================================== // User option //==================================================================== if (userOption && userOption != '') { code.appendFormat(", {0}", userOption); } code.append(')'); //==================================================================== // Reset index //==================================================================== if (resetIndex) { if (withoutColumn === 'True') { code.append('.reset_index(drop=True)'); } else { code.append('.reset_index()'); } } } if (allocateTo && allocateTo != '') { code.appendLine(); code.append(allocateTo); } return code.toString(); } loadState() { super.loadState(); var { type, concat, merge, userOption, allocateTo, resetIndex, withoutColumn } = this.state; // type $(this.wrapSelector('#vp_bdType')).val(type); if (type == 'concat') { // load concat state this._loadSelectorInput(this.wrapSelector('#vp_bdVariable'), concat.variable); $(this.wrapSelector('#vp_bdJoin')).val(concat.join); $(this.wrapSelector('#vp_bdAxis')).val(concat.axis); $(this.wrapSelector('#vp_bdSort')).val(concat.sort?'yes':'no'); } else { // load merge state $(this.wrapSelector('#vp_bdLeftDataframe')).val(merge.left.variable); $(this.wrapSelector('#vp_mpRightDataframe')).val(merge.right.variable); $(this.wrapSelector('#vp_bdHow')).val(merge.how); this._loadSelectorInput(this.wrapSelector('#vp_bdOn'), merge.on); if (merge.on && merge.on.length > 0) { $(this.wrapSelector('#vp_bdLeftOn')).attr('disabled', true); $(this.wrapSelector('#vp_bdRightOn')).attr('disabled', true); $(this.wrapSelector('#vp_bdLeftIndex')).attr('disabled', true); $(this.wrapSelector('#vp_bdRightIndex')).attr('disabled', true); } this._loadSelectorInput(this.wrapSelector('#vp_bdLeftOn'), merge.left.on); this._loadSelectorInput(this.wrapSelector('#vp_bdRightOn'), merge.right.on); if (merge.left.on.length > 0 || merge.right.on.length > 0 || merge.left.useIndex || merge.right.useIndex) { $(this.wrapSelector('#vp_bdOn')).attr('disabled', true); } $(this.wrapSelector('#vp_bdLeftIndex')).prop('checked', merge.left.useIndex); $(this.wrapSelector('#vp_bdRightIndex')).prop('checked', merge.right.useIndex); $(this.wrapSelector('#vp_bdLeftSuffix')).val(merge.left.suffix); $(this.wrapSelector('#vp_bdRightSuffix')).val(merge.right.suffix); } $(this.wrapSelector('#vp_bdUserOption')).val(userOption); $(this.wrapSelector('#vp_bdAllocateTo')).val(allocateTo); $(this.wrapSelector('#vp_bdResetIndex')).prop('checked', resetIndex); $(this.wrapSelector('#vp_bdWithoutColumn')).val(withoutColumn); $(this.wrapSelector('.vp-bd-type-box')).hide(); $(this.wrapSelector('.vp-bd-type-box.' + type)).show(); } _loadSelectorInput(tag, dataList) { $(tag).val(dataList.map(data=>data.code).join(',')); $(tag).data('list', dataList) } /** * Open Inner popup page for variable selection * @param {Object} targetSelector */ openVariablePopup(targetSelector) { this.popup.targetSelector = targetSelector; var previousList = this.popup.targetSelector.data('list'); if (previousList) { previousList = previousList.map(data => data.code) } this.popup.MultiSelector = new MultiSelector( this.wrapSelector('.vp-inner-popup-body'), { mode: 'variable', type: ['DataFrame', 'Series'], selectedList: previousList } ); this.openInnerPopup('Select variables'); } /** * Open Inner popup page for column selection * @param {Array} targetVariable * @param {Object} targetSelector * @param {string} title */ openColumnPopup(targetVariable, targetSelector, title='Select Columns') { this.popup.targetVariable = targetVariable; this.popup.targetSelector = targetSelector; var previousList = this.popup.targetSelector.data('list'); if (previousList) { previousList = previousList.map(col => col.code) } this.popup.MultiSelector = new MultiSelector( this.wrapSelector('.vp-inner-popup-body'), { mode: 'columns', parent: targetVariable, selectedList: previousList } ); this.openInnerPopup(title); } handleInnerOk() { // ok input popup var dataList = this.popup.MultiSelector.getDataList(); $(this.popup.targetSelector).val(dataList.map(data => { return data.code }).join(',')); $(this.popup.targetSelector).data('list', dataList); $(this.popup.targetSelector).trigger({ type: 'change', dataList: dataList }); this.closeInnerPopup(); } } return Bind; });