MachineLearningUI.js 9.47 KB
/**
 * Project   : AMDA-NG
 * Name	  : MachineLearningUI.js
 * @class   amdaUI.MachineLearningUI
 * @extends Ext.tab.Panel
 * @brief   Machine Learning Module UI definition (View)
 * @author  Benjamin RENARD
 * @version $Id: MachineLearningUI.js 1093 2022-01-14 15:54:26Z benjamin $
 ******************************************************************************
 *	FT Id	 :   Date   : Name - Description
 ******************************************************************************
 *  :		   :14/01/2022: BRE  - file creation
 */


Ext.define('amdaUI.MachineLearningUI', {
        extend: 'Ext.container.Container',
        alias: 'widget.panelMachineLearning',

	requires: [
		'amdaModel.MLModelObject',
		'amdaModel.MachineLearningRun'
	],

	modelTpl: new Ext.XTemplate(
		'<tpl for=".">',
		'<div class="ml-model-item">{name}</div>',
		'</tpl>'
	),

	modelUI: null,
	predictionUI: null,

	runModelObj: Ext.create('amdaModel.MachineLearningRun'),

    	constructor: function(config) {
		this.init(config);
		this.callParent(arguments);
	},

	selectModel: function(model) {
		var me = this;
		var mlModule = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.machinelearning.id);
		mlModule.getModelInfo(model.get('id'), function(modelinfo){
			if (modelinfo.get('description').length > 0) {
				// Transform description into HTML
				var converter = new showdown.Converter();
				modelinfo.set('description', converter.makeHtml(modelinfo.get('description')));
			}
			me.modelUI.setModel(modelinfo);
		});
	},

	createPredictionUI: function() {
		var me = this;
		this.predictionUI = Ext.create('Ext.form.Panel', {
			items: [
				{
					xtype: 'datefield', id: 'prediction-start', name: 'start', fieldLabel: 'Start Time',
					format: 'Y-m-d\\TH:i:s'
				},
				{
					xtype: 'datefield', id: 'prediction-stop', name: 'stop', fieldLabel: 'Stop Time',
					format: 'Y-m-d\\TH:i:s'
				},
				{
					xtype: 'fieldset', id: 'prediction-args', title: 'Input arguments', hidden: true
				},
				{
					xtype: 'fieldset', id: 'prediction-hyperparameters', title: 'Hyper parameters', hidden: true, enable: false
				},
			],
			setModel: function(modelinfo) {
				var defaultValues = modelinfo.get('defaults');
				if (!defaultValues) defaultValues = {};

				if (defaultValues['start'])
					me.predictionUI.queryById('prediction-start').setValue(defaultValues['start']);

				if (defaultValues['stop'])
					me.predictionUI.queryById('prediction-stop').setValue(defaultValues['stop']);

				var args = modelinfo.get('args');
				if (!args) args = [];
				me.predictionUI.queryById('prediction-args').setVisible(args.length > 0);
				me.predictionUI.queryById('prediction-args').removeAll();
				Ext.Array.each(args, function(arg) {
					if (arg == 'start' || arg == 'stop') {
						return;
					}
					var field = me.predictionUI.queryById('prediction-args').add({
						xtype: 'textfield',
						fieldLabel: arg,
						name: arg,
						allowBlank: false
					});
					if (defaultValues[arg]) {
						field.setValue(defaultValues[arg]);
					}
				});

				var hyperparameters = modelinfo.get('hyperparameters');
				if (!hyperparameters) hyperparameters = [];
				me.predictionUI.queryById('prediction-hyperparameters').setVisible(hyperparameters.length > 0);
				me.predictionUI.queryById('prediction-hyperparameters').removeAll();
				Ext.Array.each(hyperparameters, function(hyperparam) {
					var field = me.predictionUI.queryById('prediction-hyperparameters').add({
						xtype: 'textfield',
						fieldLabel: hyperparam,
						name: hyperparam,
						allowBlank: false
					});
					if (defaultValues[hyperparam]) {
						field.setValue(defaultValues[hyperparam]);
					}
				});
			}
		});
		return this.predictionUI;
	},

	createFittingUI: function() {
		this.fittingUI = Ext.create('Ext.form.Panel', {
			items: [
                        ],
			setModel: function(modelinfo) {

			}
                });
                return this.fittingUI;
	},

	createModelUI: function() {
		var me = this;

		this.modelUI = Ext.create('Ext.panel.Panel', {
			layout: 'border',
			items: [
				{
					xtype: "component",
					id: "ml-model-info",
					region:'north',
					height: 200,
					tpl: '<h1>{name}</h1>{description}',
				},
				{
					xtype: "tabpanel",
					region: "center",
					id: "ml-model-action",
					items: [
						{
							title: "Prediction",
							items: [
								this.createPredictionUI()
							],
							bbar: [
								{xtype: 'button', text: 'Run Prediction', handler: this.runPredictionRequest, scope: me}
							]
						}
						/*,
						{
							title: "Fitting",
							items: [
								this.createFittingUI()
							],
							bbar: [
								'->',
								{xtype: 'button', text: 'Run Fitting', handler: this.runFittingRequest, scope: me}
							]
						}*/
					],
					setModel: function(modelinfo) {
						me.runModelObj.set('moduleId', modelinfo.get('id'));
						me.runModelObj.set('output', modelinfo.get('output'));
						var defaultValues = modelinfo.get('defaults');
						if (!defaultValues) defaultValues = {};

						this.items.each(function(item) {
							item.items.getAt(0).setModel(modelinfo);
						});
					}
				}
			],
			setModel: function(modelinfo) {
				this.queryById('ml-model-info').update(modelinfo.getData());
				this.queryById('ml-model-action').setModel(modelinfo);
			}

		});
		return this.modelUI;
	},

	runPredictionRequest: function() {
		this.runRequest('prediction');
	},

	runFittingRequest: function() {
		this.runRequest('fitting');
	},

	runRequest: function(mode) {
		var currentUI = null;
		switch (mode) {
			case 'fitting':
				currentUI = this.fittingUI;
				 myDesktopApp.errorMsg('Not available in this version');
				return;
			case 'prediction':
				currentUI = this.predictionUI;
				break;
		}
		currentUI.getForm().updateRecord(this.runModelObj);
		this.runModelObj.set('mode', mode);

		var args = {};
		if (currentUI.queryById(mode+'-args').isVisible()) {
			currentUI.queryById(mode+'-args').items.each(function (item) {
				args[item.name] = item.getValue();
			});
		}
		this.runModelObj.set('args', args);

		var hyperparameters = {};
		if (currentUI.queryById(mode+'-hyperparameters').isVisible()) {
			currentUI.queryById(mode+'-hyperparameters').items.each(function (item) {
				hyperparameters[item.name] = item.getValue();
			});
		}
		this.runModelObj.set('hyperparameters', hyperparameters);

		loadMask.show(false);
		var me = this;
		AmdaAction.runMLModel(this.runModelObj.getJsonValues(), function (result, e) {
			loadMask.hide();
			if (!e.status){
				myDesktopApp.errorMsg('Internal error during request');
				return;
			}
			if (!result.success){
				myDesktopApp.errorMsg(result.message);
				return;
			}

			//new object to attach to new bkgJobNode
			var newobj = Ext.copyTo({}, me.runModelObj.getJsonValues(), me.runModelObj.propertiesToCopy);
			newobj.resultId  = result.result;
			newobj.folderId  = result.folder;
			newobj.processId = result.id;
			newobj.tabId     = result.tabId;
			newobj.name      = result.name;
			newobj.request_obj = result.request_obj;
			newobj = Ext.create(me.runModelObj.$className, newobj);

			//create bkgJobNode
			var newNode = Ext.create('amdaModel.BkgJobNode', {
				info : result.info,
				jobType : 'machinelearning',
				processId : result.id,
				id : result.id,
				text : result.name,
				status : result.status,
				start : result.start,
				stop : result.stop,
				tabId : result.tabId,
				leaf : true,
				object : newobj
			});

			//attach bkgJobNode to explorer
			switch (result.status)
			{
				case amdaModel.BkgJobNode.STATUS_LIST.DONE :
					newNode.createJobNode(true);
					newNode.editNode(true, false);
					break;
				case amdaModel.BkgJobNode.STATUS_LIST.IN_PROGRESS :
					newNode.createJobNode(false);
					break;
				default:
					newNode.createJobNode(true);
			}

		});

	},

	getDefaultModelsDataView: function() {
		var modelStore = Ext.create('Ext.data.Store', {
			fields: [
				{name: 'id', type: 'string'},
				{name: 'name', type: 'string'}
			],
			autoLoad: true,
			proxy: {
				type: 'direct',
				api: {read: AmdaAction.readDefaultMLModels},
				reader:
				{
					type: 'json',
					root: 'result',
					totalProperty: 'totalCount'
				}
			}
		});

		return {
			xtype: "dataview",
			store: modelStore,
			tpl: this.modelTpl,
			trackOver: true,
			overItemCls: 'ml-model-item-over',
			itemSelector: 'div.ml-model-item',
			autoScroll : true,
			emptyText: 'No models available',
			listeners : {
				itemclick: function( dataview, record, item, index, e, eOpts )
				{
					this.selectModel(record);
				},
				scope: this
			}

		};
	},

	getUserModelsDataView: function() {
		return {
			xtype: "dataview",
			store: Ext.create('Ext.data.Store', {
				model: 'amdaModel.MLModelObject'
			}),
			tpl: this.modelTpl,
			itemSelector: 'div.ml-model-wrap',
			emptyText: 'No models available'
		};
	},

	init: function(config) {
		var me = this;

		var myConf = {
			plain: true,
            		width: 800,
            		height: 600,
			layout: 'border',
			items: [
				{
					xtype: "panel",
					region:'west',
					width: 200,
					layout: {
						type: 'accordion',
						titleCollapse: true,
						animate: true,
						activeOnTop: true
					},
					items: [
						{
							title: 'Default Models',
							items: [
								this.getDefaultModelsDataView()
							]
						}/*,
						{
							title: 'User Models',
							items: [
								this.getUserModelsDataView()
							]
						}*/
					],
				},
				{
					xtype: "panel",
					region: 'center',
					layout: 'fit',
					items: [
						this.createModelUI()
					]
				}
			]
		};
		Ext.apply (this, Ext.apply (arguments, myConf));
	}
});