From d18b535dda83a475a400106be1382318be8509dd Mon Sep 17 00:00:00 2001 From: elena <ebudnik@irap.omp.eu> Date: Thu, 23 Jul 2015 18:55:59 +0200 Subject: [PATCH] catalog draft + context menu small modifs --- generic_data/jobs.json | 4 ++-- js/app/AmdaApp.js | 14 +++++++++++--- js/app/controllers/ExplorerModule.js | 4 +++- js/app/controllers/StatisticsModule.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ js/app/models/AliasNode.js | 9 +-------- js/app/models/BkgJobNode.js | 27 ++++++++++++++------------- js/app/models/Catalog.js | 9 +++------ js/app/models/CatalogNode.js | 43 +++++++++++++++++++++++++++++++++++++++++-- js/app/models/DerivedParamNode.js | 8 ++------ js/app/models/InteractiveNode.js | 15 ++++++++++++--- js/app/models/LocalParamNode.js | 6 +----- js/app/models/MyDataParamNode.js | 11 ++++------- js/app/models/PlotNode.js | 9 +-------- js/app/models/SearchNode.js | 9 +-------- js/app/models/Statistics.js | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ js/app/models/StatisticsNode.js | 40 ++++++++++++++++++++++++++++++++++++++++ js/app/models/TimeTableNode.js | 23 +++++++++++++---------- js/app/views/CatalogUI.js | 699 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- js/app/views/StatisticsUI.js | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ js/app/views/TabResultUI.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- js/app/views/TimeTableUI.js | 3 ++- js/resources/css/amda.css | 9 +++++++++ js/resources/images/16x16/statistics.png | Bin 0 -> 738 bytes js/resources/images/16x16/visu_catalog.png | Bin 0 -> 838 bytes js/resources/images/64x64/statistics.png | Bin 0 -> 1522 bytes js/resources/images/64x64/visu_catalog.png | Bin 0 -> 3765 bytes php/classes/AmdaAction.php | 47 ++++++++++++++++++++++++++++++++++++++++------- php/classes/CatalogCacheMgr.php | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ php/classes/CatalogMgr.php | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ php/classes/IntervalCacheObject.php | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ php/classes/TimeTableCacheMgr.php | 647 ++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- php/classes/TimeTableCacheObject.php | 509 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ php/classes/TimeTableMgr.php | 42 ++++++++++++++++++++++++++++++------------ 33 files changed, 2307 insertions(+), 1055 deletions(-) create mode 100644 js/app/controllers/StatisticsModule.js create mode 100644 js/app/models/Statistics.js create mode 100644 js/app/models/StatisticsNode.js create mode 100644 js/app/views/StatisticsUI.js create mode 100644 js/resources/images/16x16/statistics.png create mode 100644 js/resources/images/16x16/visu_catalog.png create mode 100644 js/resources/images/64x64/statistics.png create mode 100644 js/resources/images/64x64/visu_catalog.png create mode 100644 php/classes/CatalogCacheMgr.php create mode 100644 php/classes/CatalogMgr.php create mode 100644 php/classes/IntervalCacheObject.php create mode 100644 php/classes/TimeTableCacheObject.php diff --git a/generic_data/jobs.json b/generic_data/jobs.json index 107be9a..4734f8d 100644 --- a/generic_data/jobs.json +++ b/generic_data/jobs.json @@ -3,14 +3,14 @@ [{"nodeType" : "bkgWorks","text" : "Plot","id" : "resPlot-treeRootNode" }, {"nodeType" : "bkgWorks","text" : "Download","id" : "resDown-treeRootNode"}, {"nodeType" : "bkgWorks","text" : "Data Mining","id" : "resSearch-treeRootNode"}, - {"nodeType" : "bkgWorks","text" : "Catalog","id" : "resCatalog-treeRootNode"} + {"nodeType" : "bkgWorks","text" : "Statistics","id" : "resStatistics-treeRootNode"} ] }, {"nodeType" : "bkgWorks", "text" : "Jobs in Progress", "id" : "bkgjobs-treeRootNode", "children" : [{"nodeType" : "bkgWorks","text" : "Plot","id" : "bkgPlot-treeRootNode" }, {"nodeType" : "bkgWorks","text" : "Download","id" : "bkgDown-treeRootNode"}, {"nodeType" : "bkgWorks","text" : "Data Mining","id" : "bkgSearch-treeRootNode"}, - {"nodeType" : "bkgWorks","text" : "Catalog","id" : "bkgCatalog-treeRootNode"} + {"nodeType" : "bkgWorks","text" : "Statistics","id" : "bkgStatistics-treeRootNode"} ] } ]} diff --git a/js/app/AmdaApp.js b/js/app/AmdaApp.js index cdde7c9..5850111 100755 --- a/js/app/AmdaApp.js +++ b/js/app/AmdaApp.js @@ -20,10 +20,17 @@ Ext.define('amdaApp.AmdaApp', { ], dynamicModules: { + statistics : { + id : 'statistics-win', + icon : 'icon-statistics', + title : 'Statistics', + source : 'amdaDesktop.StatisticsModule', + useLauncher : true + }, catalog : { id : 'catalog-win', icon : 'icon-catalog', - title : 'Generate/Edit catalogs', + title : 'Manage catalogs', source : 'amdaDesktop.CatalogModule', useLauncher : true }, @@ -275,12 +282,13 @@ Ext.define('amdaApp.AmdaApp', { { name: 'Create/Modify parameter', iconCls: 'edit', module: 'param-win' }, { name: 'Plot data', iconCls: 'plot', module: 'plot-win'}, { name: 'Data mining', iconCls: 'search', module: 'search-win'}, + { name: 'Statistics', iconCls: 'statistics', module: 'statistics-win'}, { name: 'Download data', iconCls: 'download_manager', module: 'down-win'}, { name: 'Upload data', iconCls: 'mydata', module: 'up-win'}, { name: 'Manage TimeTables', iconCls: 'timeTable', module: 'timetab-win' }, { name: 'TimeTables operations', iconCls: 'operations', module: 'ttsOpe-win' }, - { name: 'Generate/Edit catalogs', iconCls: 'catalog', module: 'catalog-win'}, - // { name: 'Visualize catalogs', iconCls: 'visu_catalog', module: 'visucatalog-win'}, + { name: 'Manage catalogs', iconCls: 'catalog', module: 'catalog-win'}, + { name: 'Visualize catalogs', iconCls: 'visu_catalog', module: 'visucatalog-win'}, { name: 'Interoperability', iconCls: 'interop', module: 'interop-win' } ] }), diff --git a/js/app/controllers/ExplorerModule.js b/js/app/controllers/ExplorerModule.js index 16182a6..c58d252 100644 --- a/js/app/controllers/ExplorerModule.js +++ b/js/app/controllers/ExplorerModule.js @@ -23,6 +23,7 @@ Ext.define('amdaDesktop.ExplorerModule', { 'amdaModel.AliasNode', 'amdaModel.TimeTableNode', 'amdaModel.CatalogNode', + 'amdaModel.StatisticsNode', // singleton; not shown in the tree 'amdaModel.sharedTimeTableNode', 'amdaModel.MyDataParamNode', 'amdaModel.MyDataNode', @@ -35,7 +36,8 @@ Ext.define('amdaDesktop.ExplorerModule', { 'amdaModel.Plot', 'amdaModel.Download', 'amdaModel.TimeTable', - 'amdaModel.Catalog', + 'amdaModel.Catalog', + 'amdaModel.Statistics', 'amdaModel.FileObject', 'amdaModel.FileParamObject', 'amdaModel.FilterInfo' diff --git a/js/app/controllers/StatisticsModule.js b/js/app/controllers/StatisticsModule.js new file mode 100644 index 0000000..c6f55d5 --- /dev/null +++ b/js/app/controllers/StatisticsModule.js @@ -0,0 +1,45 @@ +/** + * Project AMDA-NG + * Name StatisticsModule.js + * @class amdaDesktop.StatisticsModule + * @extends amdaDesktop.InteractiveModule + * @brief Statistics Module controller definition + * @author elena + */ + +Ext.define('amdaDesktop.StatisticsModule', { + extend: 'amdaDesktop.InteractiveModule', + + requires: [ + 'amdaUI.StatisticsUI' + ], + + contentId : 'statisticsUI', + + /** + * @cfg {String} data models + * @required + */ + nodeDataModel : 'amdaModel.StatisticsNode', + objectDataModel : 'amdaModel.Statistics', + /** + * @cfg {String} window definitions + * @required + */ + width : 550, + height: 550, + uiType : 'panelStatistics', + helpTitle : 'Help on Statistics Module', + helpFile : 'statisticsHelp', + /** + * @override + */ + createWindow : function() { + if (!this.linkedNode){ + this.setLinkedNode(amdaModel.StatisticsNode); + } + this.callParent(arguments); + }, + + +}); diff --git a/js/app/models/AliasNode.js b/js/app/models/AliasNode.js index 7132a5a..aff762d 100644 --- a/js/app/models/AliasNode.js +++ b/js/app/models/AliasNode.js @@ -52,14 +52,7 @@ Ext.define('amdaModel.AliasNode', { return menuItems; }, - - getAllContextMenuItems: function(){ - return this.allMenuItems(); - }, - - getMultiContextMenuItems: function(){ - return this.allMenuMultiItems(amdaModel.AliasNode.objectName); - }, + create: function(alias, param) { AmdaAction.createObject({name: alias, param: param, diff --git a/js/app/models/BkgJobNode.js b/js/app/models/BkgJobNode.js index 1ceec5f..dace07b 100644 --- a/js/app/models/BkgJobNode.js +++ b/js/app/models/BkgJobNode.js @@ -6,11 +6,6 @@ * @brief Basic Model of Node corresponding to Amda processes in background * @author elena * @version $Id: BkgJobNode.js 1963 2013-12-06 17:50:37Z elena $ - * @todo - ******************************************************************************* - * FT Id : Date : Name - Description - ******************************************************************************* - * : :08/07/2011: elena creation */ Ext.define('amdaModel.BkgJobNode', { @@ -25,19 +20,19 @@ Ext.define('amdaModel.BkgJobNode', { PLOT: 'request',//'plot', CONDITION: 'condition', DOWNLOAD: 'download', - CATALOG: 'catalog' + STATISTICS: 'statistics' }, JOB_ROOT_NODE: { PLOT: 'bkgPlot-treeRootNode', CONDITION: 'bkgSearch-treeRootNode', DOWNLOAD: 'bkgDown-treeRootNode', - CATALOG: 'bkgCatalog-treeRootNode' + STATISTICS: 'bkgStatistics-treeRootNode' }, RES_ROOT_NODE: { PLOT: 'resPlot-treeRootNode', CONDITION: 'resSearch-treeRootNode', DOWNLOAD: 'resDown-treeRootNode', - CATALOG: 'resCatalog-treeRootNode' + STATISTICS: 'resStatistics-treeRootNode' }, STATUS_LIST: { IN_PROGRESS: 'in_progress', @@ -225,12 +220,18 @@ Ext.define('amdaModel.BkgJobNode', { var obj = null; switch (this.get('jobType')) { - case 'condition' : + case 'condition' : obj = Ext.create('amdaModel.Search', {name: result.name, resultId : result.result, folderId : result.folder}); break; + case 'statistics' : + obj = Ext.create('amdaModel.Statistics', + {name: result.name, + resultId : result.result, + folderId : result.folder}); + break; case 'request' : obj = Ext.create('amdaModel.Plot', {name: result.name, format: result.format, @@ -355,8 +356,8 @@ Ext.define('amdaModel.BkgJobNode', { case amdaModel.BkgJobNode.JOB_TYPES.DOWNLOAD: rootNodeId = amdaModel.BkgJobNode.JOB_ROOT_NODE.DOWNLOAD; break; - case amdaModel.BkgJobNode.JOB_TYPES.CATALOG: - rootNodeId = amdaModel.BkgJobNode.JOB_ROOT_NODE.CATALOG; + case amdaModel.BkgJobNode.JOB_TYPES.STATISTICS: + rootNodeId = amdaModel.BkgJobNode.JOB_ROOT_NODE.STATISTICS; break; default: break; @@ -373,8 +374,8 @@ Ext.define('amdaModel.BkgJobNode', { case amdaModel.BkgJobNode.JOB_TYPES.DOWNLOAD: rootNodeId = amdaModel.BkgJobNode.RES_ROOT_NODE.DOWNLOAD; break; - case amdaModel.BkgJobNode.JOB_TYPES.CATALOG: - rootNodeId = amdaModel.BkgJobNode.RES_ROOT_NODE.CATALOG; + case amdaModel.BkgJobNode.JOB_TYPES.STATISTICS: + rootNodeId = amdaModel.BkgJobNode.RES_ROOT_NODE.STATISTICS; break; default: break; diff --git a/js/app/models/Catalog.js b/js/app/models/Catalog.js index f43acb7..afc1c31 100644 --- a/js/app/models/Catalog.js +++ b/js/app/models/Catalog.js @@ -14,9 +14,7 @@ Ext.define('amdaModel.Catalog', { extend: 'amdaModel.TimeTable', fields : [ - { name: 'parameter' }, - { name: 'timeTables' }//, -// { name: 'timesrc', type: 'string', defaultValue : "TimeTable" } + { name: 'parameters' } ], getJsonValues : function (hasId) { @@ -25,7 +23,7 @@ Ext.define('amdaModel.Catalog', { values.id = this.get('id'); } - values.timesrc = 'TimeTable'; + values.timesrc = this.get('timesrc'); values.name = this.get('name'); values.created = this.get('created'); @@ -40,8 +38,7 @@ Ext.define('amdaModel.Catalog', { values.folderId = this.get('folderId'); values.nbIntervals = this.get('nbIntervals'); values.cacheToken = this.get('cacheToken'); - values.parameter = this.get('parameter'); - values.timeTables = this.get('timetable'); + values.parameters = this.get('parameters'); values.leaf = true; values.nodeType = amdaModel.CatalogNode.nodeType; return values; diff --git a/js/app/models/CatalogNode.js b/js/app/models/CatalogNode.js index 3108f74..a33173b 100644 --- a/js/app/models/CatalogNode.js +++ b/js/app/models/CatalogNode.js @@ -9,7 +9,7 @@ Ext.define('amdaModel.CatalogNode', { - extend: 'amdaModel.ExecutableNode', + extend: 'amdaModel.TimeTableNode', statics: { nodeType: 'catalog', @@ -23,6 +23,45 @@ Ext.define('amdaModel.CatalogNode', { this.set('ownerTreeId',amdaUI.ExplorerUI.RESRC_TAB.TREE_ID); this.set('objectDataModel',amdaModel.Catalog.$className); if (this.get('leaf')) this.set('iconCls', 'icon-catalog'); + }, + + localMenuItems : function() { + var menuItems = + [/*{ + fnId : 'dire-shareNode', + text : 'Share content', + hidden : true + },{ + fnId : 'leaf-shareLeaf', + text : 'Share '+this.self.objectName, + hidden : true + },*/{ + fnId : 'leaf-download', + text : 'Download '+ this.self.objectName, + hidden : true + }/*,{ + fnId : 'leaf-operations', + text : 'Operations', + hidden : true + }*/]; + + return menuItems; + }, + + localMultiMenuItems : function() { + var menuItems = + [/*{ + fnId : 'mult-shareMulti', + text : 'Share selected '+this.self.objectName+'s' + },*/{ + fnId : 'mult-downloadMulti', + text : 'Download selected '+this.self.objectName+'s' + }/*,{ + fnId : 'mult-operationsMulti', + text : 'Operations' + }*/]; + + return menuItems; } - + }); diff --git a/js/app/models/DerivedParamNode.js b/js/app/models/DerivedParamNode.js index a596de3..4393a87 100644 --- a/js/app/models/DerivedParamNode.js +++ b/js/app/models/DerivedParamNode.js @@ -53,15 +53,11 @@ Ext.define('amdaModel.DerivedParamNode', { getAllContextMenuItems: function(){ - var menuItems = this.allMenuItems(amdaModel.DerivedParamNode.objectName); + var menuItems = this.allMenuItems(); var locMenuItems = this.localMenuItems(); return Ext.Array.merge(menuItems,locMenuItems); - }, - - getMultiContextMenuItems: function(){ - return this.allMenuMultiItems(amdaModel.DerivedParamNode.objectName); - }, + }, isParameter : function(){ return this.get('isParameter'); diff --git a/js/app/models/InteractiveNode.js b/js/app/models/InteractiveNode.js index 3c8ceba..ded9d7d 100644 --- a/js/app/models/InteractiveNode.js +++ b/js/app/models/InteractiveNode.js @@ -369,7 +369,8 @@ Ext.define('amdaModel.InteractiveNode', { * Generic part of Context Menu * */ - allMenuItems : function(src) { + allMenuItems : function() { + var src = this.self.objectName; var menuItems = [ { fnId : 'root-createLeaf', @@ -402,16 +403,24 @@ Ext.define('amdaModel.InteractiveNode', { return menuItems; }, - allMenuMultiItems : function(src) { + allMenuMultiItems : function() { var menuMulti = [ { fnId : 'mult-deleteMulti', - text : 'Delete selected '+ src+'s' + text : 'Delete selected ' + this.self.objectName + 's' } ]; return menuMulti; }, + getAllContextMenuItems: function(){ + return this.allMenuItems(); + }, + + getMultiContextMenuItems: function(){ + return this.allMenuMultiItems(); + }, + /** * default implementation * no menu display if there's no override of this function diff --git a/js/app/models/LocalParamNode.js b/js/app/models/LocalParamNode.js index 495690b..afe6e10 100644 --- a/js/app/models/LocalParamNode.js +++ b/js/app/models/LocalParamNode.js @@ -126,11 +126,7 @@ Ext.define('amdaModel.LocalParamNode', return menuItems; }, - getAllContextMenuItems: function() - { - return this.allMenuItems(); - }, - + onMenuItemClick : function(menu,item,event) { switch (item.fnId) diff --git a/js/app/models/MyDataParamNode.js b/js/app/models/MyDataParamNode.js index 8d11ab7..69bc238 100644 --- a/js/app/models/MyDataParamNode.js +++ b/js/app/models/MyDataParamNode.js @@ -23,7 +23,8 @@ Ext.define('amdaModel.MyDataParamNode', { ], statics:{ - nodeType: 'myDataParam' + nodeType: 'myDataParam', + objectName : 'Parameter' }, constructor : function(config){ @@ -75,12 +76,8 @@ Ext.define('amdaModel.MyDataParamNode', { return menuItems; }, - getAllContextMenuItems: function(){ - - var menuItems = this.allMenuItems('Parameter'); - var locMenuItems = this.localMenuItems(); - - return Ext.Array.merge(menuItems,locMenuItems); + getAllContextMenuItems: function(){ + return this.localMenuItems(); }, onMenuItemClick : function(menu,item,event) { diff --git a/js/app/models/PlotNode.js b/js/app/models/PlotNode.js index b0229ef..2002258 100644 --- a/js/app/models/PlotNode.js +++ b/js/app/models/PlotNode.js @@ -72,14 +72,7 @@ Ext.define('amdaModel.PlotNode', { return menuMulti; }, - getAllContextMenuItems: function(){ - return this.allMenuItems(); - }, - - getMultiContextMenuItems: function(){ - return this.allMenuMultiItems(); - }, - + onMenuItemClick : function(menu,item,event) { // fnId parsing : var fnId = Ext.util.Format.substr(item.fnId, 5, item.fnId.length); diff --git a/js/app/models/SearchNode.js b/js/app/models/SearchNode.js index 67fc049..0762b4a 100644 --- a/js/app/models/SearchNode.js +++ b/js/app/models/SearchNode.js @@ -70,14 +70,7 @@ Ext.define('amdaModel.SearchNode', { return menuMulti; }, - getAllContextMenuItems: function(){ - return this.allMenuItems(); - }, - - getMultiContextMenuItems: function(){ - return this.allMenuMultiItems(); - }, - + onMenuItemClick : function(menu,item,event) { // fnId parsing : var fnId = Ext.util.Format.substr(item.fnId, 5, item.fnId.length); diff --git a/js/app/models/Statistics.js b/js/app/models/Statistics.js new file mode 100644 index 0000000..769e90a --- /dev/null +++ b/js/app/models/Statistics.js @@ -0,0 +1,75 @@ +/** + * Project : AMDA-NG + * Name : Statistics.js + * Description : Statistics Object Definition + * @class amdaModel.Statistics + * @extends amdaModel.TimeTable + * @author elena + */ + + + +Ext.define('amdaModel.Statistics', { + + extend: 'amdaModel.AmdaTimeObject', + + fields : [ + { name: 'parameter' } , + { name: 'description' } + // { name: 'timesrc', type: 'string'} + ], + + getJsonValues : function (hasId) { + var values = new Object(); + if (hasId) { + values.id = this.get('id'); + } + + values.timesrc = this.get('timesrc'); + values.name = 'test'; //this.get('name'); + + + if (this.get('description').match(/[a-z,0-9]/gi) != null) { + values.description = this.get('description'); + } +// if (this.get('history').match(/[a-z,0-9]/gi) != null) { +// values.history = this.get('history'); +// } + values.objName = this.get('objName'); + values.objFormat = this.get('objFormat'); + +// values.cacheToken = this.get('cacheToken'); + values.parameter = this.get('parameter'); + if (values.timesrc == amdaModel.AmdaTimeObject.inputTimeSrc[0]){ + // get complete timeTables collection + var timeTables = this.get('timeTables'); + // init an empty array for timeTables + values.timeTables=[]; + // for each interval record + Ext.Array.each(timeTables, function(item, index, all){ + if (!item.$className) { + values.timeTables[index] = {timeTableName : item.timeTableName, id : item.id}; + } + // get Json simplified value + else { + values.timeTables[index] = item.getJsonValues(); + } + }); + } else { + values.startDate = this.get('startDate'); + values.stopDate = this.get('stopDate'); + values.durationDay = this.get('durationDay'); + values.durationHour = this.get('durationHour'); + values.durationMin = this.get('durationMin'); + values.durationSec = this.get('durationSec'); + } + + values.leaf = true; + values.nodeType = 'statistics'; + + return values; + } + + + +}); \ No newline at end of file diff --git a/js/app/models/StatisticsNode.js b/js/app/models/StatisticsNode.js new file mode 100644 index 0000000..dce3b2e --- /dev/null +++ b/js/app/models/StatisticsNode.js @@ -0,0 +1,40 @@ +/** + * Project : AMDA-NG + * Name : StatisticsNode.js + * @class amdaModel.StatisticsNode + * @extends amdaModel.TimeTableNode + * @brief Basic Model of Node corresponding to a amda statistics operation + * @author elena + */ + +Ext.define('amdaModel.StatisticsNode', { + + extend: 'amdaModel.ExecutableNode', + + singleton: true, + + + fields: [ {name : 'downloadType', type : 'string'}, + {name: 'object', type: 'object'}, + {name: 'realLinkedNode', type: 'amdaModel.AmdaNode'}, + {name: 'moduleId', type: 'string', defaultValue:'statistics-win'}, + {name: 'nodeType', type: 'string', defaultValue: 'statistics'}, + {name: 'objectDataModel', type: 'string', defaultValue:'amdaModel.Statistics'}, + {name: 'jobNode', type: 'string', defaultValue: 'amdaModel.BkgJobNode'} + ], + + isExecutable: function(){ + return true; + } + /*, + + constructor : function(config){// + this.callParent(arguments); + this.set('nodeType',amdaModel.StatisticsNode.nodeType); + this.set('moduleId',myDesktopApp.dynamicModules.statistics.id); + this.set('ownerTreeId',amdaUI.ExplorerUI.OPE_TAB.TREE_ID); + this.set('objectDataModel',amdaModel.Statistics.$className); +// if (this.get('leaf')) this.set('iconCls', 'icon-catalog'); + } + */ +}); diff --git a/js/app/models/TimeTableNode.js b/js/app/models/TimeTableNode.js index 974a3e9..3d50811 100644 --- a/js/app/models/TimeTableNode.js +++ b/js/app/models/TimeTableNode.js @@ -60,10 +60,10 @@ Ext.define('amdaModel.TimeTableNode', { var menuItems = [/*{ fnId : 'mult-shareMulti', - text : 'Share selected '+amdaModel.TimeTableNode.objectName+'s' + text : 'Share selected '+this.self.objectName+'s' },*/{ fnId : 'mult-downloadMulti', - text : 'Download selected '+amdaModel.TimeTableNode.objectName+'s' + text : 'Download selected '+this.self.objectName+'s' },{ fnId : 'mult-operationsMulti', text : 'Operations' @@ -73,18 +73,21 @@ Ext.define('amdaModel.TimeTableNode', { }, getAllContextMenuItems: function(){ - - var menuItems = this.allMenuItems(amdaModel.TimeTableNode.objectName); + + var menuItems = this.allMenuItems(); var locMenuItems = this.localMenuItems(); - return Ext.Array.merge(menuItems,locMenuItems); + + return Ext.Array.merge(menuItems,locMenuItems); }, - getMultiContextMenuItems: function(){ - var multiMenu = this.allMenuMultiItems(amdaModel.TimeTableNode.objectName); - var locMultiMenuItems = this.localMultiMenuItems(); - return Ext.Array.merge(multiMenu,locMultiMenuItems); + getMultiContextMenuItems: function(){ + + var menuItems = this.allMenuMultiItems(); + var locMenuItems = this.localMultiMenuItems(); + + return Ext.Array.merge(menuItems,locMenuItems); }, - + onMenuItemClick : function(menu,item,event) { this.callParent(arguments); diff --git a/js/app/views/CatalogUI.js b/js/app/views/CatalogUI.js index 8dd1286..1ade353 100644 --- a/js/app/views/CatalogUI.js +++ b/js/app/views/CatalogUI.js @@ -10,103 +10,255 @@ Ext.define('amdaUI.CatalogUI', { extend: 'Ext.container.Container', alias: 'widget.panelCatalog', + + isCatalog : true, - requires : [ -// 'amdaModel.Function' - ], - - statics : { -// functionStore : null - }, - constructor: function(config) { - this.init(config); - this.callParent(arguments); - // if (this.object) this.loadObject(); + this.init(config); + this.callParent(arguments);; + if (this.object) this.loadObject(); }, - addParam : function(ParamName,isLeaf) - { - var r = Ext.create('amdaModel.AmdaObject', { name: ParamName }); - this.paramGrid.getStore().add(r); - this.paramGrid.getSelectionModel().select(this.paramGrid.getStore().getCount()-1); - }, - addTT : function(TTname,TTid) - { - Ext.define('tempObject', { - extend: 'Ext.data.Model', - fields: [ - {name: 'name', type: 'string'}, - {name: 'hidden_id', type: 'string'} - ]}); - var r = Ext.create('tempObject', { name:TTname, hidden_id : TTid }); - this.ttGrid.getStore().add(r); - this.ttGrid.getSelectionModel().select(this.paramGrid.getStore().getCount()-1); - - }, - - generateCatalog : function(){ - var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.catalog.id); - if (module) - module.linkedNode.execute(); + /** + * set params description into this.object + */ + setParamInfo : function(parameters) { + + var params = []; + Ext.Array.each(parameters, function(item, index) { + params[index] = item; + }, this); + + this.object.set('parameters', params); + }, /** * update this.object from form - */ + */ + updateObject : function(){ + // get the basic form + var basicForm = this.formPanel.getForm(); + var updateStatus = true; + + var fieldsWithoutName = basicForm.getFields().items; + Ext.Array.each(fieldsWithoutName, function(item, index,allItems){ + if(item !== this.fieldName) { + if (!item.isValid()) { + // set update isn't allowed + updateStatus = false; + } + } + }, this); + // if the update is allowed + if (updateStatus) { + /// real object update + // update TimeTable object with the content of form + basicForm.updateRecord(this.object); + } + // return the update status + return updateStatus; + }, - updateObject : function(){ - // get the basic form of the left - var basicForm = this.formPanel.items.items[0].getForm(); - - var formValues = basicForm.getValues(); - this.object.set('name',formValues.name); - this.object.set('description',formValues.description); - - var recs = this.paramGrid.getStore().getNewRecords(); - var paramArr = new Array(); - Ext.Array.each(recs, function(rec, index,allItems){ - var obj = new Object(); - obj.param = rec.get('name'); - obj.function = rec.get('function'); - paramArr.push(obj); - }); - this.object.set('parameter', paramArr); + + updateCount : function() { + this.object.set('nbIntervals',this.TTGrid.getStore().getTotalCount()); + this.formPanel.getForm().findField('nbIntervals').setValue(this.object.get('nbIntervals')); + }, + + /** + * load object catalog into this view + */ + loadObject : function(){ + // load object into form + this.formPanel.getForm().loadRecord(this.object); + + this.status = null; + + var me = this; + + var onAfterInit = function(result, e) { + + if (!result || !result.success) + { + if (result.message) + myDesktopApp.errorMsg(result.message); + else + myDesktopApp.errorMsg('Unknown error during catalog cache initialisation'); + return; + } + + + var fields = [], columns = [], i = 2, width, index; - // hidden_id - if real 'id' - getNewRecords (and other getRecords) methods doesn't work - var tts = this.ttGrid.getStore().getNewRecords(); - var ttArr = new Array(); - Ext.Array.each(tts, function(rec, index, allItems){ - var obj = new Object(); - obj.id = rec.get('hidden_id'); - ttArr.push(obj); - }); - this.object.set('timetable', ttArr); + fields[0] = Ext.create('Ext.data.Field',{ name : 'start' }); + fields[1] = Ext.create('Ext.data.Field',{ name : 'stop' }); + + columns[0] = Ext.create('Ext.grid.column.RowNumberer'); - var updateStatus = true; + columns[1] = Ext.create('Ext.grid.column.Column', { text: 'Start Time', sortable : false, dataIndex: 'start', + width : 120, menuDisabled: true }); + columns[2] = Ext.create('Ext.grid.column.Column', { text: 'Stop Time', sortable : false, dataIndex: 'stop', + width : 120, menuDisabled: true }); - return updateStatus; + Ext.Array.each(result.parameters, function(obj) { + index = 'param'+i.toString(); + fields[i] = Ext.create('Ext.data.Field',{ name : index }); + width = 50. * parseInt(obj.size); + columns[i+1] = Ext.create('Ext.grid.column.Column', { text: obj.name, sortable : false, dataIndex: index, + width : width, menuDisabled: true }); + i++; + }); + + + var store = Ext.create('Ext.data.Store', { + fields: fields, + autoDestroy: false, + pageSize : 200, + buffered : true, + purgePageCount: 0, + remoteSort: true, + proxy: { + type: 'direct', + api : + { + read : AmdaAction.readTTCacheIntervals + }, + // remplir automatiquement tt, sharedtt , catalog, shared catalog + extraParams : {'typeTT' : 'catalog'}, + reader: + { + type: 'json', + root: 'intervals', + totalProperty : 'totalCount' + } + }, + listeners: { + scope : me, + load: function(store,records) { + // myDesktopApp.EventManager.fireEvent('refresh'); + me.TTGrid.getView().refresh(); + me.TTGrid.getSelectionModel().refresh(); + me.updateCount(); + //Statistical plugin + // this.fireEvent("refresh"); + } + } + }); + + me.TTGrid.reconfigure(store, columns); + +// +// me.TTGrid.getSelectionModel().deselectAll(); +// +// // clear filters +// me.TTGrid.getStore().clearFilter(true); +// +// //clear sort +// me.TTGrid.getStore().sorters.clear(); +// //me.TTGrid.getStore().sorters = new Ext.util.MixedCollection(); +// + //set cache token to the Catalog object + me.object.set('cacheToken', result.token); + me.setParamInfo(result.parameters); + + me.TTGrid.getStore().load(); + + me.status = result.status; + + }; + + if (this.object.get('fromPlugin')) + { + if (this.object.get('objFormat') && this.object.get('objFormat') != '') + { + //From uploaded file + //AmdaAction.initTTCacheFromUploadedFile(this.object.get('objName'), this.object.get('objFormat'), onAfterInit); + } + else + { + //From tmp object (ie Search result) + AmdaAction.initTTCacheFromTmpObject(this.object.get('folderId'), this.object.get('objName'), this.isCatalog, onAfterInit); + } + } + else + { + var typeTT = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.tt.id).linkedNode.data.nodeType; + if (this.object.get('id') == '') + { + //Init empty cache + //AmdaAction.initTTCache(onAfterInit); + } + else + { + //From existing TT file + //AmdaAction.initTTCacheFromTT(this.object.get('id'), typeTT, onAfterInit); + } + } + }, + checkIntervalsStatusForSave : function(onStatusOk) { + onStatusOk(); + }, + + /* + * save method called by Save button + */ + saveProcess : function(toRename){ + var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.catalog.id); + + // if the name has been modified this is a creation + if (this.fclose()) { + + if (this.object.isModified('name') || this.object.get('fromPlugin')) { + + // if object already has an id : it's a 'rename' of an existing + if (this.object.get('id')){ + // the context Node is the parent node of current edited one + var contextNode = module.linkedNode.parentNode; + // link a new node to the TimeTableModule + module.createLinkedNode(); + // set the contextNode + module.linkedNode.set('contextNode',contextNode); + // create a new object linked + module.createObject(this.object.getJsonValues()); + + var obj = module.linkedNode.get('object'); + // synchronisation of objects + this.object = obj; + if (toRename) module.linkedNode.toRename = true; + } + module.linkedNode.create({callback : function() {module.linkedNode.update();}, scope : this}); + } else { + //update + module.linkedNode.update(); + } + } }, + /** + * Check if changes were made before closing window + * @return true if changes + */ + fclose : function() { + if (this.status == null) + return false; + + var isDirty = this.formPanel.getForm().isDirty() || (this.status.isModified) || (this.status.nbModified > 0) || (this.status.nbNew > 0); + return isDirty; + }, + + init : function (config) { -// var functions = Ext.create('Ext.data.Store', { -// fields: ['id', 'name'], -// data : [ -// {"id":"min", "name":"MIN"}, -// {"id":"max", "name":"MAX"}, -// {"id":"mean","name":"MEAN"} -// ] -// }); - + this.object = config.object; + this.fieldName = new Ext.form.field.Text({ - fieldLabel: 'Name*', + fieldLabel: 'Name', allowBlank : false, stripCharsRe: /(^\s+|\s+$)/g, emptyText: 'Please no spaces!', name: 'name', - anchor: '100%', validateOnChange: false, validateOnBlur: false, validFlag: false, @@ -115,209 +267,78 @@ Ext.define('amdaUI.CatalogUI', { } }); - var ttStore = Ext.create('Ext.data.Store', - { - fields: [ 'name', 'hidden_id'] - }); - - this.ttGrid = Ext.create('Ext.grid.Panel', { - title: 'Select Time Table', - height: 100, - store : ttStore, - columns: [ - { xtype: 'rownumberer' }, - { header: "Time Table Name", dataIndex: 'name', flex:1, sortable : false, menuDisabled: true }, - { menuDisabled: true, width: 30, renderer: function() - { - return '<div class="icon-remover" style="width: 15px; height: 15px;"></div>'; - } - } - ], - listeners : { - render : function(o,op) { - var me = this; - var el = me.getEl(); - var dropTarget = Ext.create('Ext.dd.DropTarget', el, { - ddGroup: 'explorerTree', - notifyOver : function(ddSource, e, data) - { - if ((data.records[0].get('nodeType') == 'timeTable' || data.records[0].get('nodeType') == 'sharedtimeTable') && (data.records[0].get('leaf'))) - { - this.valid = true; - return this.dropAllowed; - } - this.valid = false; - return this.dropNotAllowed; - }, - notifyDrop : function(ddSource, e, data) - { - if (!this.valid) return false; - - var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.catalog.id); - if (module) - { - module.getUiContent().addTT(data.records[0].get('text'),data.records[0].get('id')); - } - return true; - } - }); - }, - - cellclick : function(grid, cell, cellIndex, record){ - if (cellIndex == 2) - grid.getStore().remove(record); - } - } - }); - - var store = Ext.create('Ext.data.Store', - { - fields: ['name', 'function'] - }); - - this.paramGrid = Ext.create('Ext.grid.Panel', { - title: 'Select Parameter & Apply Function', - selType : 'rowmodel', - height: 200, - store : store, - columns: [ - { xtype: 'rownumberer' }, - { header: 'parameter', dataIndex: 'name', menuDisabled : true, sortable : false }, - { header: 'function', dataIndex: 'function', menuDisabled : true, sortable : false, - editor: { - xtype: 'combo', queryMode : 'local', -// emptyText : 'please click to select function', - store: [ 'min', 'max', 'mean' ], - triggerAction: 'all', -// lazyInit: false, - listeners: { - focus: function(obj) { - obj.expand(); - } - } - }, - renderer: function(v) - { - if(v != null && v.length > 0 ) - return v; - else - return 'click to select'; - } - }, - { menuDisabled : true, width: 30, renderer: function(){ - return '<div class="icon-remover" style="width: 15px; height: 15px;"></div>'; - } - } - ], - plugins: [ - Ext.create('Ext.grid.plugin.CellEditing', { - clicksToEdit: 1 - }) - ], - listeners : - { - render : function(o,op) - { - var me = this; - var el = me.body.dom; - var dropTarget = Ext.create('Ext.dd.DropTarget', el, { - ddGroup: 'explorerTree', - notifyOver : function(ddSource, e, data) - { - if (data.records[0].data.nodeType == 'localParam' && data.records[0].get('notyet')) { - this.valid = false; - return this.dropNotAllowed; - } - if (((data.records[0].data.nodeType == 'localParam') || - (data.records[0].data.nodeType == 'remoteParam') || - (data.records[0].data.nodeType == 'remoteSimuParam') || - (data.records[0].data.nodeType == 'derivedParam') || - (data.records[0].data.nodeType == 'myDataParam') || - (data.records[0].data.nodeType == 'alias'))&& - (data.records[0].isLeaf() || data.records[0].data.isParameter) && - !data.records[0].data.disable) - { - this.valid = true; - return this.dropAllowed; - } - - this.valid = false; - return this.dropNotAllowed; - }, - notifyDrop : function(ddSource, e, data) - { - if (!this.valid) - return false; - var nameToSent; - switch (data.records[0].data.nodeType) - { - case 'localParam' : - case 'remoteParam': - case 'remoteSimuParam': - nameToSent = data.records[0].get('id'); - if (data.records[0].get('alias')!= "" ) - var nameToSent = "#"+data.records[0].get('alias'); - break; - case 'alias' : - nameToSent = "#"+data.records[0].get('text'); - break; - case 'derivedParam' : - nameToSent = "ws_"+data.records[0].get('text'); - break; - case 'myDataParam' : - nameToSent = "wsd_"+data.records[0].get('text'); - break; - default : - return false; - } - var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.catalog.id); - if (module) - { - if (data.records[0].get('needsArgs') && !data.records[0].get('isSpectra')) { - module.getUiContent().fireEvent('openParamEditor',nameToSent); - } - else { - module.addParam(nameToSent,true); - } - } - return true; - } - }); - }, - cellclick : function(grid, cell, cellIndex, record){ - if (cellIndex == 3) - grid.getStore().remove(record); - } - } - }); - - this.catalogGrid = Ext.create('Ext.grid.Panel', { - title: 'Catalog', - height: 500, + + this.TTGrid = Ext.create('Ext.grid.Panel', { +// title: 'Catalog', + height: 530, columns: [ { text: '', dataIndex: '' } - ] + ], + frame: true, + dockedItems: [{ + xtype: 'toolbar', + items: [{ + iconCls: 'icon-add', + scope: this, + handler: function(){ +// cellEditing.cancelEdit(); +// +// var selection = this.TTGrid.getView().getSelectionModel().getSelection()[0]; +// var row = 0; +// if (selection) +// row = store.indexOf(selection) + 1; +// this.TTGrid.getSelectionModel().deselectAll(); +// +// var me = this; +// AmdaAction.addTTCacheInterval({'index' : row}, function (result, e) { +// this.status = result.status; +// this.TTGrid.getStore().reload({ +// callback : function(records, options, success) { +// me.TTGrid.getView().bufferedRenderer.scrollTo(row, false, function() { +// me.TTGrid.getView().select(row); +// cellEditing.startEditByPosition({row: row, column: 1}); +// }, me); +// } +// }); +// }, this); + } + }, { + iconCls: 'icon-delete', + disabled: true, + itemId: 'delete', + scope: this, + handler: function(){ +// var selection = this.TTGrid.getView().getSelectionModel().getSelection()[0]; +// if (selection) { +// var rowId = selection.get('cacheId'); +// this.TTGrid.getSelectionModel().deselectAll(); +// AmdaAction.removeTTCacheIntervalFromId(rowId, function (result, e) { +// this.status = result.status; +// this.TTGrid.getStore().reload(); +// }, this); +// } + } + }] + }] }); this.formPanel = Ext.create('Ext.form.Panel', { - height: 550, - width: 800, - layout: 'border', - defaults: { layout: 'fit', border: false }, + region : 'center', + layout: 'hbox', + bodyStyle: {background : '#dfe8f6'}, + defaults: { border : false, align: 'stretch', bodyStyle: {background : '#dfe8f6'}, padding: '3'}, fieldDefaults: { labelWidth: 80, labelAlign : 'top' }, items: [ { - xtype: 'form', - region: 'center', + xtype: 'form', flex: 1, buttonAlign: 'left', - bodyStyle: {background : '#dfe8f6'}, - padding: '5 5 5 5', +// title : 'Information', layout: {type: 'vbox', pack: 'start', align: 'stretch'}, items : [ - this.fieldName, - { + this.fieldName, + { xtype: 'fieldcontainer', layout: 'hbox', items: [ @@ -328,65 +349,111 @@ Ext.define('amdaUI.CatalogUI', { }, { xtype: 'splitter' }, { xtype:'textfield', fieldLabel: 'Intervals', name: 'nbIntervals', disabled: true} - ] - }, - { - xtype: 'textarea', - name: 'description', - fieldLabel: 'Description', -// anchor: '100% 50%' - }, - - this.paramGrid, - this.ttGrid - ], - fbar:[ - { - type: 'button', - text: 'Generate Catalog', - scope : this, - handler: function(button){ - // update object with user's values - // if the return is true (object had been updated) - // if(this.updateObject()){ - this.updateObject(); - this.generateCatalog(); - // } - } - }, - { - type: 'button', - text: 'Reset' - } - ] + ] + }, + { + xtype: 'textarea', + name: 'description', + fieldLabel: 'Description', + height: 200 + }, + { + xtype: 'component', + height: 180 + } + ], + fbar:[ + { + type: 'button', + text: 'Save', + scope : this, + handler: function () { + if (this.updateObject()){ + + var basicForm = this.formPanel.getForm(); + // if there's at least one record in the store of TTGrid + if (this.TTGrid.getStore().getTotalCount() > 0) { + // update TimeTable object which the content of form + basicForm.updateRecord(this.object); + + var me = this; + this.checkIntervalsStatusForSave(function () { + //Name validation + var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.catalog.id); + if (!module) + return; + module.linkedNode.isValidName(me.fieldName.getValue(), function (res) { + if (!res) + { + me.fieldName.validFlag = 'Error during object validation'; + myDesktopApp.errorMsg(me.fieldName.validFlag); + me.fieldName.validate(); + return; + } + + if (!res.valid) + { + if (res.error) + { + if (res.error.search('subtree') != -1) { + Ext.MessageBox.show({title:'Warning', + msg: res.error+'<br/>Do you want to overwrite it?', + width: 300, + buttons: Ext.MessageBox.OKCANCEL, + fn : me.overwriteProcess, + icon: Ext.MessageBox.WARNING, + scope : me + }); + me.fieldName.validFlag = true; + } + else + me.fieldName.validFlag = res.error; + } + else + { + me.fieldName.validFlag = 'Invalid object name'; + myDesktopApp.errorMsg(me.fieldName.validFlag); + } + me.fieldName.validate(); + return; + } + + me.fieldName.validFlag = true; + me.fieldName.validate(); + me.saveProcess(false); + }); + }); + } else { + // warning: + Ext.Msg.alert('No intervals', 'Your time table is invalid, <br>you must have at least one interval'); + } + } + } + },{ + type: 'button', + text: 'Share', + disabled: true + }, + { + type: 'button', + text: 'Visualize' + } + ] }, { - xtype: 'form', - region: 'east', + xtype: 'form', bodyStyle: {background : '#dfe8f6'}, - padding: '5 5 5 5', +// padding: '3', flex: 2, items : [ - this.catalogGrid - ], - fbar:[ - { - type: 'button', - text: 'Save' - },{ - type: 'button', - text: 'Share', - disabled: true - }, - { - type: 'button', - text: 'Visualize' - } - ] + this.TTGrid + ] } ] }); - + this.TTGrid.getSelectionModel().on('selectionchange', function(selModel,selections){ + this.TTGrid.down('#delete').setDisabled(selections.length === 0); + }, this); var myConf = { layout: 'border', diff --git a/js/app/views/StatisticsUI.js b/js/app/views/StatisticsUI.js new file mode 100644 index 0000000..556a937 --- /dev/null +++ b/js/app/views/StatisticsUI.js @@ -0,0 +1,343 @@ +/** + * Project AMDA-NG + * Name StatisticsUI.js + * @class amdaUI.statisticsUI + * @extends Ext.container.Container + * @brief Statistics Module UI definition (View) + * @author elena + */ + +Ext.define('amdaUI.StatisticsUI', { + extend: 'Ext.container.Container', + alias: 'widget.panelStatistics', + + requires : [ +// 'amdaModel.Function' + 'amdaUI.TimeSelectorUI' + ], + + statics : { +// functionStore : null + }, + + constructor: function(config) { + this.init(config); + this.callParent(arguments); + // if (this.object) this.loadObject(); + }, + + addParam : function(ParamName,isLeaf) + { + var r = Ext.create('amdaModel.AmdaObject', { name: ParamName }); + this.paramGrid.getStore().add(r); + this.paramGrid.getSelectionModel().select(this.paramGrid.getStore().getCount()-1); + }, + + addTT : function(TTname,TTid) + { + this.timeSelector.addTT(TTname, TTid); ; + + }, + + generateCatalog : function(){ + var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.statistics.id); + if (module) + module.linkedNode.execute(); + }, + + /** + * update this.object from form + */ + + updateObject : function(){ + // get the basic form of the left + var basicForm = this.formPanel.items.items[0].getForm(); + var updateStatus = true; + + var formValues = basicForm.getValues(); + // this.object.set('name',formValues.name); + // this.object.set('description',formValues.description); + + var recs = this.paramGrid.getStore().getNewRecords(); + var paramArr = new Array(); + Ext.Array.each(recs, function(rec, index,allItems){ + var obj = new Object(); + obj.param = rec.get('name'); + obj.function = rec.get('function'); + paramArr.push(obj); + }); + this.object.set('parameter', paramArr); + + var timeSource = formValues.timesrc_statisticsTimeSelector; + + if (timeSource === amdaModel.AmdaTimeObject.inputTimeSrc[0] // timeSource 'TimeTable' + && this.timeSelector.TTGrid.getStore().count() == 0) { + myDesktopApp.warningMsg('You\'ve chosen Time Selection `by TimeTable` but no timeTable was added!' + +'<br>You must add one or choose Time Selection `by Interval`'); + return false; + } + + // basicForm.updateRecord(this.object); + this.object.set('timesrc', timeSource); + // set valid intervals into TimeTable object + if (timeSource === amdaModel.AmdaTimeObject.inputTimeSrc[0]) + this.object.set('timeTables',this.timeSelector.TTGrid.getStore().data.items); + + return updateStatus; + }, + + /** + * Check if changes were made before closing window + * @return true if changes + */ + fclose : function() { + if (this.status == null) + return false; + + var isDirty = this.formPanel.getForm().isDirty() || (this.status.isModified) || (this.status.nbModified > 0) || (this.status.nbNew > 0); + return isDirty; + }, + + /** + * View configuration + */ + init : function (config) { + +// var functions = Ext.create('Ext.data.Store', { +// fields: ['id', 'name'], +// data : [ +// {"id":"min", "name":"MIN"}, +// {"id":"max", "name":"MAX"}, +// {"id":"mean","name":"MEAN"} +// ] +// }); + + this.fieldName = new Ext.form.field.Text({ + fieldLabel: 'Catalog Name', + allowBlank : false, + stripCharsRe: /(^\s+|\s+$)/g, + emptyText: 'Please no spaces!', + name: 'name', +// anchor: '100%', + validateOnChange: false, + validateOnBlur: false, + validFlag: false, + validator : function() { + return this.validFlag; + } + }); + + var ttStore = Ext.create('Ext.data.Store', + { + fields: [ 'name', 'hidden_id'] + }); + + this.timeSelector = new amdaUI.TimeSelectorUI({id: 'statisticsTimeSelector', height : 160}); + + var store = Ext.create('Ext.data.Store', + { + fields: ['name', 'function'] + }); + + this.paramGrid = Ext.create('Ext.grid.Panel', { + title: 'Select Parameter & Apply Function', + selType : 'rowmodel', +// flex: 2, + height :250, + store : store, + columns: [ + { xtype: 'rownumberer' }, + { header: 'parameter', dataIndex: 'name', menuDisabled : true, sortable : false }, + { header: 'function', dataIndex: 'function', menuDisabled : true, sortable : false, + editor: { + xtype: 'combo', queryMode : 'local', +// emptyText : 'please click to select function', + store: [ 'min', 'max', 'mean' ], + triggerAction: 'all', +// lazyInit: false, + listeners: { + focus: function(obj) { + obj.expand(); + } + } + }, + renderer: function(v) + { + if(v != null && v.length > 0 ) + return v; + else + return 'click to select'; + } + }, + { menuDisabled : true, width: 30, renderer: function(){ + return '<div class="icon-remover" style="width: 15px; height: 15px;"></div>'; + } + } + ], + plugins: [ + Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit: 1 + }) + ], + listeners : + { + render : function(o,op) + { + var me = this; + var el = me.body.dom; + var dropTarget = Ext.create('Ext.dd.DropTarget', el, { + ddGroup: 'explorerTree', + notifyOver : function(ddSource, e, data) + { + if (data.records[0].data.nodeType == 'localParam' && data.records[0].get('notyet')) { + this.valid = false; + return this.dropNotAllowed; + } + if (((data.records[0].data.nodeType == 'localParam') || + (data.records[0].data.nodeType == 'remoteParam') || + (data.records[0].data.nodeType == 'remoteSimuParam') || + (data.records[0].data.nodeType == 'derivedParam') || + (data.records[0].data.nodeType == 'myDataParam') || + (data.records[0].data.nodeType == 'alias'))&& + (data.records[0].isLeaf() || data.records[0].data.isParameter) && + !data.records[0].data.disable) + { + this.valid = true; + return this.dropAllowed; + } + + this.valid = false; + return this.dropNotAllowed; + }, + notifyDrop : function(ddSource, e, data) + { + if (!this.valid) + return false; + var nameToSent; + switch (data.records[0].data.nodeType) + { + case 'localParam' : + case 'remoteParam': + case 'remoteSimuParam': + nameToSent = data.records[0].get('id'); + if (data.records[0].get('alias')!= "" ) + var nameToSent = "#"+data.records[0].get('alias'); + break; + case 'alias' : + nameToSent = "#"+data.records[0].get('text'); + break; + case 'derivedParam' : + nameToSent = "ws_"+data.records[0].get('text'); + break; + case 'myDataParam' : + nameToSent = "wsd_"+data.records[0].get('text'); + break; + default : + return false; + } + var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.statistics.id); + if (module) + { + if (data.records[0].get('needsArgs') && !data.records[0].get('isSpectra')) { + module.getUiContent().fireEvent('openParamEditor',nameToSent); + } + else { + module.addParam(nameToSent,true); + } + } + return true; + } + }); + }, + cellclick : function(grid, cell, cellIndex, record){ + if (cellIndex == 3) + grid.getStore().remove(record); + } + } + }); + + + this.formPanel = Ext.create('Ext.form.Panel', { + region: 'center', + layout: 'hbox', +// bodyStyle: {background : '#dfe8f6'}, + defaults: { bodyStyle: {background : '#dfe8f6'}, padding : '3'}, + fieldDefaults: { labelWidth: 80, labelAlign : 'top' }, + items: [ + { + xtype: 'form', + flex : 1, + layout: {type: 'vbox', pack: 'start', align: 'stretch'}, + items : [ + this.paramGrid, + this.timeSelector + ] + }, + { + xtype: 'form', + title: 'Additional Information', + flex : 1, + layout: {type: 'vbox', pack: 'start', align: 'stretch', padding : '3'}, + items : [ + this.fieldName, + { + xtype: 'textarea', + name: 'description', + fieldLabel: 'Description', + height: 200 + } + ], + fbar: + [ + { + type: 'button', + text: 'Generate Catalog', + scope : this, + handler: function(){ + // update object with user's values + // if the return is true (object had been updated) + // if(this.updateObject()){ + this.updateObject(); + this.generateCatalog(); + // } + } + }, + { + type: 'button', + text: 'Reset', + scope : this, + handler: function() { + } + } + ] + } + ] + }); + + + + var myConf = { + layout: 'border', + items: [ + this.formPanel, + { + xtype: 'panel', + region: 'south', + title: 'Information', + collapsible: true, + height: 100, + autoHide: false, + bodyStyle: 'padding:5px', + iconCls: 'icon-information', + loader: { + autoLoad: true, + url: helpDir+'downloadHOWTO' + } + } + ] + }; + + Ext.apply (this, Ext.apply(arguments, myConf)); + } + +}); diff --git a/js/app/views/TabResultUI.js b/js/app/views/TabResultUI.js index d42fa32..773120b 100644 --- a/js/app/views/TabResultUI.js +++ b/js/app/views/TabResultUI.js @@ -37,6 +37,8 @@ Ext.define('amdaUI.TabResultUI', { break; case 'download': var title = 'Download Results'; break; + case 'statistics': var title = 'Statistics Results'; + break; default: } var newConfig = { @@ -54,7 +56,7 @@ Ext.define('amdaUI.TabResultUI', { scope : this, beforeclose : function() { //delete linked nodes connected to ResultModule and corresponding to this Tab - var module = myDesktopApp.getLoadeModule(myDesktopApp.dynamicModules.result.id); + var module = myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.result.id); if (module.linkedNodes) { var indices = new Array(); var i =0; @@ -105,6 +107,16 @@ Ext.define('amdaUI.ResultItem', { this); }, + saveCatalog: function(folderId,ttName) { + this.linkedNode = Ext.create('amdaModel.CatalogNode',{leaf : true}); + AmdaAction.getTmpObject( + folderId, + ttName, + this.linkedNode.get('nodeType'), + this.getResultCallback, + this); + }, + getResultCallback : function(result,remoteEvent){//result, e) { var t = remoteEvent.getTransaction(); //AKKA - catch error @@ -120,8 +132,8 @@ Ext.define('amdaUI.ResultItem', { return; } - var paramObj = Ext.create(this.linkedNode.get('objectDataModel'), result); - paramObj.set('fromPlugin',true); + var paramObj = Ext.create(this.linkedNode.get('objectDataModel'), result); + paramObj.set('fromPlugin',true); paramObj.set('intervals',result.intervals); // set parameter into node this.linkedNode.set('object',paramObj); @@ -130,7 +142,7 @@ Ext.define('amdaUI.ResultItem', { myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.result.id, true, function (module) { module.setLinkedNode(me.linkedNode); // Edition of parameter into parameter Module - me.linkedNode.editInModule(); + me.linkedNode.editInModule(); }); }, @@ -251,6 +263,35 @@ Ext.define('amdaUI.ResultItem', { } ] }; + var configCat = { + intId : config.title, + height : height, + title: jobtitle, + collapsible: true, + defaultType: 'button', + items :[ + { + xtype: 'label', + text: 'Catalog ' + }, + { + text: 'Edit/Save', + scope : this, + handler: function() { + this.saveCatalog(config.folderId,config.resultId); + } + }, + { + text: 'Delete', + scope : this, + handler: function() { + this.delete(config.processId); + this.ownerCt.remove(this); + } + } + ] + }; + //TODO make this properly var configPlot = { @@ -329,6 +370,9 @@ Ext.define('amdaUI.ResultItem', { case 'condition' : Ext.apply(this, configTT); break; + case 'statistics' : + Ext.apply(this, configCat); + break; case 'request' : Ext.apply(this, configPlot); break; diff --git a/js/app/views/TimeTableUI.js b/js/app/views/TimeTableUI.js index 435ff86..536e150 100755 --- a/js/app/views/TimeTableUI.js +++ b/js/app/views/TimeTableUI.js @@ -30,6 +30,7 @@ Ext.define('amdaUI.TimeTableUI', { }, status: null, + isCatalog : false, constructor: function(config) { this.init(config); @@ -116,7 +117,7 @@ Ext.define('amdaUI.TimeTableUI', { else { //From tmp object (ie Search result) - AmdaAction.initTTCacheFromTmpObject(this.object.get('folderId'), this.object.get('objName'), onAfterInit); + AmdaAction.initTTCacheFromTmpObject(this.object.get('folderId'), this.object.get('objName'), this.isCatalog, onAfterInit); } } else diff --git a/js/resources/css/amda.css b/js/resources/css/amda.css index 42fb0b1..efdecb3 100644 --- a/js/resources/css/amda.css +++ b/js/resources/css/amda.css @@ -79,6 +79,10 @@ background-image:url( ../images/16x16/search.png ) !important; } +.icon-statistics { + background-image:url( ../images/16x16/statistics.png ) !important; +} + .icon-manage-ws { background-image:url( ../images/16x16/wsManager.png ) !important; } @@ -385,3 +389,8 @@ p + p { font-style: italic !important; font-weight: bold !important; } + +.x-item-disabled .x-form-item-label { + filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100) !important; + opacity: 1.0 !important; +} diff --git a/js/resources/images/16x16/statistics.png b/js/resources/images/16x16/statistics.png new file mode 100644 index 0000000..cf014cb Binary files /dev/null and b/js/resources/images/16x16/statistics.png differ diff --git a/js/resources/images/16x16/visu_catalog.png b/js/resources/images/16x16/visu_catalog.png new file mode 100644 index 0000000..fabbb71 Binary files /dev/null and b/js/resources/images/16x16/visu_catalog.png differ diff --git a/js/resources/images/64x64/statistics.png b/js/resources/images/64x64/statistics.png new file mode 100644 index 0000000..4ef1fb3 Binary files /dev/null and b/js/resources/images/64x64/statistics.png differ diff --git a/js/resources/images/64x64/visu_catalog.png b/js/resources/images/64x64/visu_catalog.png new file mode 100644 index 0000000..ef79fda Binary files /dev/null and b/js/resources/images/64x64/visu_catalog.png differ diff --git a/php/classes/AmdaAction.php b/php/classes/AmdaAction.php index 6abc472..d311923 100644 --- a/php/classes/AmdaAction.php +++ b/php/classes/AmdaAction.php @@ -581,7 +581,10 @@ class AmdaAction { switch ($nodeType) { case 'timeTable' : $objectMgr = new TimeTableMgr(); - break; + break; + case 'catalog' : + $objectMgr = new CatalogMgr(); + break; default: return array("error" => $nodeType." NOT_IMPLEMENTED_YET"); } @@ -631,6 +634,9 @@ class AmdaAction { break; case 'timeTable' : $objectMgr = new TimeTableMgr(); + break; + case 'catalog' : + $objectMgr = new CatalogMgr(); break; case 'condition' : case 'request' : @@ -667,6 +673,9 @@ class AmdaAction { break; case 'timeTable' : $objectMgr = new TimeTableMgr(); + break; + case 'catalog' : + $objectMgr = new Catalog(); break; case 'condition' : case 'request' : @@ -698,6 +707,9 @@ class AmdaAction { break; case 'timeTable' : $objectMgr = new TimeTableMgr(); + break; + case 'catalog' : + $objectMgr = new CatalogMgr(); break; case 'condition' : case 'request' : @@ -726,7 +738,10 @@ class AmdaAction { break; case 'timeTable' : $objectMgr = new TimeTableMgr(); - break; + break; + case 'catalog' : + $objectMgr = new CatalogMgr(); + break; case 'condition' : case 'request' : $objectMgr = new RequestMgr($obj->nodeType); @@ -747,8 +762,12 @@ class AmdaAction { $objectMgr = new DerivedParamMgr($obj->nodeType); break; case 'timeTable' : + case 'sharedtimeTable' : $objectMgr = new TimeTableMgr(); break; + case 'catalog' : + $objectMgr = new CatalogMgr(); + break; case 'condition' : case 'request' : $objectMgr = new RequestMgr($obj->nodeType); @@ -927,15 +946,27 @@ class AmdaAction { return $cacheMgr->initTTCache(); } +// public function initCatalogGridFromTmpObject($folderId, $name) +// { +// $objMgr = new CatalogMgr(); +// return $objMgr->initCatalogGridFromTmpObject($folderId, $name); +// } + public function initTTCacheFromTT($id, $type) { $cacheMgr = new TimeTableCacheMgr(); return $cacheMgr->initFromTT($id, $type); } - public function initTTCacheFromTmpObject($folderId, $name) + public function initTTCacheFromTmpObject($folderId, $name, $isCatalog = false) { - $cacheMgr = new TimeTableCacheMgr(); + error_reporting(E_ERROR | E_WARNING | E_PARSE); + if (!$isCatalog) $cacheMgr = new TimeTableCacheMgr(); + + else $cacheMgr = new CatalogCacheMgr(); + + + return $cacheMgr->initFromTmpObject($folderId, $name); } @@ -947,7 +978,9 @@ class AmdaAction { public function readTTCacheIntervals($o) { - $cacheMgr = new TimeTableCacheMgr(); + if ($o->typeTT == 'catalog') $cacheMgr = new CatalogCacheMgr(); + else $cacheMgr = new TimeTableCacheMgr(); + return $cacheMgr->getIntervals($o->start,$o->limit,$o->sort,$o->filter); } @@ -1201,8 +1234,8 @@ class AmdaAction { //AKKA - New action to clean user WS public function cleanUserWS() { - require_once(INTEGRATION_SRC_DIR."RequestManager.php"); - return $this->executeRequest($obj, FunctionTypeEnumClass::PROCESSCLEAN); +// require_once(INTEGRATION_SRC_DIR."RequestManager.php"); +// return $this->executeRequest($obj, FunctionTypeEnumClass::PROCESSCLEAN); } public function deleteSpecialInfo($name) diff --git a/php/classes/CatalogCacheMgr.php b/php/classes/CatalogCacheMgr.php new file mode 100644 index 0000000..5aaf7f4 --- /dev/null +++ b/php/classes/CatalogCacheMgr.php @@ -0,0 +1,319 @@ +<?php + +/** + * @class CatalogCacheMgr + */ + + +class CatIntervalCacheObject extends IntervalCacheObject +{ + // for catalog + private $params = array(); + + public function toArray() { + $result = array( + "cacheId" => $this->id, + "start" => $this->getStartToISO(), + "stop" => $this->getStopToISO() + ); + if ($this->isNew) + $result["isNew"] = true; + if ($this->isModified) + $result["isModified"] = true; + + for ($i = 0; $i < count($this->params); $i++) { + $paramObject = array(); + $index = 'param'.sprintf("%d",$i+2); + $result[$index] = $this->params[$i]; + } + return $result; + } + + // for catalog + public function setParams($params) { + $this->params = $params; + + } + + public function getParams() { + return $this->params; + } + + public function writeBin($handle, $paramsNumber, $paramsSizes) { + fwrite($handle,pack('L6',$this->id,$this->index,$this->start,$this->stop,$this->isNew,$this->isModified)); + for ($i = 0; $i < $paramsNumber; $i++) { + $paramString = $this->params[$i]; + $paramArray = explode(',',$this->params[$i]); + for ($j = 0; $j < $paramsSizes[$i]; $j++) fwrite($handle,pack('d', $paramArray[$j])); + } + + } + + public function loadBin($handle, $paramsNumber, $paramsSizes) { + $array = unpack('L6int',fread($handle,6*4)); + $this->id = $array['int1']; + $this->index = $array['int2']; + $this->start = $array['int3']; + $this->stop = $array['int4']; + $this->isNew = $array['int5']; + $this->isModified = $array['int6']; + + for ($i = 0; $i < $paramsNumber; $i++) { + $this->params[$i] = null; + for ($j = 0; $j < $paramsSizes[$i]; $j++) { + $val = unpack('dval',fread($handle,8)); + $this->params[$i] .= $val['val']; + if ($j != $paramsSizes[$i] - 1) $this->params[$i] .= ','; + } + } + + } + + public function dump() { + echo " => Interval : id = ".$this->id.", index = ".$this->index.", start = ".$this->start.", stop = ".$this->stop.", isNew = ".$this->isNew.", isModified = ".$this->isModified.PHP_EOL; + } +} + +class CatalogCacheObject extends TimeTableCacheObject +{ + private $paramsNumber; + private $paramsSizes = array(); + + public function addInterval($startIso, $stopIso, $params, $isNew = false, $index = -1) { + $interval = new CatIntervalCacheObject($this->lastId, count($this->intervals)); + ++$this->lastId; + $interval->setStartFromISO($startIso); + $interval->setStopFromISO($stopIso); + // for catalog + $interval->setParams($params); + + $interval->setIsNew($isNew); + array_push($this->intervals, $interval); + if ($index < 0) + array_push($this->indexes, count($this->intervals) - 1); + else + array_splice($this->indexes, $index, 0, array(count($this->intervals) - 1)); + if ($isNew) + $this->isModified = true; + + return $interval; + } + + public function setParamsNumber($number){ + $this->paramsNumber = $number; + } + + public function setParamsSizes($params){ + for ($i = 0; $i < $this->paramsNumber; $i++) + $this->paramsSizes[$i] = $params[$i]['size']; + } + + public function writeBin($handle) { + //Magic key ("TTC") + fwrite($handle,pack('C3',ord('T'),ord('T'),ord('C'))); + + //Version + fwrite($handle,pack('L',TimeTableCacheObject::$format_version)); + + //Token + for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) + fwrite($handle,pack('C',ord($this->token[$i]))); + + //Modified + fwrite($handle,pack('L',$this->isModified)); + + //Filter + $this->filter->writeBin($handle); + + //Sort + $this->sort->writeBin($handle); + + //Params Number + fwrite($handle,pack('L',$this->paramsNumber)); + + //Params Sizes + for ($i = 0; $i < $this->paramsNumber; $i++) + fwrite($handle,pack('L',$this->paramsSizes[$i])); + + //Intervals + fwrite($handle,pack('L2',count($this->intervals), $this->lastId)); + + + foreach($this->intervals as $interval) + $interval->writeBin($handle,$this->paramsNumber,$this->paramsSizes); + + //Indexes + fwrite($handle,pack('L',count($this->indexes))); + foreach($this->indexes as $index) + fwrite($handle,pack('L',$index)); + } + + public function loadBin($handle) { + //Magic key ("TTC") + if (!$res = unpack('C3key',fread($handle,3))) + return false; + + if (($res['key1'] != ord('T')) || ($res['key2'] != ord('T')) || ($res['key3'] != ord('C'))) + return false; + + //Version + if (!$res = unpack('Lversion',fread($handle,4))) + return false; + if (($res['version'] != TimeTableCacheObject::$format_version)) + return false; + + //Token + $token = ""; + for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) + { + if (!$res = unpack('Ctoken',fread($handle,1))) + return false; + $token .= chr($res['token']); + } + $this->token = $token; + + //Modified + if (!$res = unpack('Lmodified',fread($handle,4))) + return false; + $this->isModified = $res['modified']; + + //Filter + $this->filter->loadBin($handle); + + //Sort + $this->sort->loadBin($handle); + + //ParamsNumber + if (!$res = unpack('Lnumber',fread($handle,4))) + return false; + $this->paramsNumber = $res['number']; + + //ParamsSizes + for ($i = 0; $i < $this->paramsNumber; $i++) { + if (!$res = unpack('Lsize',fread($handle,4))) + return false; + $this->paramsSizes[$i] = $res['size']; + } + + //Intervals + $res = unpack('L2data',fread($handle,2*4)); + $nbIntervals = $res['data1']; + $this->lastId = $res['data2']; + + for ($i = 0; $i < $nbIntervals; ++$i) + { + $interval = new CatIntervalCacheObject(-1); + $interval->loadBin($handle, $this->paramsNumber, $this->paramsSizes); + array_push($this->intervals, $interval); + } + + //Indexes + $res = unpack('Ldata',fread($handle,4)); + $nbIndexes = $res['data']; + for ($i = 0; $i < $nbIndexes; ++$i) + { + $res = unpack('Lindex',fread($handle,4)); + array_push($this->indexes, $res['index']); + } + + return true; + } + +} + +class CatalogCacheMgr extends TimeTableCacheMgr +{ + + protected static $cache_file = "cacheCat"; + + protected $ttMgr = null; + protected $cache = null; + + function __construct() { + + $this->ttMgr = new CatalogMgr(); + } + + + public function initFromTmpObject($folderId, $name) { + + //Create new cache + $this->cache = new CatalogCacheObject(); + + //Load intervals from TmpObject file (Statistics Module) + $intervals_res = $this->ttMgr->getTmpObject($folderId, $name); + + if (!isset($intervals_res)) + return array('success' => false, 'message' => 'Cannot get Tmp Object'); + + if (array_key_exists('intervals', $intervals_res)) + { + foreach ($intervals_res['intervals'] as $interval) + { + //Add interval + $this->cache->addInterval($interval['start'], $interval['stop'], $interval['paramTable']); + + } + } + + $this->cache->setIsModified(true); + + $paramHeaders = $intervals_res['parameters']; + + $this->cache->setParamsNumber(count($paramHeaders)); + $this->cache->setParamsSizes($paramHeaders); + + unset($intervals_res); + + //Update cache + $this->cache->updateIndexes(); + + //Save cache file + return array('success' => $this->saveToFile(), 'token' => $this->cache->getToken(), + 'status' => $this->cache->getStatus(), 'parameters' => $paramHeaders); + } + + + protected function loadFromFile() { + + if (!file_exists($this->getCacheFilePath())) + return false; + $this->cache = new CatalogCacheObject(); + $handle = fopen($this->getCacheFilePath(), 'rb'); + $result = false; + if (flock($handle, LOCK_SH)) + { + $this->cache->loadBin($handle); + flock( $handle, LOCK_UN ); + $result = true; + } + fclose($handle); + return $result; + } + + protected function getCacheFilePath() { + return USERTTDIR.(self::$cache_file); + } + + public function saveInTT($id, $action, $token) { + + if (!$this->loadFromFile()) + return array('success' => false, 'message' => 'Cannot load cache file'); + + if ($token != $this->cache->getToken()) + return array('success' => false, 'message' => 'Cache token check error'); + + $this->cache->updateIndexes(); + $this->saveToFile(); + + $intervals = $this->cache->getIntervalsArray(NULL,NULL,true); + + $this->cache->reset(); + + return $this->ttMgr->saveIntervals($id, $intervals, $action); + } + + + + } +?> \ No newline at end of file diff --git a/php/classes/CatalogMgr.php b/php/classes/CatalogMgr.php new file mode 100644 index 0000000..27d093f --- /dev/null +++ b/php/classes/CatalogMgr.php @@ -0,0 +1,227 @@ +<?php + +/** + * @class CatalogMgr + */ + +class CatalogMgr extends TimeTableMgr { + + function __construct() { + parent::__construct('Tt.xml'); + $this->contentRootId = 'catalog-treeRootNode'; + $this->contentRootTag = 'catalogList'; + $this->attributes = array('name' => '', 'intervals' => ''); // + 'parameters' + $this->optionalAttributes = array(); + $this->objTagName = 'catalog'; + $this->id_prefix = 'cat_'; // 'tt_' ? + + if (!file_exists($this->xmlName)) { + $this->createDom(); + $this->xp = new domxpath($this->contentDom); + } + } + + public function getTmpObject($folderId, $name, $onlyDescription = false) { + + $filePath = USERWORKINGDIR.$folderId.'/'.$name.'.xml'; + + if (!file_exists($filePath)) + return array('error' => 'Cannot find result file'); + + $dom = new DomDocument('1.0'); + $dom->formatOutput = true; + + if (!$dom -> load($filePath)) + return array('error' => 'Cannot load result file'); + $nameNodes = $dom->getElementsByTagName('name'); + if ($nameNodes->length > 0) + $attributesToReturn['name'] = $nameNodes->item(0)->nodeValue; + + $descNodes = $dom->getElementsByTagName('description'); + if ($descNodes->length > 0) + $attributesToReturn['description'] = $descNodes->item(0)->nodeValue; + + $creatNodes = $dom->getElementsByTagName('created'); + if ($creatNodes->length > 0) + $attributesToReturn['created'] = $creatNodes->item(0)->nodeValue; + + $histNodes = $dom->getElementsByTagName('history'); + if ($histNodes->length > 0) + $attributesToReturn['history'] = $histNodes->item(0)->nodeValue; + + $attributesToReturn['objName'] = $name; + $attributesToReturn['folderId'] = $folderId; + $attributesToReturn['success'] = true; + + if (!$onlyDescription) + { + $intNodes = $dom->getElementsByTagName('intervals'); + foreach ($intNodes as $intNode) + { + $startNodes = $intNode->getElementsByTagName('start'); + if ($startNodes->length <= 0) + return array('error' => 'Error detected in result file'); + + $stopNodes = $intNode->getElementsByTagName('stop'); + if ($stopNodes->length <= 0) + return array('error' => 'Error detected in result file'); + + // for catalog + $paramNodes = $intNode->getElementsByTagName('param'); + $params = array(); + if ($paramNodes->length > 0) + foreach ( $paramNodes as $paramNode ) $params[] = $paramNode->nodeValue; + + + $attributesToReturn['intervals'][] = array('start' => $startNodes->item(0)->nodeValue, + 'stop' => $stopNodes->item(0)->nodeValue, + 'paramTable' => $params); + } + // for catalog + $paramsNodes = $dom->getElementsByTagName('parameter'); + + if ($paramsNodes->length > 0){ + + $paramsArray = array(); + foreach ($paramsNodes as $paramNode) { + + $oneParam = array(); + foreach ($paramNode->attributes as $attr) + $oneParam[$attr->nodeName] = $attr->nodeValue; + + if (substr($paramNode->getAttribute('id'),0,8) == 'stat_cov') { + $oneParam['size'] = '1'; + $oneParam['name'] = 'Flag'; + } + + $paramsArray[] = $oneParam; + } + $attributesToReturn['success'] = true; + $attributesToReturn['parameters'] = $paramsArray; + } + else + return array('error' => 'No information on parameters in result file'); + + + } + + return $attributesToReturn; + } + + +// public function loadIntervalsFromTT($id,$typeTT,$start = NULL, $limit = NULL) +// { +// +// if ($typeTT == 'sharedtimeTable') { +// $pathid = SHAREDPATH.'TT/'.$id; +// } +// else { +// $pathid = USERTTDIR.$id; +// } +// +// //load intervals from TT id +// if (!file_exists($pathid.'.xml')) +// return array('success' => false, 'message' => "Cannot find TT file ".$id); +// +// $this->objectDom -> load($pathid.'.xml'); +// +// if (!($objToGet = $this->objectDom->getElementById($id))) +// return array('success' => false, 'message' => NO_SUCH_ID." ".$id); +// +// $xpath = new DOMXPath($this->objectDom); +// $intervals = $xpath->query('//intervals'); +// +// $result = array(); +// +// if (!isset($start) || !isset($limit)) +// { +// foreach ($intervals as $interval) +// { +// $startTime = $interval->getElementsByTagName('start')->item(0)->nodeValue; +// $stopTime = $interval->getElementsByTagName('stop')->item(0)->nodeValue; +// array_push($result, array('start' => $startTime, 'stop' => $stopTime)); +// } +// } +// else +// { +// for ($i = 0; $i < $limit; ++$i) +// { +// if ($start+$i >= $intervals->length) +// break; +// $startTime = $intervals->item($start+$i)->getElementsByTagName('start')->item(0)->nodeValue; +// $stopTime = $intervals->item($start+$i)->getElementsByTagName('stop')->item(0)->nodeValue; +// array_push($result, array('start' => $startTime, 'stop' => $stopTime)); +// } +// } +// +// return array( +// 'totalCount' => $intervals->length, +// 'intervals' => $result, +// 'start' => isset($start) ? $start : 0, +// 'limit' => isset($limit) ? $limit : 0, +// 'success' => true +// ); +// +// } + + /* + * catalog header + */ + protected function setParamDescription($params) { + + $paramsElement = $this->objectDom->createElement('parametres'); + foreach ($params as $param) { + $paramElement = $this->objectDom->createElement('param'); + $attrArray = (array)$param; + foreach ($attrArray as $key => $value) + $paramElement->setAttribute($key, $value); + $paramsElement->appendChild($paramElement); + } + + return $paramsElement; + } + + protected function createIntervalElement($interval) { + $newInterval = $this->objectDom->createElement('intervals'); + $newInterval->appendChild($this->objectDom->createElement('start',$interval['start'])); + $newInterval->appendChild($this->objectDom->createElement('stop',$interval['stop'])); + foreach ($interval as $key =>$value) { + if (substr($key,0,5) == 'param') + $newInterval->appendChild($this->objectDom->createElement('param', $value)); + + } + return $newInterval; + } + + public function createObject($p, $folder){ + + if ($p -> leaf) + { + $result = $this->createParameter($p, $folder); + if ($result['error']) + return $result; + + $cacheMgr = new CatalogCacheMgr(); + + if (isset($p->cacheToken) && ($p->cacheToken != '')) + { + $resultSaveInt = $cacheMgr->saveInTT($result['id'], "update", $p->cacheToken); + if (!$resultSaveInt['success']) + { + if ($resultSaveInt['message']) + return array('error' => $resultSaveInt['message']); + else + return array('error' => 'Unknown error during intervals save'); + } + } + return $result; + } + // else return $this->createFolder($p); + //TODO check if this is possible? + else return array('error' => 'createFolder should be called from RENAME'); + + } + + +} +?> \ No newline at end of file diff --git a/php/classes/IntervalCacheObject.php b/php/classes/IntervalCacheObject.php new file mode 100644 index 0000000..56b7804 --- /dev/null +++ b/php/classes/IntervalCacheObject.php @@ -0,0 +1,114 @@ +<?php +class IntervalCacheObject +{ + protected $id = -1; + protected $index = -1; + protected $start = 0; + protected $stop = 0; + protected $isNew = false; + protected $isModified = false; + + function __construct($id, $index) { + $this->id = $id; + $this->index = $index; + } + + public function getId() { + return $this->id; + } + + public function getIndex() { + return $this->index; + } + + public function setIndex($index) { + $this->index = $index; + } + + public function getStartToStamp() { + return $this->start; + } + + public function getStartToISO() { + return CacheTools::stamp2iso($this->start); + } + + public function setStartFromStamp($stamp) { + $this->start = $stamp; + } + + public function setStartFromISO($iso) { + $this->start = CacheTools::iso2stamp($iso); + } + + public function getStopToStamp() { + return $this->stop; + } + + public function getStopToISO() { + return CacheTools::stamp2iso($this->stop); + } + + public function setStopFromStamp($stamp) { + $this->stop = $stamp; + } + + public function setStopFromISO($iso) { + $this->stop = CacheTools::iso2stamp($iso); + } + + public function getDuration() { + return ($this->stop-$this->start); + } + + public function isModified() { + return $this->isModified; + } + + public function setIsModified($isModified) { + $this->isModified = $isModified; + } + + public function isNew() { + return $this->isNew; + } + + public function setIsNew($isNew) { + $this->isNew = $isNew; + } + + public function toArray() { + $result = array( + "cacheId" => $this->id, + "start" => $this->getStartToISO(), + "stop" => $this->getStopToISO() + ); + if ($this->isNew) + $result["isNew"] = true; + if ($this->isModified) + $result["isModified"] = true; + + return $result; + } + + + + public function writeBin($handle) { + fwrite($handle,pack('L6',$this->id,$this->index,$this->start,$this->stop,$this->isNew,$this->isModified)); + } + + public function loadBin($handle) { + $array = unpack('L6int',fread($handle,6*4)); + $this->id = $array['int1']; + $this->index = $array['int2']; + $this->start = $array['int3']; + $this->stop = $array['int4']; + $this->isNew = $array['int5']; + $this->isModified = $array['int6']; + } + + public function dump() { + echo " => Interval : id = ".$this->id.", index = ".$this->index.", start = ".$this->start.", stop = ".$this->stop.", isNew = ".$this->isNew.", isModified = ".$this->isModified.PHP_EOL; + } +} +?> \ No newline at end of file diff --git a/php/classes/TimeTableCacheMgr.php b/php/classes/TimeTableCacheMgr.php index d3c9a02..7b98815 100644 --- a/php/classes/TimeTableCacheMgr.php +++ b/php/classes/TimeTableCacheMgr.php @@ -27,8 +27,8 @@ class SortPartCacheObject public static $DIRECTION_ASC = 1; public static $DIRECTION_DES = 2; - private $type; - private $dir; + protected $type; + protected $dir; function __construct() { $this->type = self::$TYPE_UNKNOWN; @@ -172,7 +172,7 @@ class SortPartCacheObject class SortCacheObject { - private $parts = array(); + protected $parts = array(); function __construct() { } @@ -286,9 +286,9 @@ class FilterPartCacheObject public static $OPERATION_GT = 2; public static $OPERATION_EQ = 3; - private $type; - private $op; - private $value; + protected $type; + protected $op; + protected $value; function __construct() { $this->type = self::$TYPE_UNKNOWN; @@ -470,7 +470,7 @@ class FilterPartCacheObject class FilterCacheObject { - private $parts = array(); + protected $parts = array(); function __construct() { @@ -555,631 +555,12 @@ class FilterCacheObject } } -class IntervalCacheObject + class TimeTableCacheMgr { - private $id = -1; - private $index = -1; - private $start = 0; - private $stop = 0; - private $isNew = false; - private $isModified = false; - - function __construct($id, $index) { - $this->id = $id; - $this->index = $index; - } - - public function getId() { - return $this->id; - } - - public function getIndex() { - return $this->index; - } - - public function setIndex($index) { - $this->index = $index; - } - - public function getStartToStamp() { - return $this->start; - } - - public function getStartToISO() { - return CacheTools::stamp2iso($this->start); - } - - public function setStartFromStamp($stamp) { - $this->start = $stamp; - } - - public function setStartFromISO($iso) { - $this->start = CacheTools::iso2stamp($iso); - } - - public function getStopToStamp() { - return $this->stop; - } - - public function getStopToISO() { - return CacheTools::stamp2iso($this->stop); - } - - public function setStopFromStamp($stamp) { - $this->stop = $stamp; - } - - public function setStopFromISO($iso) { - $this->stop = CacheTools::iso2stamp($iso); - } - - public function getDuration() { - return ($this->stop-$this->start); - } - - public function isModified() { - return $this->isModified; - } - - public function setIsModified($isModified) { - $this->isModified = $isModified; - } - - public function isNew() { - return $this->isNew; - } - - public function setIsNew($isNew) { - $this->isNew = $isNew; - } - - public function toArray() { - $result = array( - "cacheId" => $this->id, - "start" => $this->getStartToISO(), - "stop" => $this->getStopToISO() - ); - if ($this->isNew) - $result["isNew"] = true; - if ($this->isModified) - $result["isModified"] = true; - - return $result; - } - - - - public function writeBin($handle) { - fwrite($handle,pack('L6',$this->id,$this->index,$this->start,$this->stop,$this->isNew,$this->isModified)); - } - - public function loadBin($handle) { - $array = unpack('L6int',fread($handle,6*4)); - $this->id = $array['int1']; - $this->index = $array['int2']; - $this->start = $array['int3']; - $this->stop = $array['int4']; - $this->isNew = $array['int5']; - $this->isModified = $array['int6']; - } - - public function dump() { - echo " => Interval : id = ".$this->id.", index = ".$this->index.", start = ".$this->start.", stop = ".$this->stop.", isNew = ".$this->isNew.", isModified = ".$this->isModified.PHP_EOL; - } -} - -class TimeTableCacheObject -{ - private static $format_version = 1; - private static $token_len = 8; - - private $token = ""; - - private $lastId = 0; - - private $intervals = array(); - private $indexes = array(); - - private $isModified = false; - - private $filter = null; - - private $sort = null; - - function __construct() { - $this->token = $this->getRandomToken(); - $this->filter = new FilterCacheObject(); - $this->sort = new SortCacheObject(); - } - - public function reset() { - $this->lastId = 0; - $this->isModified = false; - $this->intervals = array(); - $this->indexes = array(); - unset($this->filter); - $this->filter = new FilterCacheObject();; - unset($this->sort); - $this->sort = new SortCacheObject(); - } - - public function setIsModified($isModified) { - $this->isModified = $isModified; - } - - public function addInterval($startIso, $stopIso, $isNew = false, $index = -1) { - $interval = new IntervalCacheObject($this->lastId, count($this->intervals)); - ++$this->lastId; - $interval->setStartFromISO($startIso); - $interval->setStopFromISO($stopIso); - $interval->setIsNew($isNew); - array_push($this->intervals, $interval); - if ($index < 0) - array_push($this->indexes, count($this->intervals) - 1); - else - array_splice($this->indexes, $index, 0, array(count($this->intervals) - 1)); - if ($isNew) - $this->isModified = true; - return $interval; - } - - public function removeIntervalFromId($id) { - for ($i = 0; $i < count($this->intervals); ++$i) - { - if ($this->intervals[$i]->getId() == $id) - { - //Remove interval - array_splice($this->intervals, $i, 1); - //Remove interval index if exist in indexes list - for ($j = 0; $j < count($this->indexes); ++$j) - { - if ($this->indexes[$j] == $i) - { - array_splice($this->indexes, $j, 1); - break; - } - } - //Update indexes list - for ($j = 0; $j < count($this->indexes); ++$j) - { - if ($this->indexes[$j] >= $i) - $this->indexes[$j]--; - } - $this->isModified = true; - return true; - } - } - - return false; - } - - public function modifyIntervalFromId($id, $start, $stop) { - foreach ($this->intervals as $interval) - { - if ($interval->getId() == $id) - { - if (isset($start)) - $interval->setStartFromISO($start); - if (isset($stop)) - $interval->setStopFromISO($stop); - $interval->setIsModified(true); - $this->isModified = true; - return true; - } - } - - return false; - } - - public function operationIntervals($extendTime, $shiftTime) { - if (($extendTime == 0) && ($shiftTime == 0)) - //Nothing to do - return true; - - for ($i = 0; $i < count($this->indexes); ++$i) { - $start = $this->intervals[$this->indexes[$i]]->getStartToStamp(); - $start -= $extendTime; - $start += $shiftTime; - $this->intervals[$this->indexes[$i]]->setStartFromStamp($start); - - $stop = $this->intervals[$this->indexes[$i]]->getStopToStamp(); - $stop += $extendTime; - $stop += $shiftTime; - $this->intervals[$this->indexes[$i]]->setStopFromStamp($stop); - - $this->intervals[$this->indexes[$i]]->setIsModified(true); - $this->isModified = true; - } - - return true; - } - - public function mergeIntervals() { - $this->sort->reset(); - - $this->sort->loadFromObject( - array( - (object)array("property" => "start", "direction" => "DESC") - ) - ); - - $this->updateIndexes(); - - $merged_intervals = array(); - - for ($i = 0; $i < count($this->indexes); ++$i) { - if (count($merged_intervals) == 0) - { - array_push($merged_intervals,array( - "start" => $this->intervals[$this->indexes[$i]]->getStartToStamp(), - "stop" => $this->intervals[$this->indexes[$i]]->getStopToStamp(), - "mod" => FALSE) - ); - continue; - } - if (($merged_intervals[count($merged_intervals)-1]["stop"] >= $this->intervals[$this->indexes[$i]]->getStartToStamp()) && - ($merged_intervals[count($merged_intervals)-1]["stop"] < $this->intervals[$this->indexes[$i]]->getStopToStamp())) - { - $merged_intervals[count($merged_intervals)-1]["stop"] = $this->intervals[$this->indexes[$i]]->getStopToStamp(); - $merged_intervals[count($merged_intervals)-1]["mod"] = TRUE; - } - else - array_push($merged_intervals,array( - "start" => $this->intervals[$this->indexes[$i]]->getStartToStamp(), - "stop" => $this->intervals[$this->indexes[$i]]->getStopToStamp(), - "mod" => FALSE) - ); - } - - $this->reset(); - - foreach ($merged_intervals as $merged_interval) { - $interval = new IntervalCacheObject($this->lastId, count($this->intervals)); - ++$this->lastId; - $interval->setStartFromStamp($merged_interval["start"]); - $interval->setStopFromStamp($merged_interval["stop"]); - $interval->setIsNew($merged_interval["mod"]); - if ($merged_interval["mod"]) - $this->isModified = true; - array_push($this->intervals, $interval); - array_push($this->indexes, count($this->intervals) - 1); - } - - return true; - } - - public function getStatistics() { - $minTime = NULL; - $maxTime = NULL; - $minDuration = NULL; - $maxDuration = NULL; - $indexMinDuration = -1; - $indexMaxDuration = -1; - - $nbValid = 0; - $durationTotal = 0; - - //Min & Max - for ($i = 0; $i < count($this->indexes); ++$i) { - if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) - //Invalid interval - continue; - - ++$nbValid; - $durationTotal += $this->intervals[$this->indexes[$i]]->getDuration(); - - if (!isset($minTime) || ($minTime > $this->intervals[$this->indexes[$i]]->getStartToStamp())) - $minTime = $this->intervals[$this->indexes[$i]]->getStartToStamp(); - - if (!isset($maxTime) || ($maxTime < $this->intervals[$this->indexes[$i]]->getStopToStamp())) - $maxTime = $this->intervals[$this->indexes[$i]]->getStopToStamp(); - - if (!isset($minDuration) || ($minDuration > $this->intervals[$this->indexes[$i]]->getDuration())) - { - $minDuration = $this->intervals[$this->indexes[$i]]->getDuration(); - $indexMinDuration = $i; - } - - if (!isset($maxDuration) || ($maxDuration < $this->intervals[$this->indexes[$i]]->getDuration())) - { - $maxDuration = $this->intervals[$this->indexes[$i]]->getDuration(); - $indexMaxDuration = $i; - } - } - - if (!isset($minTime)) - $minTime = 0; - if (!isset($maxTime)) - $maxTime = 0; - if (!isset($minDuration)) - $minDuration = 0; - if (!isset($maxDuration)) - $maxDuration = 0; - - - //Mean - if ($nbValid > 0) - $mean = $durationTotal / $nbValid; - else - $mean = 0; - - //Standard deviation - $pow = 0; - for ($i = 0; $i < count($this->indexes); ++$i) { - if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) - //Invalid interval - continue; - - $pow += pow($this->intervals[$this->indexes[$i]]->getDuration()-$mean,2); - } - if ($nbValid > 0) - $variance = $pow/$nbValid; - else - $variance = 0; - $stdev = sqrt($variance); - - //Sort by duration to get median - $this->sort->reset(); - - $this->sort->loadFromObject( - array( - (object)array("property" => "durationSec", "direction" => "DESC") - ) - ); - - $this->updateIndexes(); - - $durations = array(); - for ($i = 0; $i < count($this->indexes); ++$i) { - if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) - //Invalid interval - continue; - - array_push($durations, $this->intervals[$this->indexes[$i]]->getDuration()); - } - - if (count($durations) > 0) - { - if (count($durations)%2 > 0) { - $median = $durations[count($durations)/2-0.5]; - } else { // else the number of intervals is an even number - $median = ($durations[count($durations)/2-1] + $durations[count($durations)/2])/2; - } - } - else - $median = 0; - - //Merge intervals to get density - $this->mergeIntervals(); - - $durationMergedTotal = 0; - for ($i = 0; $i < count($this->indexes); ++$i) { - if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) - //Invalid interval - continue; - - $durationMergedTotal += $this->intervals[$this->indexes[$i]]->getDuration(); - } - - if (($maxTime-$minTime) > 0) - $density = (($durationMergedTotal/($maxTime-$minTime))); - else - $density = 0; - - return array( - "minDuration" => $minDuration, - "minDurationIndex"=> $indexMinDuration, - "maxDuration" => $maxDuration, - "maxDurationIndex"=> $indexMaxDuration, - "mean" => $mean, - "stdev" => $stdev, - "median" => $median, - "density" => $density); - } - - public function getStatus() { - $nbFiltered = count($this->intervals) - count($this->indexes); - - $nbModified = 0; - $nbNew = 0; - $nbInvalid = 0; - $nbValid = 0; - for ($i = 0; $i < count($this->indexes); ++$i) { - if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) - ++$nbInvalid; - else - ++$nbValid; - if ($this->intervals[$this->indexes[$i]]->isModified()) - ++$nbModified; - if ($this->intervals[$this->indexes[$i]]->isNew()) - ++$nbNew; - } - - return array( - "nbFiltered" => $nbFiltered, - "nbModified" => $nbModified, - "nbNew" => $nbNew, - "nbInvalid" => $nbInvalid, - "nbValid" => $nbValid, - "isModified" => $this->isModified - ); - } - - public function getIntervalsArray($startIndex, $limit,$skipInvalid = false) { - $intervals = array(); - - if (!isset($startIndex)) - $startIndex = 0; - - if (!isset($limit)) - $limit = count($this->indexes); - - for ($i = 0; $i < $limit; ++$i) { - if ($startIndex+$i >= count($this->indexes)) - break; - if ($skipInvalid && ($this->intervals[$this->indexes[$startIndex+$i]]->getDuration() <= 0)) - continue; - array_push($intervals, $this->intervals[$this->indexes[$startIndex+$i]]->toArray()); - } - return $intervals; - } - - public function getLength() { - return count($this->indexes); - } - - public function getToken() { - return $this->token; - } - - public function getFilter() { - return $this->filter; - } - - public function getSort() { - return $this->sort; - } - - public function updateIndexes() { - $this->indexes = array(); - - for ($i = 0; $i < count($this->intervals); ++$i) - $this->intervals[$i]->setIndex($i); - - //Apply sort - $sort_result = $this->sort->apply($this->intervals); - - //Apply filter - for ($i = 0; $i < count($sort_result); ++$i) - { - if (!$this->filter->toFiltered($this->intervals[$sort_result[$i]])) - array_push($this->indexes,$this->intervals[$sort_result[$i]]->getIndex()); - } - } - - public function writeBin($handle) { - //Magic key ("TTC") - fwrite($handle,pack('C3',ord('T'),ord('T'),ord('C'))); - - //Version - fwrite($handle,pack('L',TimeTableCacheObject::$format_version)); - - //Token - for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) - fwrite($handle,pack('C',ord($this->token[$i]))); - - //Modified - fwrite($handle,pack('L',$this->isModified)); - - //Filter - $this->filter->writeBin($handle); - - //Sort - $this->sort->writeBin($handle); - - //Intervals - fwrite($handle,pack('L2',count($this->intervals), $this->lastId)); - foreach($this->intervals as $interval) - $interval->writeBin($handle); - - //Indexes - fwrite($handle,pack('L',count($this->indexes))); - foreach($this->indexes as $index) - fwrite($handle,pack('L',$index)); - } - - public function loadBin($handle) { - //Magic key ("TTC") - if (!$res = unpack('C3key',fread($handle,3))) - return false; - - if (($res['key1'] != ord('T')) || ($res['key2'] != ord('T')) || ($res['key3'] != ord('C'))) - return false; - - //Version - if (!$res = unpack('Lversion',fread($handle,4))) - return false; - if (($res['version'] != TimeTableCacheObject::$format_version)) - return false; - - //Token - $token = ""; - for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) - { - if (!$res = unpack('Ctoken',fread($handle,1))) - return false; - $token .= chr($res['token']); - } - $this->token = $token; - - //Modified - if (!$res = unpack('Lmodified',fread($handle,4))) - return false; - $this->isModified = $res['modified']; - - //Filter - $this->filter->loadBin($handle); - - //Sort - $this->sort->loadBin($handle); - - //Intervals - $res = unpack('L2data',fread($handle,2*4)); - $nbIntervals = $res['data1']; - $this->lastId = $res['data2']; - for ($i = 0; $i < $nbIntervals; ++$i) - { - $interval = new IntervalCacheObject(-1); - $interval->loadBin($handle); - array_push($this->intervals, $interval); - } - - //Indexes - $res = unpack('Ldata',fread($handle,4)); - $nbIndexes = $res['data']; - for ($i = 0; $i < $nbIndexes; ++$i) - { - $res = unpack('Lindex',fread($handle,4)); - array_push($this->indexes, $res['index']); - } - - return true; - } - - private function getRandomToken() { - $letters = 'abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; - return substr(str_shuffle($letters), 0, TimeTableCacheObject::$token_len); - } - - public function dump() { - echo " => TimeTableCacheObject : token = ".$this->token.", nb intervals = ".count($this->intervals).", last id = ".$this->lastId.", nb indexes = ".count($this->indexes).PHP_EOL; - echo PHP_EOL; - - $this->filter->dump(); - echo PHP_EOL; - - $this->sort->dump(); - echo PHP_EOL; - - foreach ($this->intervals as $interval) - $interval->dump(); - echo PHP_EOL; - - echo " => Indexes list : "; - foreach ($this->indexes as $index) - { - echo $index.", "; - } - echo PHP_EOL; - } -} - -class TimeTableCacheMgr -{ - private static $cache_file = "cacheTT"; + protected static $cache_file = "cacheTT"; - private $ttMgr = null; - private $cache = null; + protected $ttMgr = null; + protected $cache = null; function __construct() { $this->ttMgr = new TimeTableMgr(); @@ -1449,11 +830,11 @@ class TimeTableCacheMgr $this->cache->dump(); } - private function getCacheFilePath() { + protected function getCacheFilePath() { return USERTTDIR.(self::$cache_file); } - private function saveToFile() { + protected function saveToFile() { if (!isset($this->cache)) return false; $handle = fopen($this->getCacheFilePath(), 'wb'); @@ -1468,7 +849,7 @@ class TimeTableCacheMgr return $result; } - private function loadFromFile() { + protected function loadFromFile() { if (!file_exists($this->getCacheFilePath())) return false; $this->cache = new TimeTableCacheObject(); diff --git a/php/classes/TimeTableCacheObject.php b/php/classes/TimeTableCacheObject.php new file mode 100644 index 0000000..d2fb579 --- /dev/null +++ b/php/classes/TimeTableCacheObject.php @@ -0,0 +1,509 @@ +<?php + +class TimeTableCacheObject +{ + protected static $format_version = 1; + protected static $token_len = 8; + + protected $token = ""; + + protected $lastId = 0; + + protected $intervals = array(); + protected $indexes = array(); + + protected $isModified = false; + + protected $filter = null; + + protected $sort = null; + + function __construct() { + $this->token = $this->getRandomToken(); + $this->filter = new FilterCacheObject(); + $this->sort = new SortCacheObject(); + } + + public function reset() { + $this->lastId = 0; + $this->isModified = false; + $this->intervals = array(); + $this->indexes = array(); + unset($this->filter); + $this->filter = new FilterCacheObject();; + unset($this->sort); + $this->sort = new SortCacheObject(); + } + + public function setIsModified($isModified) { + $this->isModified = $isModified; + } + + public function addInterval($startIso, $stopIso, $isNew = false, $index = -1) { + $interval = new IntervalCacheObject($this->lastId, count($this->intervals)); + ++$this->lastId; + $interval->setStartFromISO($startIso); + $interval->setStopFromISO($stopIso); + $interval->setIsNew($isNew); + array_push($this->intervals, $interval); + if ($index < 0) + array_push($this->indexes, count($this->intervals) - 1); + else + array_splice($this->indexes, $index, 0, array(count($this->intervals) - 1)); + if ($isNew) + $this->isModified = true; + return $interval; + } + + public function removeIntervalFromId($id) { + for ($i = 0; $i < count($this->intervals); ++$i) + { + if ($this->intervals[$i]->getId() == $id) + { + //Remove interval + array_splice($this->intervals, $i, 1); + //Remove interval index if exist in indexes list + for ($j = 0; $j < count($this->indexes); ++$j) + { + if ($this->indexes[$j] == $i) + { + array_splice($this->indexes, $j, 1); + break; + } + } + //Update indexes list + for ($j = 0; $j < count($this->indexes); ++$j) + { + if ($this->indexes[$j] >= $i) + $this->indexes[$j]--; + } + $this->isModified = true; + return true; + } + } + + return false; + } + + public function modifyIntervalFromId($id, $start, $stop) { + foreach ($this->intervals as $interval) + { + if ($interval->getId() == $id) + { + if (isset($start)) + $interval->setStartFromISO($start); + if (isset($stop)) + $interval->setStopFromISO($stop); + $interval->setIsModified(true); + $this->isModified = true; + return true; + } + } + + return false; + } + + public function operationIntervals($extendTime, $shiftTime) { + if (($extendTime == 0) && ($shiftTime == 0)) + //Nothing to do + return true; + + for ($i = 0; $i < count($this->indexes); ++$i) { + $start = $this->intervals[$this->indexes[$i]]->getStartToStamp(); + $start -= $extendTime; + $start += $shiftTime; + $this->intervals[$this->indexes[$i]]->setStartFromStamp($start); + + $stop = $this->intervals[$this->indexes[$i]]->getStopToStamp(); + $stop += $extendTime; + $stop += $shiftTime; + $this->intervals[$this->indexes[$i]]->setStopFromStamp($stop); + + $this->intervals[$this->indexes[$i]]->setIsModified(true); + $this->isModified = true; + } + + return true; + } + + public function mergeIntervals() { + $this->sort->reset(); + + $this->sort->loadFromObject( + array( + (object)array("property" => "start", "direction" => "DESC") + ) + ); + + $this->updateIndexes(); + + $merged_intervals = array(); + + for ($i = 0; $i < count($this->indexes); ++$i) { + if (count($merged_intervals) == 0) + { + array_push($merged_intervals,array( + "start" => $this->intervals[$this->indexes[$i]]->getStartToStamp(), + "stop" => $this->intervals[$this->indexes[$i]]->getStopToStamp(), + "mod" => FALSE) + ); + continue; + } + if (($merged_intervals[count($merged_intervals)-1]["stop"] >= $this->intervals[$this->indexes[$i]]->getStartToStamp()) && + ($merged_intervals[count($merged_intervals)-1]["stop"] < $this->intervals[$this->indexes[$i]]->getStopToStamp())) + { + $merged_intervals[count($merged_intervals)-1]["stop"] = $this->intervals[$this->indexes[$i]]->getStopToStamp(); + $merged_intervals[count($merged_intervals)-1]["mod"] = TRUE; + } + else + array_push($merged_intervals,array( + "start" => $this->intervals[$this->indexes[$i]]->getStartToStamp(), + "stop" => $this->intervals[$this->indexes[$i]]->getStopToStamp(), + "mod" => FALSE) + ); + } + + $this->reset(); + + foreach ($merged_intervals as $merged_interval) { + $interval = new IntervalCacheObject($this->lastId, count($this->intervals)); + ++$this->lastId; + $interval->setStartFromStamp($merged_interval["start"]); + $interval->setStopFromStamp($merged_interval["stop"]); + $interval->setIsNew($merged_interval["mod"]); + if ($merged_interval["mod"]) + $this->isModified = true; + array_push($this->intervals, $interval); + array_push($this->indexes, count($this->intervals) - 1); + } + + return true; + } + + public function getStatistics() { + $minTime = NULL; + $maxTime = NULL; + $minDuration = NULL; + $maxDuration = NULL; + $indexMinDuration = -1; + $indexMaxDuration = -1; + + $nbValid = 0; + $durationTotal = 0; + + //Min & Max + for ($i = 0; $i < count($this->indexes); ++$i) { + if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) + //Invalid interval + continue; + + ++$nbValid; + $durationTotal += $this->intervals[$this->indexes[$i]]->getDuration(); + + if (!isset($minTime) || ($minTime > $this->intervals[$this->indexes[$i]]->getStartToStamp())) + $minTime = $this->intervals[$this->indexes[$i]]->getStartToStamp(); + + if (!isset($maxTime) || ($maxTime < $this->intervals[$this->indexes[$i]]->getStopToStamp())) + $maxTime = $this->intervals[$this->indexes[$i]]->getStopToStamp(); + + if (!isset($minDuration) || ($minDuration > $this->intervals[$this->indexes[$i]]->getDuration())) + { + $minDuration = $this->intervals[$this->indexes[$i]]->getDuration(); + $indexMinDuration = $i; + } + + if (!isset($maxDuration) || ($maxDuration < $this->intervals[$this->indexes[$i]]->getDuration())) + { + $maxDuration = $this->intervals[$this->indexes[$i]]->getDuration(); + $indexMaxDuration = $i; + } + } + + if (!isset($minTime)) + $minTime = 0; + if (!isset($maxTime)) + $maxTime = 0; + if (!isset($minDuration)) + $minDuration = 0; + if (!isset($maxDuration)) + $maxDuration = 0; + + + //Mean + if ($nbValid > 0) + $mean = $durationTotal / $nbValid; + else + $mean = 0; + + //Standard deviation + $pow = 0; + for ($i = 0; $i < count($this->indexes); ++$i) { + if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) + //Invalid interval + continue; + + $pow += pow($this->intervals[$this->indexes[$i]]->getDuration()-$mean,2); + } + if ($nbValid > 0) + $variance = $pow/$nbValid; + else + $variance = 0; + $stdev = sqrt($variance); + + //Sort by duration to get median + $this->sort->reset(); + + $this->sort->loadFromObject( + array( + (object)array("property" => "durationSec", "direction" => "DESC") + ) + ); + + $this->updateIndexes(); + + $durations = array(); + for ($i = 0; $i < count($this->indexes); ++$i) { + if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) + //Invalid interval + continue; + + array_push($durations, $this->intervals[$this->indexes[$i]]->getDuration()); + } + + if (count($durations) > 0) + { + if (count($durations)%2 > 0) { + $median = $durations[count($durations)/2-0.5]; + } else { // else the number of intervals is an even number + $median = ($durations[count($durations)/2-1] + $durations[count($durations)/2])/2; + } + } + else + $median = 0; + + //Merge intervals to get density + $this->mergeIntervals(); + + $durationMergedTotal = 0; + for ($i = 0; $i < count($this->indexes); ++$i) { + if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) + //Invalid interval + continue; + + $durationMergedTotal += $this->intervals[$this->indexes[$i]]->getDuration(); + } + + if (($maxTime-$minTime) > 0) + $density = (($durationMergedTotal/($maxTime-$minTime))); + else + $density = 0; + + return array( + "minDuration" => $minDuration, + "minDurationIndex"=> $indexMinDuration, + "maxDuration" => $maxDuration, + "maxDurationIndex"=> $indexMaxDuration, + "mean" => $mean, + "stdev" => $stdev, + "median" => $median, + "density" => $density); + } + + public function getStatus() { + $nbFiltered = count($this->intervals) - count($this->indexes); + + $nbModified = 0; + $nbNew = 0; + $nbInvalid = 0; + $nbValid = 0; + for ($i = 0; $i < count($this->indexes); ++$i) { + if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) + ++$nbInvalid; + else + ++$nbValid; + if ($this->intervals[$this->indexes[$i]]->isModified()) + ++$nbModified; + if ($this->intervals[$this->indexes[$i]]->isNew()) + ++$nbNew; + } + + return array( + "nbFiltered" => $nbFiltered, + "nbModified" => $nbModified, + "nbNew" => $nbNew, + "nbInvalid" => $nbInvalid, + "nbValid" => $nbValid, + "isModified" => $this->isModified + ); + } + + public function getIntervalsArray($startIndex, $limit,$skipInvalid = false) { + $intervals = array(); + + if (!isset($startIndex)) + $startIndex = 0; + + if (!isset($limit)) + $limit = count($this->indexes); + + for ($i = 0; $i < $limit; ++$i) { + if ($startIndex+$i >= count($this->indexes)) + break; + if ($skipInvalid && ($this->intervals[$this->indexes[$startIndex+$i]]->getDuration() <= 0)) + continue; + array_push($intervals, $this->intervals[$this->indexes[$startIndex+$i]]->toArray()); + } + return $intervals; + } + + public function getLength() { + return count($this->indexes); + } + + public function getToken() { + return $this->token; + } + + public function getFilter() { + return $this->filter; + } + + public function getSort() { + return $this->sort; + } + + public function updateIndexes() { + $this->indexes = array(); + + for ($i = 0; $i < count($this->intervals); ++$i) + $this->intervals[$i]->setIndex($i); + + //Apply sort + $sort_result = $this->sort->apply($this->intervals); + + //Apply filter + for ($i = 0; $i < count($sort_result); ++$i) + { + if (!$this->filter->toFiltered($this->intervals[$sort_result[$i]])) + array_push($this->indexes,$this->intervals[$sort_result[$i]]->getIndex()); + } + } + + public function writeBin($handle) { + //Magic key ("TTC") + fwrite($handle,pack('C3',ord('T'),ord('T'),ord('C'))); + + //Version + fwrite($handle,pack('L',TimeTableCacheObject::$format_version)); + + //Token + for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) + fwrite($handle,pack('C',ord($this->token[$i]))); + + //Modified + fwrite($handle,pack('L',$this->isModified)); + + //Filter + $this->filter->writeBin($handle); + + //Sort + $this->sort->writeBin($handle); + + //Intervals + fwrite($handle,pack('L2',count($this->intervals), $this->lastId)); + foreach($this->intervals as $interval) + $interval->writeBin($handle); + + //Indexes + fwrite($handle,pack('L',count($this->indexes))); + foreach($this->indexes as $index) + fwrite($handle,pack('L',$index)); + } + + public function loadBin($handle) { + //Magic key ("TTC") + if (!$res = unpack('C3key',fread($handle,3))) + return false; + + if (($res['key1'] != ord('T')) || ($res['key2'] != ord('T')) || ($res['key3'] != ord('C'))) + return false; + + //Version + if (!$res = unpack('Lversion',fread($handle,4))) + return false; + if (($res['version'] != TimeTableCacheObject::$format_version)) + return false; + + //Token + $token = ""; + for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) + { + if (!$res = unpack('Ctoken',fread($handle,1))) + return false; + $token .= chr($res['token']); + } + $this->token = $token; + + //Modified + if (!$res = unpack('Lmodified',fread($handle,4))) + return false; + $this->isModified = $res['modified']; + + //Filter + $this->filter->loadBin($handle); + + //Sort + $this->sort->loadBin($handle); + + //Intervals + $res = unpack('L2data',fread($handle,2*4)); + $nbIntervals = $res['data1']; + $this->lastId = $res['data2']; + for ($i = 0; $i < $nbIntervals; ++$i) + { + $interval = new IntervalCacheObject(-1); + $interval->loadBin($handle); + array_push($this->intervals, $interval); + } + + //Indexes + $res = unpack('Ldata',fread($handle,4)); + $nbIndexes = $res['data']; + for ($i = 0; $i < $nbIndexes; ++$i) + { + $res = unpack('Lindex',fread($handle,4)); + array_push($this->indexes, $res['index']); + } + + return true; + } + + protected function getRandomToken() { + $letters = 'abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; + return substr(str_shuffle($letters), 0, TimeTableCacheObject::$token_len); + } + + public function dump() { + echo " => TimeTableCacheObject : token = ".$this->token.", nb intervals = ".count($this->intervals).", last id = ".$this->lastId.", nb indexes = ".count($this->indexes).PHP_EOL; + echo PHP_EOL; + + $this->filter->dump(); + echo PHP_EOL; + + $this->sort->dump(); + echo PHP_EOL; + + foreach ($this->intervals as $interval) + $interval->dump(); + echo PHP_EOL; + + echo " => Indexes list : "; + foreach ($this->indexes as $index) + { + echo $index.", "; + } + echo PHP_EOL; + } +} + +?> \ No newline at end of file diff --git a/php/classes/TimeTableMgr.php b/php/classes/TimeTableMgr.php index 49ebf52..0f32209 100644 --- a/php/classes/TimeTableMgr.php +++ b/php/classes/TimeTableMgr.php @@ -22,8 +22,6 @@ function timeFormat($myString) { class TimeTableMgr extends AmdaObjectMgr { -//TODO add catalogs as for requestMgr - function __construct($user) { parent::__construct('Tt.xml'); @@ -82,8 +80,13 @@ class TimeTableMgr extends AmdaObjectMgr { //if (!($p->intervals)) return true; return false; } + + /* + * In case of catalogs + */ + protected function setParamDescription($param) { } - + /* * Create Time Table */ @@ -95,12 +98,15 @@ class TimeTableMgr extends AmdaObjectMgr { $this->id = $this->setId(); $this->created = date('Y-m-d\TH:i:s'); if (!$this->id) return array('error' => ID_CREATION_ERROR); + $this->resFileName = USERTTDIR.$this->id.'.xml'; + //TODO catalog root element = 'timetable' $rootElement = $this->objectDom->createElement('timetable'); $rootElement->setAttribute('xml:id',$this->id); foreach ($p as $key => $value) - if ($key != 'id' && $key != 'leaf' && $key != 'nodeType') { + if ($key != 'id' && $key != 'leaf' && $key != 'nodeType' && + $key != 'objName' && $key != 'objFormat' && $key != 'folderId' && $key != 'cacheToken') { if ($key == 'created') { $rootElement->appendChild($this->objectDom->createElement($key, $this->created)); } @@ -114,8 +120,14 @@ class TimeTableMgr extends AmdaObjectMgr { $n_int++; } }*/ + // it is catalog + else if ($key == 'parameters') { + $paramsElement = $this->setParamDescription($value); + if ($paramsElement) $rootElement->appendChild($paramsElement); + + } else if ($key != 'intervals') - $rootElement->appendChild($this->objectDom->createElement($key, htmlspecialchars($value))); + $rootElement->appendChild($this->objectDom->createElement($key, htmlspecialchars($value))); } $this->objectDom->appendChild($rootElement); @@ -418,12 +430,20 @@ class TimeTableMgr extends AmdaObjectMgr { 'totalCount' => $intervals->length, 'intervals' => $result, 'start' => isset($start) ? $start : 0, - 'limit' => isset($limit) ? $limit : 0, + 'limit' => isset($limit) ? $limit : 0, 'success' => true ); } - + + protected function createIntervalElement($interval) { + + $newInterval = $this->objectDom->createElement('intervals'); + $newInterval->appendChild($this->objectDom->createElement('start',$interval->start)); + $newInterval->appendChild($this->objectDom->createElement('stop',$interval->stop)); + return $newInterval; + } + public function saveIntervals($id,$intervals,$action) { if (substr($id,0,6) == 'shared') { @@ -457,11 +477,9 @@ class TimeTableMgr extends AmdaObjectMgr { //add new intervals foreach ($intervals as $interval) - { - $newInterval = $this->objectDom->createElement('intervals'); - $newInterval->appendChild($this->objectDom->createElement('start',$interval->start)); - $newInterval->appendChild($this->objectDom->createElement('stop',$interval->stop)); - $this->objectDom->documentElement->appendChild($newInterval); + { + $newInterval = $this-> createIntervalElement($interval); + $this->objectDom->documentElement->appendChild($newInterval); } //save modifications -- libgit2 0.21.2