--- /dev/null
+manifold-tables -A -o authority -f \* -a R -j CACHE
--- /dev/null
+/**\r
+ * @license jahashtable, a JavaScript implementation of a hash table. It creates a single constructor function called\r
+ * Hashtable in the global scope.\r
+ *\r
+ * http://www.timdown.co.uk/jshashtable/\r
+ * Copyright %%build:year%% Tim Down.\r
+ * Version: %%build:version%%\r
+ * Build date: %%build:date%%\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+var Hashtable = (function(UNDEFINED) {\r
+ var FUNCTION = "function", STRING = "string", UNDEF = "undefined";\r
+\r
+ // Require Array.prototype.splice, Object.prototype.hasOwnProperty and encodeURIComponent. In environments not\r
+ // having these (e.g. IE <= 5), we bail out now and leave Hashtable null.\r
+ if (typeof encodeURIComponent == UNDEF ||\r
+ Array.prototype.splice === UNDEFINED ||\r
+ Object.prototype.hasOwnProperty === UNDEFINED) {\r
+ return null;\r
+ }\r
+\r
+ function toStr(obj) {\r
+ return (typeof obj == STRING) ? obj : "" + obj;\r
+ }\r
+\r
+ function hashObject(obj) {\r
+ var hashCode;\r
+ if (typeof obj == STRING) {\r
+ return obj;\r
+ } else if (typeof obj.hashCode == FUNCTION) {\r
+ // Check the hashCode method really has returned a string\r
+ hashCode = obj.hashCode();\r
+ return (typeof hashCode == STRING) ? hashCode : hashObject(hashCode);\r
+ } else {\r
+ return toStr(obj);\r
+ }\r
+ }\r
+ \r
+ function merge(o1, o2) {\r
+ for (var i in o2) {\r
+ if (o2.hasOwnProperty(i)) {\r
+ o1[i] = o2[i];\r
+ }\r
+ }\r
+ }\r
+\r
+ function equals_fixedValueHasEquals(fixedValue, variableValue) {\r
+ return fixedValue.equals(variableValue);\r
+ }\r
+\r
+ function equals_fixedValueNoEquals(fixedValue, variableValue) {\r
+ return (typeof variableValue.equals == FUNCTION) ?\r
+ variableValue.equals(fixedValue) : (fixedValue === variableValue);\r
+ }\r
+\r
+ function createKeyValCheck(kvStr) {\r
+ return function(kv) {\r
+ if (kv === null) {\r
+ throw new Error("null is not a valid " + kvStr);\r
+ } else if (kv === UNDEFINED) {\r
+ throw new Error(kvStr + " must not be undefined");\r
+ }\r
+ };\r
+ }\r
+\r
+ var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");\r
+\r
+ /*----------------------------------------------------------------------------------------------------------------*/\r
+\r
+ function Bucket(hash, firstKey, firstValue, equalityFunction) {\r
+ this[0] = hash;\r
+ this.entries = [];\r
+ this.addEntry(firstKey, firstValue);\r
+\r
+ if (equalityFunction !== null) {\r
+ this.getEqualityFunction = function() {\r
+ return equalityFunction;\r
+ };\r
+ }\r
+ }\r
+\r
+ var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;\r
+\r
+ function createBucketSearcher(mode) {\r
+ return function(key) {\r
+ var i = this.entries.length, entry, equals = this.getEqualityFunction(key);\r
+ while (i--) {\r
+ entry = this.entries[i];\r
+ if ( equals(key, entry[0]) ) {\r
+ switch (mode) {\r
+ case EXISTENCE:\r
+ return true;\r
+ case ENTRY:\r
+ return entry;\r
+ case ENTRY_INDEX_AND_VALUE:\r
+ return [ i, entry[1] ];\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ };\r
+ }\r
+\r
+ function createBucketLister(entryProperty) {\r
+ return function(aggregatedArr) {\r
+ var startIndex = aggregatedArr.length;\r
+ for (var i = 0, entries = this.entries, len = entries.length; i < len; ++i) {\r
+ aggregatedArr[startIndex + i] = entries[i][entryProperty];\r
+ }\r
+ };\r
+ }\r
+\r
+ Bucket.prototype = {\r
+ getEqualityFunction: function(searchValue) {\r
+ return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;\r
+ },\r
+\r
+ getEntryForKey: createBucketSearcher(ENTRY),\r
+\r
+ getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE),\r
+\r
+ removeEntryForKey: function(key) {\r
+ var result = this.getEntryAndIndexForKey(key);\r
+ if (result) {\r
+ this.entries.splice(result[0], 1);\r
+ return result[1];\r
+ }\r
+ return null;\r
+ },\r
+\r
+ addEntry: function(key, value) {\r
+ this.entries.push( [key, value] );\r
+ },\r
+\r
+ keys: createBucketLister(0),\r
+\r
+ values: createBucketLister(1),\r
+\r
+ getEntries: function(destEntries) {\r
+ var startIndex = destEntries.length;\r
+ for (var i = 0, entries = this.entries, len = entries.length; i < len; ++i) {\r
+ // Clone the entry stored in the bucket before adding to array\r
+ destEntries[startIndex + i] = entries[i].slice(0);\r
+ }\r
+ },\r
+\r
+ containsKey: createBucketSearcher(EXISTENCE),\r
+\r
+ containsValue: function(value) {\r
+ var entries = this.entries, i = entries.length;\r
+ while (i--) {\r
+ if ( value === entries[i][1] ) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ };\r
+\r
+ /*----------------------------------------------------------------------------------------------------------------*/\r
+\r
+ // Supporting functions for searching hashtable buckets\r
+\r
+ function searchBuckets(buckets, hash) {\r
+ var i = buckets.length, bucket;\r
+ while (i--) {\r
+ bucket = buckets[i];\r
+ if (hash === bucket[0]) {\r
+ return i;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ function getBucketForHash(bucketsByHash, hash) {\r
+ var bucket = bucketsByHash[hash];\r
+\r
+ // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype\r
+ return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;\r
+ }\r
+\r
+ /*----------------------------------------------------------------------------------------------------------------*/\r
+\r
+ function Hashtable() {\r
+ var buckets = [];\r
+ var bucketsByHash = {};\r
+ var properties = {\r
+ replaceDuplicateKey: true,\r
+ hashCode: hashObject,\r
+ equals: null\r
+ };\r
+\r
+ var arg0 = arguments[0], arg1 = arguments[1];\r
+ if (arg1 !== UNDEFINED) {\r
+ properties.hashCode = arg0;\r
+ properties.equals = arg1;\r
+ } else if (arg0 !== UNDEFINED) {\r
+ merge(properties, arg0);\r
+ }\r
+\r
+ var hashCode = properties.hashCode, equals = properties.equals;\r
+\r
+ this.properties = properties;\r
+\r
+ this.put = function(key, value) {\r
+ checkKey(key);\r
+ checkValue(value);\r
+ var hash = hashCode(key), bucket, bucketEntry, oldValue = null;\r
+\r
+ // Check if a bucket exists for the bucket key\r
+ bucket = getBucketForHash(bucketsByHash, hash);\r
+ if (bucket) {\r
+ // Check this bucket to see if it already contains this key\r
+ bucketEntry = bucket.getEntryForKey(key);\r
+ if (bucketEntry) {\r
+ // This bucket entry is the current mapping of key to value, so replace the old value.\r
+ // Also, we optionally replace the key so that the latest key is stored.\r
+ if (properties.replaceDuplicateKey) {\r
+ bucketEntry[0] = key;\r
+ }\r
+ oldValue = bucketEntry[1];\r
+ bucketEntry[1] = value;\r
+ } else {\r
+ // The bucket does not contain an entry for this key, so add one\r
+ bucket.addEntry(key, value);\r
+ }\r
+ } else {\r
+ // No bucket exists for the key, so create one and put our key/value mapping in\r
+ bucket = new Bucket(hash, key, value, equals);\r
+ buckets.push(bucket);\r
+ bucketsByHash[hash] = bucket;\r
+ }\r
+ return oldValue;\r
+ };\r
+\r
+ this.get = function(key) {\r
+ checkKey(key);\r
+\r
+ var hash = hashCode(key);\r
+\r
+ // Check if a bucket exists for the bucket key\r
+ var bucket = getBucketForHash(bucketsByHash, hash);\r
+ if (bucket) {\r
+ // Check this bucket to see if it contains this key\r
+ var bucketEntry = bucket.getEntryForKey(key);\r
+ if (bucketEntry) {\r
+ // This bucket entry is the current mapping of key to value, so return the value.\r
+ return bucketEntry[1];\r
+ }\r
+ }\r
+ return null;\r
+ };\r
+\r
+ this.containsKey = function(key) {\r
+ checkKey(key);\r
+ var bucketKey = hashCode(key);\r
+\r
+ // Check if a bucket exists for the bucket key\r
+ var bucket = getBucketForHash(bucketsByHash, bucketKey);\r
+\r
+ return bucket ? bucket.containsKey(key) : false;\r
+ };\r
+\r
+ this.containsValue = function(value) {\r
+ checkValue(value);\r
+ var i = buckets.length;\r
+ while (i--) {\r
+ if (buckets[i].containsValue(value)) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ };\r
+\r
+ this.clear = function() {\r
+ buckets.length = 0;\r
+ bucketsByHash = {};\r
+ };\r
+\r
+ this.isEmpty = function() {\r
+ return !buckets.length;\r
+ };\r
+\r
+ var createBucketAggregator = function(bucketFuncName) {\r
+ return function() {\r
+ var aggregated = [], i = buckets.length;\r
+ while (i--) {\r
+ buckets[i][bucketFuncName](aggregated);\r
+ }\r
+ return aggregated;\r
+ };\r
+ };\r
+\r
+ this.keys = createBucketAggregator("keys");\r
+ this.values = createBucketAggregator("values");\r
+ this.entries = createBucketAggregator("getEntries");\r
+\r
+ this.remove = function(key) {\r
+ checkKey(key);\r
+\r
+ var hash = hashCode(key), bucketIndex, oldValue = null;\r
+\r
+ // Check if a bucket exists for the bucket key\r
+ var bucket = getBucketForHash(bucketsByHash, hash);\r
+\r
+ if (bucket) {\r
+ // Remove entry from this bucket for this key\r
+ oldValue = bucket.removeEntryForKey(key);\r
+ if (oldValue !== null) {\r
+ // Entry was removed, so check if bucket is empty\r
+ if (bucket.entries.length == 0) {\r
+ // Bucket is empty, so remove it from the bucket collections\r
+ bucketIndex = searchBuckets(buckets, hash);\r
+ buckets.splice(bucketIndex, 1);\r
+ delete bucketsByHash[hash];\r
+ }\r
+ }\r
+ }\r
+ return oldValue;\r
+ };\r
+\r
+ this.size = function() {\r
+ var total = 0, i = buckets.length;\r
+ while (i--) {\r
+ total += buckets[i].entries.length;\r
+ }\r
+ return total;\r
+ };\r
+ }\r
+\r
+ Hashtable.prototype = {\r
+ each: function(callback) {\r
+ var entries = this.entries(), i = entries.length, entry;\r
+ while (i--) {\r
+ entry = entries[i];\r
+ callback(entry[0], entry[1]);\r
+ }\r
+ },\r
+\r
+ equals: function(hashtable) {\r
+ var keys, key, val, count = this.size();\r
+ if (count == hashtable.size()) {\r
+ keys = this.keys();\r
+ while (count--) {\r
+ key = keys[count];\r
+ val = hashtable.get(key);\r
+ if (val === null || val !== this.get(key)) {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+ },\r
+\r
+ putAll: function(hashtable, conflictCallback) {\r
+ var entries = hashtable.entries();\r
+ var entry, key, value, thisValue, i = entries.length;\r
+ var hasConflictCallback = (typeof conflictCallback == FUNCTION);\r
+ while (i--) {\r
+ entry = entries[i];\r
+ key = entry[0];\r
+ value = entry[1];\r
+\r
+ // Check for a conflict. The default behaviour is to overwrite the value for an existing key\r
+ if ( hasConflictCallback && (thisValue = this.get(key)) ) {\r
+ value = conflictCallback(key, thisValue, value);\r
+ }\r
+ this.put(key, value);\r
+ }\r
+ },\r
+\r
+ clone: function() {\r
+ var clone = new Hashtable(this.properties);\r
+ clone.putAll(this);\r
+ return clone;\r
+ }\r
+ };\r
+\r
+ Hashtable.prototype.toQueryString = function() {\r
+ var entries = this.entries(), i = entries.length, entry;\r
+ var parts = [];\r
+ while (i--) {\r
+ entry = entries[i];\r
+ parts[i] = encodeURIComponent( toStr(entry[0]) ) + "=" + encodeURIComponent( toStr(entry[1]) );\r
+ }\r
+ return parts.join("&");\r
+ };\r
+\r
+ return Hashtable;\r
+})();\r
+var guid = (function() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return function() {
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+ };
+})();
+
function ManifoldQuery(action, object, timestamp, filters, params, fields, unique, query_uuid, aq, sq) {
// get, update, delete, create
var action;
});
};
- if (this.analyzed_query !== undefined)
+ if (!!this.analyzed_query)
query = this.analyzed_query;
else
query = this;
else
this.unique = unique;
- this.query_uuid = query_uuid;
+ if (typeof unique == "undefined")
+ this.query_uuid = guid();
+ else
+ this.query_uuid = query_uuid;
if (typeof aq == "undefined")
this.analyzed_query = null;
var CLEAR_FIELDS = 6;
var NEW_RECORD = 7;
var CLEAR_RECORDS = 8;
+
+/**
+ * event: FIELD_STATE_CHANGED
+ *
+ * Parameters:
+ * dict :
+ * .request : ???? used to be FIELD_REQUEST_ADD / FIELD_REQUEST_REMOVE
+ * .key : ??? the key fields of the record
+ * .value : the key of the record who has received an update
+ * .status : the new state of the record
+ * TODO rename to state, and use values from STATE_SET
+ */
var FIELD_STATE_CHANGED = 9;
var IN_PROGRESS = 101;
var DONE = 102;
/* Update requests related to subqueries */
+
+/**
+ * event: SET_ADD
+ *
+ * Parameters:
+ * string : The key of the element being added
+ */
var SET_ADD = 201;
+
+/**
+ * event: SET_REMOVED
+ *
+ * Parameters:
+ * string : The key of the element being removed
+ */
var SET_REMOVED = 202;
+
// request
var FIELD_REQUEST_CHANGE = 301;
var FIELD_REQUEST_ADD = 302;
var FIELD_REQUEST_REMOVE = 303;
var FIELD_REQUEST_ADD_RESET = 304;
var FIELD_REQUEST_REMOVE_RESET = 305;
-// status
+// status (XXX Should be deprecated)
var FIELD_REQUEST_PENDING = 401;
var FIELD_REQUEST_SUCCESS = 402;
var FIELD_REQUEST_FAILURE = 403;
+var STATUS_OKAY = 404;
+var STATUS_SET_WARNING = 405;
+var STATUS_ADD_WARNING = 406;
+var STATUS_REMOVE_WARNING = 407;
+var STATUS_RESET = 408;
+
+/* Requests for query cycle */
+var RUN_UPDATE = 601;
+
+/* MANIFOLD types */
+var TYPE_VALUE = 1;
+var TYPE_RECORD = 2;
+var TYPE_LIST_OF_VALUES = 3;
+var TYPE_LIST_OF_RECORDS = 4;
+
+/******************************************************************************
+ * QUERY STATUS (for manifold events)
+ ******************************************************************************/
-/* Query status */
var STATUS_NONE = 500; // Query has not been started yet
var STATUS_GET_IN_PROGRESS = 501; // Query has been sent, no result has been received
var STATUS_GET_RECEIVED = 502; // Success
var STATUS_UPDATE_RECEIVED = 506;
var STATUS_UPDATE_ERROR = 507;
-/* Requests for query cycle */
-var RUN_UPDATE = 601;
+/******************************************************************************
+ * QUERY STATE (for query_store)
+ ******************************************************************************/
-/* MANIFOLD types */
-var TYPE_VALUE = 1;
-var TYPE_RECORD = 2;
-var TYPE_LIST_OF_VALUES = 3;
-var TYPE_LIST_OF_RECORDS = 4;
+// XXX Rendundant with query status ?
+
+var QUERY_STATE_INIT = 0;
+var QUERY_STATE_INPROGRESS = 1;
+var QUERY_STATE_DONE = 2;
+
+/******************************************************************************
+ * RECORD STATES (for query_store)
+ ******************************************************************************/
+
+var STATE_SET = 0;
+var STATE_WARNINGS = 1;
+var STATE_VISIBLE = 2;
+// STATE_SET : enum
+var STATE_SET_IN = 0;
+var STATE_SET_OUT = 1;
+var STATE_SET_IN_PENDING = 2;
+var STATE_SET_OUT_PENDING = 3;
+var STATE_SET_IN_SUCCESS = 4;
+var STATE_SET_OUT_SUCCESS = 5;
+var STATE_SET_IN_FAILURE = 6;
+var STATE_SET_OUT_FAILURE = 7;
+
+// STATE_WARNINGS : dict
+
+// STATE_VISIBLE : boolean
+
+/******************************************************************************
+ * CONSTRAINTS
+ ******************************************************************************/
+
+var CONSTRAINT_RESERVABLE_LEASE = 0;
// A structure for storing queries
-function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, disabled) {
+function QueryExt(query, parent_query_ext, main_query_ext, update_query_ext, disabled, domain_query_ext) {
/* Constructor */
if (typeof query == "undefined")
this.main_query_ext = (typeof main_query_ext == "undefined") ? null : main_query_ext;
this.update_query_ext = (typeof update_query_ext == "undefined") ? null : update_query_ext;
this.update_query_orig_ext = (typeof update_query_orig_ext == "undefined") ? null : update_query_orig_ext;
- this.disabled = (typeof update_query_ext == "undefined") ? false : disabled;
+ this.disabled = (typeof disabled == "undefined") ? false : disabled;
+
+ // A domain query is a query that is issued to retrieve all possible values for a set
+ // eg. all resources that can be attached to a slice
+ // It is null unless we are a subquery for which a domain query has been issued
+ this.domain_query_ext = (typeof domain_query_ext == "undefined") ? null : domain_query_ext;
+
+ // Set members to buffer until the domain query is completed
+ // A list of keys
+ this.set_members = [];
+
+ // The set query is the query for which the domain query has been issued.
+ // It is null unless the query is a domain query
+ this.set_query_ext = (typeof set_query_ext == "undefined") ? null : domain_query_ext;
- this.status = null;
- this.results = null;
+ this.query_state = QUERY_STATE_INIT;
+
+ // Results from a query consists in a dict that maps keys to records
+ this.records = new Hashtable();
+
+ // Status is a dict that maps keys to record status
+ this.state = new Hashtable();
+
+ // Filters that impact visibility in the local interface
+ this.filters = [];
+
// update_query null unless we are a main_query (aka parent_query == null); only main_query_fields can be updated...
}
// We also need to insert all queries and subqueries from the analyzed_query
// XXX We need the root of all subqueries
query.iter_subqueries(function(sq, data, parent_query) {
- if (parent_query)
+ var parent_query_ext;
+ if (parent_query) {
parent_query_ext = manifold.query_store.find_analyzed_query_ext(parent_query.query_uuid);
- else
+ } else {
parent_query_ext = null;
+ }
// XXX parent_query_ext == false
// XXX main.subqueries = {} # Normal, we need analyzed_query
sq_ext = new QueryExt(sq, parent_query_ext, query_ext)
+
+ if (parent_query) {
+ /* Let's issue a query for the subquery domain. This query will not need any update etc.
+ eg. for resources in a slice, we also query all resources */
+ var all_fields = manifold.metadata.get_field_names(sq.object);
+ var domain_query = new ManifoldQuery('get', sq.object, 'now', [], {}, all_fields);
+ //var domain_query = new ManifoldQuery('get', sq.object);
+
+ console.log("Created domain query", domain_query);
+ var domain_query_ext = new QueryExt(domain_query);
+
+ domain_query_ext.set_query_ext = sq_ext;
+ sq_ext.domain_query_ext = domain_query_ext;
+
+ // One of these two is useless ?
+ manifold.query_store.main_queries[domain_query.query_uuid] = domain_query_ext;
+ manifold.query_store.analyzed_queries[domain_query.query_uuid] = domain_query_ext;
+
+ // XXX This query is run before the plugins are initialized and listening
+ manifold.run_query(domain_query);
+ }
+
manifold.query_store.analyzed_queries[sq.query_uuid] = sq_ext;
});
/* Searching */
- this.find_query_ext = function(query_uuid) {
+ this.find_query_ext = function(query_uuid)
+ {
return this.main_queries[query_uuid];
}
- this.find_query = function(query_uuid) {
+ this.find_query = function(query_uuid)
+ {
return this.find_query_ext(query_uuid).query;
}
- this.find_analyzed_query_ext = function(query_uuid) {
+ this.find_analyzed_query_ext = function(query_uuid)
+ {
return this.analyzed_queries[query_uuid];
}
- this.find_analyzed_query = function(query_uuid) {
+ this.find_analyzed_query = function(query_uuid)
+ {
return this.find_analyzed_query_ext(query_uuid).query;
}
+
+ this.state_dict_create = function(default_set)
+ {
+ default_set = (default_set === undefined) ? STATE_SET_OUT : default_set;
+ var state_dict = {};
+ // We cannot use constants in literal definition, so...
+ state_dict[STATE_WARNINGS] = {};
+ state_dict[STATE_SET] = default_set;
+ state_dict[STATE_VISIBLE] = true;
+ return state_dict;
+ }
+
+ // RECORDS
+
+ this.set_records = function(query_uuid, records, default_set)
+ {
+ default_set = (default_set === undefined) ? STATE_SET_OUT : default_set;
+
+ var self = this;
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ var record_key = manifold.metadata.get_key(query_ext.query.object);
+ $.each(records, function(i, record) {
+ var key = manifold.metadata.get_key(query_ext.query.object);
+ // ["start_time", "resource", "end_time"]
+ // ["urn"]
+
+ var record_key_value = manifold.record_get_value(record, record_key);
+ query_ext.records.put(record_key_value, record);
+
+ if (!(query_ext.state.get(record_key_value)))
+ query_ext.state.put(record_key_value, self.state_dict_create(default_set));
+ });
+ }
+
+ this.get_records = function(query_uuid)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ return query_ext.records.values();
+ }
+
+ this.get_record = function(query_uuid, record_key)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ return query_ext.records.get(record_key);
+ }
+
+ this.add_record = function(query_uuid, record, new_state)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+
+ var key = manifold.metadata.get_key(query_ext.query.object);
+ var record_key = manifold.record_get_value(record, key);
+
+ var record_entry = query_ext.records.get(record_key);
+ if (!record_entry)
+ query_ext.records.put(record_key, record);
+
+ manifold.query_store.set_record_state(query_uuid, record_key, STATE_SET, new_state);
+ }
+
+ this.remove_record = function(query_uuid, record, new_state)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+
+ var key = manifold.metadata.get_key(query_ext.query.object);
+ var record_key = manifold.record_get_value(record, key);
+
+ manifold.query_store.set_record_state(query_uuid, record_key, STATE_SET, new_state);
+ }
+
+ this.iter_records = function(query_uuid, callback)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ query_ext.records.each(callback);
+ //callback = function(record_key, record)
+ }
+
+ this.iter_visible_records = function(query_uuid, callback)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ query_ext.records.each(function(record_key, record) {
+ if (query_ext.state.get(record_key)[STATE_VISIBLE]) // .STATE_VISIBLE would be for the string key
+ callback(record_key, record);
+ });
+ //callback = function(record_key, record)
+
+ }
+
+ // STATE
+
+ this.set_record_state = function(query_uuid, result_key, state, value)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ var state_dict = query_ext.state.get(result_key);
+ if (!state_dict)
+ state_dict = this.state_dict_create();
+
+ state_dict[state] = value;
+
+ query_ext.state.put(result_key, state_dict);
+ }
+
+ this.get_record_state = function(query_uuid, result_key, state)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ var state_dict = query_ext.state.get(result_key);
+ if (!state_dict)
+ return null;
+ return state_dict[state];
+ }
+
+ // FILTERS
+
+ this.add_filter = function(query_uuid, filter)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ // XXX When we update a filter
+ query_ext.filters.push(filter);
+
+ this.apply_filters(query_uuid);
+
+ }
+
+ this.update_filter = function(query_uuid, filter)
+ {
+ // XXX
+
+ this.apply_filters(query_uuid);
+ }
+
+ this.remove_filter = function(query_uuid, filter)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ query_ext.filters = $.grep(query_ext.filters, function(x) {
+ return x == filter;
+ });
+
+ this.apply_filters(query_uuid);
+ }
+
+ this.get_filters = function(query_uuid)
+ {
+ var query_ext = this.find_analyzed_query_ext(query_uuid);
+ return query_ext.filters;
+ }
+
+ this.apply_filters = function(query_uuid)
+ {
+ // Toggle visibility of records according to the different filters.
+
+ var self = this;
+ var filters = this.get_filters(query_uuid);
+ var col_value;
+
+ // Adapted from querytable._querytable_filter()
+
+ this.iter_records(query_uuid, function(record_key, record) {
+ var visible = true;
+
+ // We go through each filter and decide whether it affects the visibility of the record
+ $.each(filters, function(index, filter) {
+ var key = filter[0];
+ var op = filter[1];
+ var value = filter[2];
+
+
+ /* We do some special handling for the manifold:status filter
+ * predicates. */
+
+ if (key == 'manifold:status') {
+ if (op != '=' && op != '==') {
+ // Unsupported filter, let's ignore it
+ console.log("Unsupported filter on manifold:status. Should be EQUAL only.");
+ return true; // ~ continue
+ }
+
+ var record_state = manifold.query_store.get_record_state(query_uuid, record_key, STATE_SET);
+ var record_warnings = manifold.query_store.get_record_state(query_uuid, record_key, STATE_WARNINGS);
+
+ switch (value) {
+ case 'reserved':
+ visible = (record_state == STATE_SET_IN)
+ || (record_state == STATE_SET_OUT_PENDING)
+ || (record_state == STATE_SET_IN_SUCCESS)
+ || (record_state == STATE_SET_OUT_FAILURE);
+ // visible = true => ~ continue
+ // visible = false => ~ break
+ return visible;
+
+ case 'unconfigured':
+ var in_set = (record_state == STATE_SET_IN) // should not have warnings
+ || (record_state == STATE_SET_IN_PENDING)
+ || (record_state == STATE_SET_IN_SUCCESS)
+ || (record_state == STATE_SET_OUT_FAILURE); // should not have warnings
+ visible = (in_set && !$.isEmptyObject(record_warnings));
+ return visible;
+
+ case 'pending':
+ visible = (record_state == STATE_SET_IN_PENDING)
+ || (record_state == STATE_SET_OUT_PENDING);
+ return visible;
+ }
+ return false; // ~ break
+ }
+
+ /* Normal filtering behaviour (according to the record content) follows... */
+ col_value = manifold.record_get_value(record, record_key);
+
+ // When the filter does not match, we hide the column by default
+ if (col_value === 'undefined') {
+ visible = false;
+ return false; // ~ break
+ }
+
+ // XXX This should accept pluggable filtering functions.
+
+
+ /* Test whether current filter is compatible with the column */
+ if (op == '=' || op == '==') {
+ if ( col_value != value || col_value==null || col_value=="" || col_value=="n/a")
+ visible = false;
+ }else if (op == 'included') {
+ $.each(value, function(i,x) {
+ if(x == col_value){
+ visible = true;
+ return false; // ~ break
+ }else{
+ visible = false;
+ }
+ });
+ }else if (op == '!=') {
+ if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a")
+ visible = false;
+ } else if(op=='<') {
+ if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a")
+ visible = false;
+ } else if(op=='>') {
+ if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a")
+ visible = false;
+ } else if(op=='<=' || op=='≤') {
+ if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a")
+ visible = false;
+ } else if(op=='>=' || op=='≥') {
+ if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a")
+ visible = false;
+ }else{
+ // How to break out of a loop ?
+ alert("filter not supported");
+ return false; // break
+ }
+
+ });
+
+ // Set the visibility status in the query store
+ self.set_record_state(query_uuid, record_key, STATE_VISIBLE, visible);
+ });
+
+ }
+
}
/*!
}
},
+ /**
+ * Args:
+ * fields: A String instance (field name), or a set of String instances
+ * (field names) # XXX tuple !!
+ * Returns:
+ * If fields is a String, return the corresponding value.
+ * If fields is a set, return a tuple of corresponding value.
+ *
+ * Raises:
+ * KeyError if at least one of the fields is not found
+ */
+ record_get_value: function(record, fields)
+ {
+ if (typeof(fields) === 'string') {
+ if (fields.indexOf('.') != -1) {
+ key_subkey = key.split('.', 2);
+ key = key_subkey[0];
+ subkey = key_subkey[1];
+
+ if (record.indexOf(key) == -1) {
+ return null;
+ }
+ // Tests if the following is an array (typeof would give object)
+ if (Object.prototype.toString.call(record[key]) === '[object Array]') {
+ // Records
+ return $.map(record[key], function(subrecord) { return manifold.record_get_value(subrecord, subkey) });
+ } else if (typeof(record) == 'object') {
+ // Record
+ return manifold.record_get_value(record[key], subkey);
+ } else {
+ console.log('Unknown field');
+ }
+ } else {
+ return record[fields];
+ }
+ } else {
+ // see. get_map_entries
+ if (fields.length == 1)
+ return manifold.record_get_value(record, fields[0])
+
+ // Build a new record
+ var ret = {};
+ $.each(fields, function(i, field) {
+ ret[field] = manifold.record_get_value(record, field);
+ });
+ ret.hashCode = record.hashCode;
+ ret.equals = record.equals;
+ return ret;
+ // this was an array, we want a dictionary
+ //return $.map(fields, function(x) { manifold.record_get_value(record, x) });
+
+ }
+ },
+
+ record_hashcode: function(key_fields)
+ {
+ return function() {
+ ret = "";
+ for (i=0; i < key_fields.length; i++)
+ ret += "@@" + this[key_fields[i]];
+ return ret;
+ };
+ },
+
+ record_equals: function(key_fields)
+ {
+ var self = this;
+
+ return function(other) {
+ for (i=0; i < key_fields.length; i++) {
+ var this_value = this[key_fields[i]];
+ var other_value = other[key_fields[i]];
+
+ var this_type = self.get_type(this_value);
+ var other_type = self.get_type(other_value);
+ if (this_type != other_type)
+ return false;
+
+ switch (this_type) {
+ case TYPE_VALUE:
+ case TYPE_LIST_OF_VALUES:
+ if (this_value != other_value)
+ return false;
+ break;
+ case TYPE_RECORD:
+ if (!(record_equals(this_value, other_value)))
+ return false;
+ break;
+ case TYPE_LIST_OF_RECORDS:
+ if (this_value.length != other_value.length)
+ return false;
+ for (i = 0; i < this_value.length; i++)
+ if (!(record_equals(this_value, other_value)))
+ return false;
+ break;
+ }
+ }
+ return true;
+ };
+ },
+
+
/**************************************************************************
* Metadata management
**************************************************************************/
return (typeof table.column === 'undefined') ? null : table.column;
},
+ get_field_names: function(method)
+ {
+ var columns = this.get_columns(method);
+ if (!columns)
+ return null;
+ return $.map(columns, function (x) { return x.name });
+ },
+
get_key: function(method) {
var table = this.get_table(method);
if (!table)
if (!table)
return null;
+ var match = $.grep(table.column, function(x) { return x.name == name });
+ if (match.length == 0) {
+ return undefined;
+ } else {
+ return match[0].type;
+ }
return (typeof table.type === 'undefined') ? null : table.type;
}
// NEW API
manifold.query_store.insert(query);
+ // Run
+ manifold.run_query(query);
+
// FORMER API
if (query.analyzed_query == null) {
query.analyze_subqueries();
}
},
- run_query: function(query, callback) {
+ run_query: function(query, callback)
+ {
// default value for callback = null
if (typeof callback === 'undefined')
callback = null;
+ var query_ext = manifold.query_store.find_query_ext(query.query_uuid);
+ query_ext.query_state = QUERY_STATE_INPROGRESS;
+
var query_json = JSON.stringify(query);
- /* Nothing related to pubsub here... for the moment at least. */
- //query.iter_subqueries(function (sq) {
- // manifold.raise_record_event(sq.query_uuid, IN_PROGRESS);
- //});
+ // Inform plugins about the progress
+ query.iter_subqueries(function (sq) {
+ var sq_query_ext = manifold.query_store.find_analyzed_query_ext(sq.query_uuid);
+ sq_query_ext.query_state = QUERY_STATE_INPROGRESS;
+
+ manifold.raise_record_event(sq.query_uuid, IN_PROGRESS);
+ });
+
$.post(manifold.proxy_url, {'json': query_json} , manifold.success_closure(query, null, callback));
},
+ // XXX DEPRECATED
// Executes all async. queries - intended for the javascript header to initialize queries
// input queries are specified as a list of {'query_uuid': <query_uuid> }
// each plugin is responsible for managing its spinner through on_query_in_progress
messages.debug(".. publish_result (5) END q=" + query.__repr());
},
+ store_records: function(query, records) {
+ // Store records
+ var query_ext = manifold.query_store.find_analyzed_query_ext(query.query_uuid);
+ if (query_ext.set_query_ext) {
+ // We have a domain query
+ // The results are stored in the corresponding set_query
+ manifold.query_store.set_records(query_ext.set_query_ext.query.query_uuid, records)
+
+ } else if (query_ext.domain_query_ext) {
+ // We have a set query, it is only used to determine which objects are in the set, we should only retrieve the key
+ // Has it a domain query, and has it completed ?
+ $.each(records, function(i, record) {
+ var key = manifold.metadata.get_key(query.object);
+ var record_key = manifold.record_get_value(record, key);
+ manifold.query_store.set_record_state(query.query_uuid, record_key, STATE_SET, STATE_SET_IN);
+ });
+
+ } else {
+ // We have a normal query
+ manifold.query_store.set_records(query.query_uuid, records, STATE_SET_IN);
+ }
+ },
+
/*!
* Recursively publish result
* \fn publish_result_rec(query, result)
*
* Note: this function works on the analyzed query
*/
- publish_result_rec: function(query, result) {
+ publish_result_rec: function(query, records) {
/* If the result is not unique, only publish the top query;
* otherwise, publish the main object as well as subqueries
* XXX how much recursive are we ?
*/
if (manifold.pubsub_debug)
- messages.debug (">>>>> publish_result_rec " + query.object);
+ messages.debug (">>>>> publish_result_rec " + query.object);
if (manifold.query_expects_unique_result(query)) {
/* Also publish subqueries */
$.each(query.subqueries, function(object, subquery) {
- manifold.publish_result_rec(subquery, result[0][object]);
+ manifold.publish_result_rec(subquery, records[0][object]);
/* TODO remove object from result */
});
}
if (manifold.pubsub_debug)
messages.debug ("===== publish_result_rec " + query.object);
- manifold.publish_result(query, result);
+ var query_ext = manifold.query_store.find_analyzed_query_ext(query.query_uuid);
+ query_ext.query_state = QUERY_STATE_DONE;
+
+ this.store_records(query, records);
+
+ var pub_query;
+
+ if (query_ext.set_query_ext) {
+ if (query_ext.set_query_ext.query_state != QUERY_STATE_DONE)
+ return;
+ pub_query = query_ext.set_query_ext.query;
+ } else if (query_ext.domain_query_ext) {
+ if (query_ext.domain_query_ext.query_state != QUERY_STATE_DONE)
+ return;
+ pub_query = query;
+ } else {
+ pub_query = query;
+ }
+ // We can only publish results if the query (and its related domain query) is complete
+ manifold.publish_result(pub_query, records);
if (manifold.pubsub_debug)
messages.debug ("<<<<< publish_result_rec " + query.object);
var record = records[0];
var update_query_ext = query_ext.update_query_ext;
+
+ console.log("Update case not handled yet!");
+ if (!update_query_ext)
+ return;
+
var update_query = update_query_ext.query;
var update_query_ext = query_ext.update_query_ext;
var update_query_orig = query_ext.update_query_orig_ext.query;
if (!subrecords)
continue
$.each(subrecords, function (i, subrecord) {
- if (key.length == 1){
- key = key[0];
- sq_keys.push(subrecord[key]);
- }else{
- // more than what's necessary, but should work
- sq_keys.push(subrecord);
- }
+ sq_keys.push(manifold.record_get_value(subrecord, key));
});
update_query.params[method] = sq_keys;
update_query_orig.params[method] = sq_keys.slice();
process_get_query_records: function(query, records) {
this.setup_update_query(query, records);
+
+ var query_ext = manifold.query_store.find_query_ext(query.query_uuid);
+ query_ext.query_state = QUERY_STATE_DONE;
/* Publish full results */
- var tmp_query = manifold.find_query(query.query_uuid);
- manifold.publish_result_rec(tmp_query.analyzed_query, records);
+ var tmp_query = manifold.query_store.find_analyzed_query(query.query_uuid);
+ manifold.publish_result_rec(tmp_query, records);
+ },
+
+ make_records: function(object, records)
+ {
+ $.each(records, function(i, record) {
+ manifold.make_record(object, record);
+ });
+ },
+
+ make_record: function(object, record)
+ {
+ // To make an object a record, we just add the hash function
+ var key = manifold.metadata.get_key(object);
+ record.hashCode = manifold.record_hashcode(key.sort());
+ record.equals = manifold.record_equals(key);
+
+ // Looking after subrecords
+ for (var field in record) {
+ var result_value = record[field];
+
+ switch (this.get_type(result_value)) {
+ case TYPE_RECORD:
+ var subobject = manifold.metadata.get_type(object, field);
+ if (subobject)
+ manifold.make_record(subobject, result_value);
+ break;
+ case TYPE_LIST_OF_RECORDS:
+ var subobject = manifold.metadata.get_type(object, field);
+ if (subobject)
+ manifold.make_records(subobject, result_value);
+ break;
+ }
+ }
},
/**
* previous 'process_get_query_records' function.
*/
process_update_query_records: function(query, records) {
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
+ // XXX XXX XXX XXX
// First issue: we request everything, and not only what we modify, so will will have to ignore some fields
var query_uuid = query.query_uuid;
var query_ext = manifold.query_store.find_analyzed_query_ext(query_uuid);
// Let's iterate over the object properties
for (var field in record) {
- switch (this.get_type(record[field])) {
+ var result_value = record[field];
+ switch (this.get_type(result_value)) {
case TYPE_VALUE:
// Did we ask for a change ?
var update_value = update_query[field];
// We assume it won't have changed
continue;
- var result_value = record[field];
if (!result_value)
throw "Internal error";
case TYPE_LIST_OF_VALUES:
// Same as list of records, but we don't have to extract keys
- var result_keys = record[field]
// The rest of exactly the same (XXX factorize)
var update_keys = update_query_orig.params[field];
$.each(added_keys, function(i, key) {
- if ($.inArray(key, result_keys) == -1) {
+ if ($.inArray(key, result_value) == -1) {
data = {
request: FIELD_REQUEST_ADD,
key : field,
key = key[0];
/* XXX should be modified for multiple keys */
- var result_keys = $.map(record[field], function(x) { return x[key]; });
+ var result_keys = $.map(record[field], function(x) { return manifold.record_get_value(x, key); });
var update_keys = update_query_orig.params[field];
var query_keys = update_query.params[field];
var result=data.value;
if (result) {
/* Eventually update the content of related queries (update, etc) */
+ manifold.make_records(query.object, result);
this.process_query_records(query, result);
/* Publish results: disabled here, done in the previous call */
query = query_ext.query;
switch(event_type) {
+
case FIELD_STATE_CHANGED:
// value is an object (request, key, value, status)
// update is only possible is the query is not pending, etc
case SET_ADD:
case SET_REMOVED:
-
+
+ /* An object has been added to / removed from a set : its
+ * status become pending or reset to the original state. We
+ * update the record status in the analyzed queries.
+ *
+ * XXX Shall we update something in the main_query ?
+ */
+ var prev_state, new_state;
+
+ prev_state = manifold.query_store.get_record_state(query_uuid, value, STATE_SET);
+ if (prev_state === null)
+ prev_state = STATE_SET_OUT;
+
+ if (event_type == SET_ADD) {
+ switch (prev_state) {
+ case STATE_SET_OUT:
+ case STATE_SET_OUT_SUCCESS:
+ case STATE_SET_IN_FAILURE:
+ new_state = STATE_SET_IN_PENDING;
+ break;
+
+ case STATE_SET_OUT_PENDING:
+ new_state = STATE_SET_IN;
+ break;
+
+ case STATE_SET_IN:
+ case STATE_SET_IN_PENDING:
+ case STATE_SET_IN_SUCCESS:
+ case STATE_SET_OUT_FAILURE:
+ console.log("Inconsistent state: already in");
+ return;
+ }
+ } else { // SET_REMOVE
+ switch (prev_state) {
+ case STATE_SET_IN:
+ case STATE_SET_IN_SUCCESS:
+ case STATE_SET_OUT_FAILURE:
+ new_state = STATE_SET_OUT_PENDING;
+ break;
+
+ case STATE_SET_IN_PENDING:
+ new_state = STATE_SET_OUT;
+ break;
+
+ case STATE_SET_OUT:
+ case STATE_SET_OUT_PENDING:
+ case STATE_SET_OUT_SUCCESS:
+ case STATE_SET_IN_FAILURE:
+ console.log("Inconsistent state: already out");
+ return;
+ }
+ }
+
+
+ var resource_key = value;
+
+ if (event_type == SET_ADD)
+ manifold.query_store.add_record(query_uuid, resource_key, new_state);
+ else
+ manifold.query_store.remove_record(query_uuid, resource_key, new_state);
+
+ var record = manifold.query_store.get_record(query_uuid, resource_key);
+
+ /* CONSTRAINTS */
+
+ // CONSTRAINT_RESERVABLE_LEASE
+ //
+ // +) If a reservable node is added to the slice, then it should have a corresponding lease
+ var is_reservable = (record.exclusive == true);
+ if (is_reservable) {
+ var warnings = manifold.query_store.get_record_state(query_uuid, resource_key, STATE_WARNINGS);
+
+ if (event_type == SET_ADD) {
+ // We should have a lease_query associated
+ var lease_query = query_ext.parent_query_ext.query.subqueries['lease'];
+ var lease_query_ext = manifold.query_store.find_analyzed_query_ext(lease_query.query_uuid);
+ // Do we have lease records with this resource
+ var lease_records = $.grep(lease_query_ext.records, function(lease_key, lease) {
+ return lease['resource'] == value;
+ });
+ if (lease_records.length == 0) {
+ // Sets a warning
+ // XXX Need for a better function to manage warnings
+ var warn = "No lease defined for this reservable resource.";
+ warnings[CONSTRAINT_RESERVABLE_LEASE] = warn;
+ } else {
+ // Lease are defined, delete the warning in case it was set previously
+ delete warnings[CONSTRAINT_RESERVABLE_LEASE];
+ }
+ } else {
+ // Remove warnings attached to this resource
+ delete warnings[CONSTRAINT_RESERVABLE_LEASE];
+ }
+
+ manifold.query_store.set_record_state(query_uuid, resource_key, STATE_WARNINGS, warnings);
+
+ }
+ // Signal the change to plugins (even if the constraint does not apply, so that the plugin can display a checkmark)
+ data = {
+ request: null,
+ key : null,
+ value : resource_key,
+ status: STATE_WARNINGS
+ };
+ manifold.raise_record_event(query_uuid, FIELD_STATE_CHANGED, data);
+
+ // -) When a lease is added, it might remove the warning associated to a reservable node
+
+ // If a NITOS node is reserved, then at least a NITOS channel should be reserved
+ // - When a NITOS channel is added, it might remove a warning associated to all NITOS nodes
+
+ // If a NITOS channel is reserved, then at least a NITOS node should be reserved
+ // - When a NITOS node is added, it might remove a warning associated to all NITOS channels
+
+ // A lease is present while the resource has been removed => Require warnings on nodes not in set !
+
+ /* END CONSTRAINTS */
+
// update is only possible is the query is not pending, etc
// CHECK status !
request: (event_type == SET_ADD) ? FIELD_REQUEST_ADD : FIELD_REQUEST_REMOVE,
key : path,
value : value,
- status: FIELD_REQUEST_PENDING,
+ status: STATE_SET, // XXX used to be FIELD_REQUEST_PENDING, and not new_state
};
this.raise_event(main_query.query_uuid, FIELD_STATE_CHANGED, data);
manifold.run_query(query_ext.main_query_ext.update_query_ext.query);
break;
+ /* FILTERS */
+
case FILTER_ADDED:
+ /* Update internal record state */
+ manifold.query_store.add_filter(query_uuid, value);
+
+ /* Propagate the message to plugins */
manifold.raise_query_event(query_uuid, event_type, value);
+
break;
+
case FILTER_REMOVED:
+ /* Update internal record state */
+ manifold.query_store.remove_filter(query_uuid, value);
+
+ /* Propagate the message to plugins */
manifold.raise_query_event(query_uuid, event_type, value);
+
break;
+
case FIELD_ADDED:
main_query = query_ext.main_query_ext.query;
main_update_query = query_ext.main_query_ext.update_query;
id_from_key: function(key_field, value) {
- return key_field + manifold.separator + this.escape_id(value).replace(/\\/g, '');
+ return key_field + manifold.separator + this.escape_id(value); //.replace(/\\/g, '');
},
// NOTE
--- /dev/null
+from unfold.plugin import Plugin
+from plugins.queryupdater import QueryUpdaterPlugin
+
+class ApplyPlugin(Plugin):
+
+ def __init__ (self, query=None, **settings):
+ Plugin.__init__ (self, **settings)
+ self.query = query
+
+ def template_file (self):
+ return "apply.html"
+
+ def template_env(self, request):
+ query_updater = QueryUpdaterPlugin(
+ page = self.page,
+ title = 'Pending operations',
+ query = self.query,
+ togglable = False,
+ # start turned off, it will open up itself when stuff comes in
+ toggled = True,
+ domid = 'pending',
+ outline_complete = True,
+ username = request.user, # XXX ???
+ )
+
+ env = Plugin.template_env(self, request)
+ env.update({'query_updater': query_updater.render(request)})
+ return env
+
+ def requirements (self):
+ reqs = {
+ 'js_files' : [
+ 'js/apply.js'
+ ],
+ 'css_files' : [
+ 'css/apply.css'
+ ],
+ }
+ return reqs
+
+ def json_settings_list (self):
+ # query_uuid will pass self.query results to the javascript
+ # and will be available as "record" in :
+ # on_new_record: function(record)
+ return ['plugin_uuid', 'domid', 'query_uuid']
+
+ def export_json_settings (self):
+ return True
--- /dev/null
+.modal-dialog-large {
+ width: 800px;
+}
--- /dev/null
+/**
+ * TestbedsPlugin: List of testbeds plugin
+ * Version: 0.1
+ * Description: TODO -> generalize to a list of possible filters
+ * This file is part of the Manifold project
+ * Requires: js/plugin.js
+ * URL: http://www.myslice.info
+ * Author: Loïc Baron <loic.baron@lip6.fr>
+ * Copyright: Copyright 2012-2013 UPMC Sorbonne Universités
+ * License: GPLv3
+ */
+
+(function($){
+
+ var ApplyPlugin = Plugin.extend({
+
+ /**
+ * @brief Plugin constructor
+ * @param options : an associative array of setting values
+ * @param element :
+ * @return : a jQuery collection of objects on which the plugin is
+ * applied, which allows to maintain chainability of calls
+ */
+ init: function(options, element)
+ {
+ // Call the parent constructor, see FAQ when forgotten
+ this._super(options, element);
+ },
+
+
+ })
+
+ /* Plugin registration */
+ $.plugin('ApplyPlugin', ApplyPlugin);
+
+})(jQuery);
--- /dev/null
+<div id={{ domid }}>
+ <!-- Modal - columns selector -->
+ <div class="modal fade" id="{{domid}}__apply" tabindex="-1" role="dialog" aria-labelledby="{{domid}}__apply__label" aria-hidden="true">
+ <div class="modal-dialog modal-dialog-large">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title" id="{{domid}}__apply__label">Columns selector</h4>
+ </div>
+ <div class="modal-body">
+ {{query_updater}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+
+ <!-- Button toolbar -->
+ <button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#{{domid}}__apply">Apply</button>
+ <button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#{{domid}}__cancel">Cancel</button>
+</div>
--- /dev/null
+from unfold.plugin import Plugin
+
+class FilterStatusPlugin(Plugin):
+
+ def __init__ (self, query=None, **settings):
+ Plugin.__init__ (self, **settings)
+ self.query = query
+
+ def template_file (self):
+ return "filter_status.html"
+
+ def requirements (self):
+ reqs = {
+ 'js_files' : [
+ 'js/filter_status.js'
+ ],
+# 'css_files': [
+# 'css/myplugin.css',
+# ]
+ }
+ return reqs
+
+ def json_settings_list (self):
+ # query_uuid will pass self.query results to the javascript
+ # and will be available as "record" in :
+ # on_new_record: function(record)
+ return ['plugin_uuid', 'domid', 'query_uuid']
+
+ def export_json_settings (self):
+ return True
--- /dev/null
+/**
+ * TestbedsPlugin: List of testbeds plugin
+ * Version: 0.1
+ * Description: TODO -> generalize to a list of possible filters
+ * This file is part of the Manifold project
+ * Requires: js/plugin.js
+ * URL: http://www.myslice.info
+ * Author: Loïc Baron <loic.baron@lip6.fr>
+ * Copyright: Copyright 2012-2013 UPMC Sorbonne Universités
+ * License: GPLv3
+ */
+
+(function($){
+
+ var FilterStatusPlugin = Plugin.extend({
+
+ /**
+ * @brief Plugin constructor
+ * @param options : an associative array of setting values
+ * @param element :
+ * @return : a jQuery collection of objects on which the plugin is
+ * applied, which allows to maintain chainability of calls
+ */
+ init: function(options, element)
+ {
+ // Call the parent constructor, see FAQ when forgotten
+ this._super(options, element);
+
+ /* Setup query and record handlers */
+ this.listen_query(options.query_uuid);
+
+ /* Setup click handlers */
+ this.elts('list-group-item').click({'instance': this}, this._on_click);
+
+ this.prev_filter_status = null;
+ },
+
+ /**************************************************************************
+ * GUI MANAGEMENT *
+ **************************************************************************/
+
+ select_tab: function(tab)
+ {
+ this.elts('list-group-item').removeClass('active');
+ this.elmt(tab).addClass('active');
+ },
+
+ /**************************************************************************
+ * EVENT HANDLERS *
+ **************************************************************************/
+
+ // These functions are here to react on external filters, which we don't
+ // use at the moment
+
+ on_filter_added: function(filter) {
+ // XXX
+ },
+
+ on_filter_removed: function(filter) {
+ // XXX
+ },
+
+ /**************************************************************************
+ * PRIVATE METHODS *
+ **************************************************************************/
+
+ /**
+ * @brief : Click event handler
+ */
+ _on_click: function(e)
+ {
+ var filter_status;
+ var filter;
+
+ // A pointer to the plugin instance, since 'this' is overriden here
+ self = e.data.instance;
+
+
+ // Select the tab...
+ filter_status = this.dataset['status'];
+ self.select_tab(filter_status);
+
+ // ... and communicate the appropriate filters to the manager
+ // NOTE: we use the manifold namespace for internal filters
+ if (self.prev_filter_status)
+ manifold.raise_event(self.options.query_uuid, FILTER_REMOVED, self.prev_filter_status);
+
+ // XXX The datatables will be refreshed twice !
+ if (filter_status != 'all') {
+ // No filter for 'all'
+ var filter = ['manifold:status', '==', filter_status];
+ manifold.raise_event(self.options.query_uuid, FILTER_ADDED, filter);
+ }
+
+ self.prev_filter_status = filter_status;
+ },
+
+ });
+
+ /* Plugin registration */
+ $.plugin('FilterStatusPlugin', FilterStatusPlugin);
+
+})(jQuery);
--- /dev/null
+<div id={{ domid }}>
+<span class="list-group-item-heading">Filter on status:</span>
+<a href="#" class="list-group-item sl-platform active" style='display: inline-block !important;' id="{{ domid }}__all" data-status="all"><p class="list-group-item-heading">All</p></a>
+<a href="#" class="list-group-item sl-platform" style='display: inline-block !important;' id="{{ domid }}__reserved" data-status="reserved"><p class="list-group-item-heading">Reserved</p></a>
+<a href="#" class="list-group-item sl-platform" style='display: inline-block !important;' id="{{ domid }}__unconfigured" data-status="unconfigured"><p class="list-group-item-heading">Unconfigured</p></a>
+<a href="#" class="list-group-item sl-platform" style='display: inline-block !important;' id="{{ domid }}__pending" data-status="pending"><p class="list-group-item-heading">Pending</p></a>
+</div>
.gtop{}
.gsubcell{float:left;}
rect{fill:none;}
-.tooltip{text-align:left;font-size:12px;}
+.Btooltip{text-align:left;font-size:12px;}
.tooltip .top-tip{padding-bottom:2px;border-bottom:1px solid #fff}
.tooltip .bottom-tip{padding-top:2px;}
.legends{
# hopefully temporary, when/if datatables supports sPaginationType=bootstrap3
# for now we use full_numbers, with our own ad hoc css
#"css/dataTables.full_numbers.css",
- #"css/querytable.css" ,
+ "css/querytable.css" ,
],
}
return reqs
-/* the bottom of the datatable needs more space */
-div.querytable-spacer { padding: 8px 4px 15px 4px; }
-
-div.QueryTable table.dataTable th {
- font: bold 12px/22px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
- color: #4f6b72;
- border-right: 1px solid #C1DAD7;
- border-bottom: 1px solid #C1DAD7;
- border-top: 1px solid #C1DAD7;
- letter-spacing: 1px;
- text-transform: uppercase;
- text-align: left;
- padding: 8px 12px 4px 20px;
- vertical-align:middle;
-/* background: #CAE8EA url(../img/tablesort-header.jpg) no-repeat; */
-}
-
-div.QueryTable table.dataTable th.checkbox {
-}
-
-div.QueryTable table.dataTable td, div.QueryTable table.dataTable textarea, div.QueryTable table.dataTable input [type="text"] {
- font: normal 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
- border-right: 1px solid #C1DAD7;
- border-bottom: 1px solid #C1DAD7;
-}
-div.QueryTable table.dataTable td {
- padding: 4px 8px 4px 8px;
- /* this applies on even rows only, odd ones have a setting in bootstrap of rbg 249.249.249 */
- background-color: #f4f4f4;
-}
-div.QueryTable table.dataTable td a {
- font-weight:normal;
-}
-/* these come from bootstrap */
-div.QueryTable div.dataTables_info {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-}
-
-/* one could think or using repeat-x here but that's not working because of the arrows
- * we might need to make these wider some day
- * and/or to add background-color: #caebea
- * which would look less conspicuous in case of overflow
-*/
-
-div.QueryTable table.dataTable thead .sorting { background: url('../img/tablesort-header-sortable.png') no-repeat; }
-div.QueryTable table.dataTable thead .sorting_asc { background: url('../img/tablesort-header-up.png') no-repeat; }
-div.QueryTable table.dataTable thead .sorting_desc { background: url('../img/tablesort-header-down.png') no-repeat; }
-/* this icons set does not have that exact equivalent - using an approximation for now */
-div.QueryTable table.dataTable thead .sorting_asc_disabled { background: url('../img/tablesort-header.png') repeat-x; }
-div.QueryTable table.dataTable thead .sorting_desc_disabled { background: url('../img/tablesort-header.png') repeat-x; }
-
-/* the footers are not active */
-div.QueryTable table.dataTable tfoot {
- background: url('../img/tablesort-header.png') repeat-x;
- background-color: #caebea;
-}
-/* and when sorting is turned off it's useful to set this on header too */
-div.QueryTable table.dataTable thead {
- background: url('../img/tablesort-header.png') repeat-x;
- background-color: #caebea;
+
+div .added {
+ background-color: #FFFF99;
+}
+
+div .removed {
+ background-color: #E8E8E8;
+}
+
+.sidebar-nav {
+ padding: 9px 0;
+}
+
+.dropdown-menu .sub-menu {
+ left: 100%;
+ top: 0;
+ position: absolute;
+ visibility: hidden;
+ margin-top: -1px;
+}
+
+.dropdown-menu li:hover .sub-menu {
+ visibility: visible;
+}
+
+.dropdown:hover .dropdown-menu {
+ display: block;
+}
+
+.nav-tabs .dropdown-menu, .nav-pills .dropdown-menu, .navbar .dropdown-menu {
+ margin-top: 0;
+}
+
+.navbar .sub-menu:before {
+ border-bottom: 7px solid transparent;
+ border-left: none;
+ border-right: 7px solid rgba(0, 0, 0, 0.2);
+ border-top: 7px solid transparent;
+ left: -7px;
+ top: 10px;
+}
+.navbar .sub-menu:after {
+ border-top: 6px solid transparent;
+ border-left: none;
+ border-right: 6px solid #fff;
+ border-bottom: 6px solid transparent;
+ left: 10px;
+ top: 11px;
+ left: -6px;
+}
+
+.dropdown-menu-right {
+ left: auto; /* Reset the default from `.dropdown-menu` */
+ right: 0;
+}
+
+.nav > li > a.nopadding {
+ padding: 0 0;
}
* License: GPLv3
*/
+BGCOLOR_RESET = 0;
+BGCOLOR_ADDED = 1;
+BGCOLOR_REMOVED = 2;
+
(function($){
var debug=false;
/* Setup query and record handlers */
this.listen_query(options.query_uuid);
- this.listen_query(options.query_all_uuid, 'all');
+ //this.listen_query(options.query_all_uuid, 'all');
/* GUI setup and event binding */
this.initialize_table();
}
/* fill in stuff depending on the column name */
- for (var j = 1; j < nb_col; j++) {
+ for (var j = 1; j < nb_col - 1; j++) { // nb_col includes status
if (typeof colnames[j] == 'undefined') {
line.push('...');
} else if (colnames[j] == 'hostname') {
line.push('');
}
}
-
-
+ line.push('<span id="' + this.id_from_key('status', record[this.init_key]) + '"></span>'); // STATUS
// adding an array in one call is *much* more efficient
// this.table.fnAddData(line);
- this.buffered_lines.push(line);
+ return line;
},
clear_table: function()
// this is used at init-time, at which point only init_key can make sense
// (because the argument record, if it comes from query, might not have canonical_key set
set_checkbox_from_record: function (record, checked) {
- if (checked === undefined) checked = true;
+ if (checked === undefined) checked = true;
var init_id = record[this.init_key];
- if (debug) messages.debug("querytable.set_checkbox_from_record, init_id="+init_id);
+ this.set_checkbox_from_record_key(init_id, checked);
+ },
+
+ set_checkbox_from_record_key: function (record_key, checked) {
+ if (checked === undefined) checked = true;
+ if (debug) messages.debug("querytable.set_checkbox_from_record, record_key="+record_key);
// using table.$ to search inside elements that are not visible
- var element = this.table.$('[init_id="'+init_id+'"]');
+ var element = this.table.$('[init_id="'+record_key+'"]');
element.attr('checked',checked);
},
element.attr('checked',checked);
},
+ /**
+ * Arguments
+ *
+ * key_value: the key from which we deduce the id
+ * request: STATUS_OKAY, etc.
+ * content: some HTML content
+ */
+ change_status: function(key_value, warnings)
+ {
+ var msg;
+
+ if ($.isEmptyObject(warnings)) {
+ var state = manifold.query_store.get_record_state(this.options.query_uuid, key_value, STATE_SET);
+ switch(state) {
+ case STATE_SET_IN:
+ case STATE_SET_IN_SUCCESS:
+ case STATE_SET_OUT_FAILURE:
+ case STATE_SET_IN_PENDING:
+ // Checkmark sign if no warning for an object in the set
+ msg = '✓';
+ break;
+ default:
+ // Nothing is the object is not in the set
+ msg = '';
+ break;
+ }
+ } else {
+ msg = '<ul class="nav nav-pills">';
+ msg += '<li class="dropdown">'
+ msg += '<a href="#" data-toggle="dropdown" class="dropdown-toggle nopadding"><b>⚠</b></a>';
+ msg += ' <ul class="dropdown-menu dropdown-menu-right" id="menu1">';
+ $.each(warnings, function(i,warning) {
+ msg += '<li><a href="#">' + warning + '</a></li>';
+ });
+ msg += ' </ul>';
+ msg += '</li>';
+ msg += '</ul>';
+ }
+
+ $(document.getElementById(this.id_from_key('status', key_value))).html(msg);
+ $('[data-toggle="tooltip"]').tooltip({'placement': 'bottom'});
+
+ },
+
+ set_bgcolor: function(key_value, class_name)
+ {
+ var elt = $(document.getElementById(this.id_from_key(this.canonical_key, key_value)))
+ if (class_name == BGCOLOR_RESET)
+ elt.removeClass('added removed');
+ else
+ elt.addClass((class_name == BGCOLOR_ADDED ? 'added' : 'removed'));
+ },
+
+ do_filter: function()
+ {
+ // Let's clear the table and only add lines that are visible
+ var self = this;
+ this.clear_table();
+
+ // XXX Here we have lost checkboxes
+ // set checkbox from record.
+ // only the current plugin known that we have an element in a set
+
+ lines = Array();
+ var record_keys = [];
+ manifold.query_store.iter_visible_records(this.options.query_uuid, function (record_key, record) {
+ lines.push(self.new_record(record));
+ record_keys.push(record_key);
+ });
+ this.table.fnAddData(lines);
+ $.each(record_keys, function(i, record_key) {
+ var state = manifold.query_store.get_record_state(self.options.query_uuid, record_key, STATE_SET);
+ var warnings = manifold.query_store.get_record_state(self.options.query_uuid, record_key, STATE_WARNINGS);
+ switch(state) {
+ // XXX The row and checkbox still does not exists !!!!
+ case STATE_SET_IN:
+ case STATE_SET_IN_SUCCESS:
+ case STATE_SET_OUT_FAILURE:
+ self.set_checkbox_from_record_key(record_key, true);
+ break;
+ case STATE_SET_OUT:
+ case STATE_SET_OUT_SUCCESS:
+ case STATE_SET_IN_FAILURE:
+ //self.set_checkbox_from_record_key(record_key, false);
+ break;
+ case STATE_SET_IN_PENDING:
+ self.set_checkbox_from_record_key(record_key, true);
+ self.set_bgcolor(record_key, BGCOLOR_ADDED);
+ break;
+ case STATE_SET_OUT_PENDING:
+ //self.set_checkbox_from_record_key(record_key, false);
+ self.set_bgcolor(record_key, BGCOLOR_REMOVED);
+ break;
+ }
+ self.change_status(record_key, warnings); // XXX will retrieve status again
+ });
+ },
+
/*************************** QUERY HANDLER ****************************/
on_filter_added: function(filter)
{
+ this.do_filter();
+
+ /*
this.filters.push(filter);
this.redraw_table();
+ */
},
on_filter_removed: function(filter)
{
+ this.do_filter();
+ /*
// Remove corresponding filters
this.filters = $.grep(this.filters, function(x) {
return x == filter;
});
this.redraw_table();
+ */
},
on_filter_clear: function()
{
- // XXX
- this.redraw_table();
+ this.do_filter();
},
on_field_added: function(field)
on_all_filter_added: function(filter)
{
- // XXX
- this.redraw_table();
+ this.do_filter();
},
on_all_filter_removed: function(filter)
{
- // XXX
- this.redraw_table();
+ this.do_filter();
},
on_all_filter_clear: function()
{
- // XXX
- this.redraw_table();
+ this.do_filter();
},
on_all_field_added: function(field)
on_query_done: function()
{
+ this.do_filter();
+/*
this.received_query = true;
// unspin once we have received both
if (this.received_all_query && this.received_query) this.unspin();
+*/
},
on_field_state_changed: function(data)
{
- switch(data.request) {
- case FIELD_REQUEST_ADD:
- case FIELD_REQUEST_ADD_RESET:
- this.set_checkbox_from_data(data.value, true);
- break;
- case FIELD_REQUEST_REMOVE:
- case FIELD_REQUEST_REMOVE_RESET:
- this.set_checkbox_from_data(data.value, false);
- break;
- default:
+ var state = manifold.query_store.get_record_state(this.options.query_uuid, data.value, data.status);
+ switch(data.status) {
+ case STATE_SET:
+ switch(state) {
+ case STATE_SET_IN:
+ case STATE_SET_IN_SUCCESS:
+ case STATE_SET_OUT_FAILURE:
+ this.set_checkbox_from_data(data.value, true);
+ this.set_bgcolor(data.value, BGCOLOR_RESET);
+ break;
+ case STATE_SET_OUT:
+ case STATE_SET_OUT_SUCCESS:
+ case STATE_SET_IN_FAILURE:
+ this.set_checkbox_from_data(data.value, false);
+ this.set_bgcolor(data.value, BGCOLOR_RESET);
+ break;
+ case STATE_SET_IN_PENDING:
+ this.set_checkbox_from_data(data.value, true);
+ this.set_bgcolor(data.value, BGCOLOR_ADDED);
+ break;
+ case STATE_SET_OUT_PENDING:
+ this.set_checkbox_from_data(data.value, false);
+ this.set_bgcolor(data.value, BGCOLOR_REMOVED);
+ break;
+ }
break;
- }
- },
- /* XXX TODO: make this generic a plugin has to subscribe to a set of Queries to avoid duplicated code ! */
- // all
- on_all_field_state_changed: function(data)
- {
- switch(data.request) {
- case FIELD_REQUEST_ADD:
- case FIELD_REQUEST_ADD_RESET:
- this.set_checkbox_from_data(data.value, true);
- break;
- case FIELD_REQUEST_REMOVE:
- case FIELD_REQUEST_REMOVE_RESET:
- this.set_checkbox_from_data(data.value, false);
- break;
- default:
+ case STATE_WARNINGS:
+ this.change_status(data.value, state);
break;
}
},
- on_all_new_record: function(record)
- {
- this.new_record(record);
- },
-
- on_all_clear_records: function()
- {
- this.clear_table();
-
- },
-
- on_all_query_in_progress: function()
- {
- // XXX parent
- this.spin();
- }, // on_all_query_in_progress
-
- on_all_query_done: function()
- {
- if (debug) messages.debug("1-shot initializing dataTables content with " + this.buffered_lines.length + " lines");
- this.table.fnAddData (this.buffered_lines);
- this.buffered_lines=[];
-
- var self = this;
- // if we've already received the slice query, we have not been able to set
- // checkboxes on the fly at that time (dom not yet created)
- $.each(this.buffered_records_to_check, function(i, record) {
- if (debug) messages.debug ("querytable delayed turning on checkbox " + i + " record= " + record);
- self.set_checkbox_from_record(record, true);
- });
- this.buffered_records_to_check = [];
-
- this.received_all_query = true;
- // unspin once we have received both
- if (this.received_all_query && this.received_query) this.unspin();
-
- }, // on_all_query_done
-
/************************** PRIVATE METHODS ***************************/
/**
{% if checkboxes %}<th class="checkbox">+/-</th>{% endif %}
{% for column in columns %} <th>{{ column }}</th> {% endfor %}
{% for column in hidden_columns %} <th>{{ column }}</th> {% endfor %}
+ <th class="checkbox">status</th>
</tr>
</thead>
<tbody>
{% if checkboxes %} <th>+/-</th> {% endif %}
{% for column in columns %} <th>{{ column }}</th> {% endfor %}
{% for column in hidden_columns %} <th>{{ column }}</th> {% endfor %}
+ <th class="checkbox">status</th>
</tr>
</tfoot>
</table>
from unfold.plugin import Plugin
-class QueryUpdater(Plugin):
+class QueryUpdaterPlugin(Plugin):
def __init__ (self, query=None, **settings):
Plugin.__init__ (self, **settings)
// Record state through the query cycle
- var QueryUpdater = Plugin.extend({
+ var QueryUpdaterPlugin = Plugin.extend({
init: function(options, element) {
this.classname="queryupdater";
// XXX check that the query is not disabled
self.spin();
- console.log("do_update");
// XXX check that the query is not disabled
manifold.raise_event(self.options.query_uuid, RUN_UPDATE);
return;
set_state: function(data)
{
- console.log("function set_state");
var action;
var msg;
var button = '';
// XXX we don't want to show automaticaly the pending when a checkbox is checked
//this.toggle_on();
-
- switch(data.request) {
- case FIELD_REQUEST_ADD_RESET:
- case FIELD_REQUEST_REMOVE_RESET:
+
+ switch (data.status) {
+ case STATE_SET_IN_PENDING:
+ action = 'ADD';
+ msg = 'PENDING';
+ button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
+ break;
+ case STATE_SET_OUT_PENDING:
+ action = 'REMOVE';
+ msg = 'PENDING';
+ button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
+ break;
+ case STATE_SET_IN:
+ case STATE_SET_OUT:
// find line and delete it
+ // XXX Naming is incorrect for badge-pending !!!!
+ // XXX What is this badge ?
row = this.find_row(data.value);
if (row)
this.table.fnDeleteRow(row.nTr);
$("#badge-pending").data('number', $("#badge-pending").data('number') - 1 );
$("#badge-pending").text($("#badge-pending").data('number'));
return;
- case FIELD_REQUEST_CHANGE:
- action = 'UPDATE';
- break;
- case FIELD_REQUEST_ADD:
- action = 'ADD';
- break;
- case FIELD_REQUEST_REMOVE:
- action = 'REMOVE';
- break;
- }
-
- switch(data.status) {
- case FIELD_REQUEST_PENDING:
- msg = 'PENDING';
- button = "<span class='glyphicon glyphicon-remove ResourceSelectedClose' id='" + data.key + "'/>";
- break;
- case FIELD_REQUEST_SUCCESS:
+ break;
+ case STATE_SET_IN_SUCCESS:
+ case STATE_SET_OUT_SUCCESS:
msg = 'SUCCESS';
break;
- case FIELD_REQUEST_FAILURE:
+ case STATE_SET_IN_FAILURE:
+ case STATE_SET_OUT_FAILURE:
msg = 'FAILURE';
break;
+ case STATE_CHANGE:
+ action = 'UPDATE';
+ break;
+
}
var status = msg + status;
-
-
// find line
// if no, create it, else replace it
// XXX it's not just about adding lines, but sometimes removing some
// XXX how do we handle status reset ?
- data.value = JSON.stringify(data.value);
+
+ // Jordan : I don't understand this. I added this test otherwise we have string = ""..."" double quoted twice.
+ if (typeof(data.value) !== "string")
+ data.value = JSON.stringify(data.value);
data.selected_resources = this.selected_resources;
row = this.find_row(data.value);
newline = [
on_new_record: function(record)
{
- console.log("query_updater on_new_record");
- console.log(record);
// if (not and update) {
on_query_done: function()
{
- console.log("on_query_done");
this.unspin();
},
// NOTE: record_key could be sufficient
on_added_record: function(record)
{
- console.log("on_added_record = ",record);
this.set_record_state(record, RECORD_STATE_ADDED);
// update pending number
},
on_removed_record: function(record_key)
{
- console.log("on_removed_record = ",record_key);
this.set_record_state(RECORD_STATE_REMOVED);
},
on_field_state_changed: function(result)
{
- console.log("on_field_state_changed");
- console.log(result);
if(result.request == FIELD_REQUEST_ADD){
this.selected_resources.push(result.value);
} else if(result.request == FIELD_REQUEST_REMOVE_RESET){
this.selected_resources.splice(i,1);
}
}
- console.log("Resources: " + self.selected_resources);
messages.debug(result)
- /* this.set_state(result.request, result.key, result.value, result.status); */
+
this.set_state(result);
},
});
- $.plugin('QueryUpdater', QueryUpdater);
+ $.plugin('QueryUpdaterPlugin', QueryUpdaterPlugin);
})(jQuery);
+++ /dev/null
-asd
\ No newline at end of file
};\r
});\r
\r
+myApp.filter('offset', function() {\r
+ return function(input, start) {\r
+ start = parseInt(start, 10);\r
+ return input.slice(start);\r
+ };\r
+});\r
+\r
// Create a private execution space for our controller. When\r
// executing this function expression, we're going to pass in\r
// the Angular reference and our application module.\r
// Set up the default scope value.\r
this.scope.errorMessage = null;\r
this.scope.name = "";\r
+\r
//Pagin\r
- $scope.totalPages = 4;\r
- $scope.curPage = 0;\r
- this.scope.pageSize = 25;\r
+ $scope.current_page = 1;\r
+ this.scope.items_per_page = 10;\r
+ $scope.from = 0; // JORDAN\r
\r
$scope.resources = new Array();\r
$scope.slots = SchedulerSlotsViewData;\r
+ $scope.granularity = DEFAULT_GRANULARITY; /* Will be setup */\r
//$scope.msg = "hello";\r
\r
angular.element(document).ready(function() {\r
//afterAngularRendered();\r
});\r
\r
+ // Jordan\r
+/*\r
+ $scope.redraw = function()\r
+ {\r
+\r
+ // Refresh slots\r
+ $scope.slots = [];\r
+ for (var i = $scope.from; i < $scope.from + SchedulerTotalVisibleCells; i++)\r
+ $scope.slots.push(SchedulerSlots[i]);\r
+\r
+\r
+ // Collect lease information. This could be made once if no refresh... \r
+ lease_by_resource = {};\r
+ manifold.query_store.iter_visible_records($scope.options.query_lease_uuid, function (record_key, record) {\r
+ lease_by_resource[record['resource']] = record;\r
+ // Need something to interrupt the loop\r
+ });\r
+\r
+ // Create resources\r
+ $scope.resources = [];\r
+ // current_page, items_per_page indicates which resources to show\r
+ manifold.query_store.iter_visible_records($scope.options.query_uuid, function (record_key, record) {\r
+ // copy not to modify original record\r
+ var resource = jQuery.extend(true, {}, record);\r
+ resource.leases = []; // a list of occupied timeslots\r
+\r
+ // How many timeslots ? SchedulerTotalVisibleCells\r
+ // SchedulerDateSelected\r
+ // from : to ??\r
+ // slot duration ?\r
+ for (i=0; i < SchedulerTotalVisibleCells; i++) {\r
+ resource.leases.push({\r
+ 'id': 'coucou',\r
+ 'status': 'free', // 'selected', 'reserved', 'maintenance'\r
+ });\r
+ }\r
+\r
+ // For each lease we need to mark slots appropriately\r
+ if (lease_by_resource[resource['urn']]) {\r
+ $.each(lease_by_resource[resource['urn']], function(i, lease) {\r
+ // $scope.from * GRANULARITY minutes since start\r
+ $scope.from * GRANULARITY\r
+ from_date = new Date(date.getTime() + ($scope.from * GRANULARITY) * 60000);\r
+ to_date = new Date(date.getTime() + (($scope.from + SchedulerTotalVisibleCells) * GRANULARITY) * 60000);\r
+ // start_time, end_time\r
+ });\r
+ }\r
+ \r
+ $scope.resources.push(resource);\r
+ $scope.$apply();\r
+ });\r
+ }\r
+*/\r
$scope.clearStuff = function() {\r
$scope.resources = new Array();\r
$scope.$apply();\r
}\r
\r
- $scope.initSchedulerResources = function (pageSize) {\r
+ // Called at initialization, after filtering, and after changing the date.\r
+ // this is like setpage(1) ???\r
+/*\r
+ $scope.initSchedulerResources = function (items_per_page) {\r
$scope.resources = new Array();\r
\r
- for (var k = 0; k < pageSize; k++) {\r
+ for (var k = 0; k < items_per_page; k++) {\r
$scope.resources.push(jQuery.extend(true, {}, SchedulerDataViewData[k]));\r
$scope.resources[k].leases = [];\r
}\r
- $scope.pageSize = pageSize;\r
- $scope.curPage = 0;\r
- $scope.totalPages = parseInt(Math.ceil(SchedulerDataViewData.length / $scope.pageSize));\r
+ $scope.items_per_page = items_per_page;\r
+ $scope.current_page = 0;\r
+ $scope.totalPages = parseInt(Math.ceil(SchedulerDataViewData.length / $scope.items_per_page));\r
$scope.initSlots(0, SchedulerTotalVisibleCells);\r
};\r
+*/\r
+\r
+ // Pagination\r
+\r
+ $scope.range = function() {\r
+ var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count();\r
+ var ret = [];\r
+ var start;\r
+\r
+ start = $scope.current_page;\r
+ if ( start > $scope.page_count()-range_size ) {\r
+ start = $scope.page_count()-range_size+1;\r
+ }\r
+\r
+ for (var i=start; i<start+range_size; i++) {\r
+ ret.push(i);\r
+ }\r
+ return ret;\r
+ };\r
+\r
+ $scope.prevPage = function() {\r
+ if ($scope.current_page > 1) {\r
+ $scope.current_page--;\r
+ }\r
+ };\r
\r
+ $scope.prevPageDisabled = function() {\r
+ return $scope.current_page === 1 ? "disabled" : "";\r
+ };\r
+ \r
+ $scope.page_count = function() {\r
+ return Math.ceil($scope.resources.length/$scope.items_per_page);\r
+ };\r
+ \r
+ $scope.nextPage = function() {\r
+ if ($scope.current_page < $scope.page_count()) {\r
+ $scope.current_page++;\r
+ }\r
+ };\r
+ \r
+ $scope.nextPageDisabled = function() {\r
+ return $scope.current_page === $scope.page_count() ? "disabled" : "";\r
+ }; \r
+\r
+ $scope.setPage = function(n) {\r
+ $scope.current_page = n;\r
+ };\r
+ // END pagination\r
+\r
+ // FILTER\r
+\r
+ $scope.filter_visible = function(resource)\r
+ {\r
+ return manifold.query_store.get_record_state($scope.options.query_uuid, resource['urn'], STATE_VISIBLE);\r
+ };\r
+\r
+ // SELECTION\r
+\r
+ $scope.select = function(index, model_lease, model_resource)\r
+ {\r
+ // XXX\r
+ // XXX Events won't work until we properly handle sets with composite keys\r
+ // XXX\r
+ console.log("Selected", index, model_lease, model_resource);\r
+\r
+ if (model_lease.status != 'free') {\r
+ console.log("Already selected slot");\r
+ return;\r
+ }\r
+ \r
+ var day_timestamp = SchedulerDateSelected.getTime() / 1000;\r
+ var start_time = day_timestamp + index * model_resource.granularity;\r
+ var end_time = day_timestamp + (index + 1) * model_resource.granularity;\r
+ var start_date = new Date(start_time * 1000);\r
+ var end_date = new Date(end_time * 1000);\r
+\r
+ var lease_key = manifold.metadata.get_key('lease');\r
+\r
+ // We search for leases in the cache we previously constructed\r
+ var resource_leases = $scope._leases_by_resource[model_resource.urn];\r
+ if (resource_leases) {\r
+ /* Search for leases before */\r
+ $.each(resource_leases, function(i, other) {\r
+ if (other.end_time != start_time)\r
+ return true; // ~ continue\r
+\r
+ /* The lease 'other' is just before, and there should not exist\r
+ * any other lease before it */\r
+ start_time = other.start_time;\r
+\r
+ other_key = {\r
+ resource: other.resource,\r
+ start_time: other.start_time,\r
+ end_time: other.end_time\r
+ }\r
+ // This is needed to create a hashable object\r
+ other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
+ other_key.equals = manifold.record_equals(lease_key);\r
+\r
+ manifold.raise_event($scope.options.query_lease_uuid, SET_REMOVED, other_key);\r
+ /* Remove from local cache also, unless we listen to events from outside */\r
+ $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
+ return false; // ~ break\r
+ });\r
+\r
+ /* Search for leases after */\r
+ $.each(resource_leases, function(i, other) {\r
+ if (other.start_time != end_time)\r
+ return true; // ~ continue\r
+\r
+ /* The lease 'other' is just after, and there should not exist\r
+ * any other lease after it */\r
+ end_time = other.end_time;\r
+ // XXX SET_ADD and SET_REMOVE should accept full objects\r
+ other_key = {\r
+ resource: other.resource,\r
+ start_time: other.start_time,\r
+ end_time: other.end_time\r
+ }\r
+ // This is needed to create a hashable object\r
+ other_key.hashCode = manifold.record_hashcode(lease_key.sort());\r
+ other_key.equals = manifold.record_equals(lease_key);\r
+\r
+ manifold.raise_event($scope.options.query_lease_uuid, SET_REMOVED, other_key);\r
+ /* Remove from local cache also, unless we listen to events from outside */\r
+ $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; });\r
+ return false; // ~ break\r
+ });\r
+ }\r
+\r
+ /* Create a new lease */\r
+ new_lease = {\r
+ resource: model_resource.urn,\r
+ start_time: start_time,\r
+ end_time: end_time,\r
+ };\r
+\r
+ // This is needed to create a hashable object\r
+ new_lease.hashCode = manifold.record_hashcode(lease_key.sort());\r
+ new_lease.equals = manifold.record_equals(lease_key);\r
+\r
+ manifold.raise_event($scope.options.query_lease_uuid, SET_ADD, new_lease);\r
+ /* Add to local cache also, unless we listen to events from outside */\r
+ if (!(model_resource.urn in $scope._leases_by_resource))\r
+ $scope._leases_by_resource[model_resource.urn] = [];\r
+ $scope._leases_by_resource[model_resource.urn].push(new_lease);\r
+\r
+ // XXX Shall we set it or wait for manifold event ?\r
+ model_lease.status = 'reserved'; // XXX pending\r
+\r
+ // DEBUG: display all leases and their status in the log\r
+ var leases = manifold.query_store.get_records($scope.options.query_lease_uuid);\r
+ console.log("--------------------");\r
+ $.each(leases, function(i, lease) {\r
+ var key = manifold.metadata.get_key('lease');\r
+ var lease_key = manifold.record_get_value(lease, key);\r
+ var state = manifold.query_store.get_record_state($scope.options.query_lease_uuid, lease_key, STATE_SET);\r
+ var state_str;\r
+ switch(state) {\r
+ case STATE_SET_IN:\r
+ state_str = 'STATE_SET_IN';\r
+ break;\r
+ case STATE_SET_OUT:\r
+ state_str = 'STATE_SET_OUT';\r
+ break;\r
+ case STATE_SET_IN_PENDING:\r
+ state_str = 'STATE_SET_IN_PENDING';\r
+ break;\r
+ case STATE_SET_OUT_PENDING:\r
+ state_str = 'STATE_SET_OUT_PENDING';\r
+ break;\r
+ case STATE_SET_IN_SUCCESS:\r
+ state_str = 'STATE_SET_IN_SUCCESS';\r
+ break;\r
+ case STATE_SET_OUT_SUCCESS:\r
+ state_str = 'STATE_SET_OUT_SUCCESS';\r
+ break;\r
+ case STATE_SET_IN_FAILURE:\r
+ state_str = 'STATE_SET_IN_FAILURE';\r
+ break;\r
+ case STATE_SET_OUT_FAILURE:\r
+ state_str = 'STATE_SET_OUT_FAILURE';\r
+ break;\r
+ }\r
+ console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str);\r
+ });\r
+ };\r
+ \r
+\r
+/*\r
$scope.setPage = function(page) {\r
- var tmpFrm = $scope.pageSize * page;\r
- var tmpTo = tmpFrm + $scope.pageSize;\r
+ var tmpFrm = $scope.items_per_page * page;\r
+ var tmpTo = tmpFrm + $scope.items_per_page;\r
tmpTo = SchedulerDataViewData.length < tmpTo ? SchedulerDataViewData.length : tmpTo;\r
- $scope.curPage = page;\r
+ $scope.current_page = page;\r
$scope.resources = [];\r
var j = 0;\r
for (var k = tmpFrm; k < tmpTo; k++) {\r
$('#tblSlider').slider('value', 0);\r
//init Slots\r
$scope.initSlots(0, SchedulerTotalVisibleCells);\r
- };\r
+ };*/\r
\r
+ // Typically we will only init visible slots\r
$scope.initSlots = function (from, to) {\r
+ return; // JORDAN !!!\r
+\r
//init\r
$scope.slots = [];\r
\r
//set\r
for (var i = from; i < to; i++) {\r
$scope.slots.push(SchedulerSlots[i]);\r
- resourceIndex = $scope.pageSize * $scope.curPage;\r
+ resourceIndex = $scope.items_per_page * $scope.current_page;\r
for (var j = 0; j < $scope.resources.length; j++) {\r
if (i == from) {\r
$scope.resources[j].leases = [];\r
$scope.$apply();\r
};\r
\r
- $scope.moveFrontSlot = function(from, to) {\r
- //$scope.slots.shift();\r
- //$scope.slots.push(SchedulerSlots[to]);\r
- //for (var j = 0; j < $scope.resources.length; j++) {\r
- // $scope.resources[j].leases.shift();\r
- // $scope.resources[j].leases.push(SchedulerData[j].leases[to]);\r
- //}\r
- //try {\r
- // $scope.$digest();\r
- // //$scope.$apply();\r
- //} catch (err) {\r
- // $scope.initSlots(from, to);\r
- //}\r
- $scope.initSlots(from, to);\r
- };\r
-\r
- $scope.moveBackSlot = function(from, to) {\r
- //$scope.$apply(function() {\r
- //try {\r
- // $scope.slots.pop();\r
- // $scope.slots.unshift(SchedulerSlots[from]);\r
- // for (var j = 0; j < $scope.resources.length; j++) {\r
- // $scope.resources[j].leases.pop();\r
- // $scope.resources[j].leases.unshift(SchedulerData[j].leases[from]);\r
- // }\r
- //} catch (err) {\r
- // alert("error");\r
- //}\r
-\r
- $scope.initSlots(from, to);\r
- //});\r
- };\r
-\r
+/*\r
$scope.getPageNumbers = function () {\r
var totalNumbersShowned = ($scope.totalPages > 10 ? 10 : $scope.totalPages + 1 );\r
var tmtNumDiv = totalNumbersShowned / 2;\r
if (totalNumbersShowned > 1) {\r
//set from - to\r
if ($scope.totalPages > totalNumbersShowned) {\r
- if ($scope.curPage <= tmtNumDiv) {\r
+ if ($scope.current_page <= tmtNumDiv) {\r
//nothing\r
- } else if ($scope.curPage >= $scope.totalPages - tmtNumDiv) {\r
+ } else if ($scope.current_page >= $scope.totalPages - tmtNumDiv) {\r
numTo = $scope.totalPages;\r
numFrom = numTo - totalNumbersShowned;\r
} else {\r
- numFrom = $scope.curPage - tmtNumDiv;\r
+ numFrom = $scope.current_page - tmtNumDiv;\r
numTo = numFrom + totalNumbersShowned;\r
}\r
}\r
}\r
return rtrnArr;\r
};\r
-\r
+*/\r
// Return this object reference.\r
return (this);\r
\r
app.controller("SchedulerCtrl", Controller);\r
\r
\r
-})(angular, myApp);
\ No newline at end of file
+})(angular, myApp);\r
return clonedArray;\r
}\r
\r
-function schedulerGetSlots(slotSpan) {\r
- if (slotSpan == 0) slotSpan = 10;\r
- var slots = [];\r
- var d = new Date(2014, 1, 1, 0, 0, 0, 0);\r
- var i = 0;\r
- while (d.getDate() == 1) {\r
- var tmpTime = schedulerPadStr(d.getHours()) + ':' + schedulerPadStr(d.getMinutes());\r
- slots.push({ id: i, time: tmpTime });\r
- d = schedulerAddMinutes(d, slotSpan);\r
- i++;\r
- }\r
- return slots;\r
-}\r
-\r
function schedulerGetLeases(slotSpan, granularity) {\r
granularity = granularity / 60;\r
if (slotSpan == 0) slotSpan = 10;\r
return new Date(date.getTime() + minutes * 60000);\r
}\r
\r
-function schedulerCompareOnDay(dateOne, dateTwo) {\r
+/**\r
+ * Compares two dates\r
+ *\r
+ * Returns:\r
+ * 0 if they are equal\r
+ * -1 if the first is less than the second\r
+ * 1 if the first is more than the second\r
+ */\r
+function schedulerCompareOnDay(dateOne, dateTwo)\r
+{\r
if (dateOne.getYear() == dateTwo.getYear() &&\r
dateOne.getMonth() == dateTwo.getMonth() &&\r
dateOne.getDate() == dateTwo.getDate()) {\r
+++ /dev/null
-////version 3\r
-var scheduler_table_dragStart_td = 0;\r
-var scheduler_table_dragStart_tr = 0;\r
-var scheduler_table_dragEnd_td = 0;\r
-var scheduler_table_dragEnd_tr = 0;\r
-//tmp gia ta loops & check gia to last state\r
-var tmp_scheduler_table_dragStart_td;\r
-var tmp_scheduler_table_dragStart_tr;\r
-var tmp_scheduler_table_dragEnd_td;\r
-var tmp_scheduler_table_dragEnd_tr;\r
-var schedulerTableIsDragging = false;\r
-// try stop \r
-var continueExecuting = false;\r
-var isExecuting = false;\r
-\r
-\r
-\r
-function rangeMouseDown(e) {\r
- if (SchedulerData) console.time("mouse:rangeMouseDown");\r
- if (schedulerIsRightClick(e)) {\r
- return false;\r
- } else {\r
- scheduler_table_dragStart_tr = $(this).parent().index();\r
- scheduler_table_dragStart_td = $(this).index() -1;\r
- scheduler_table_dragEnd_tr = scheduler_table_dragStart_tr;\r
- scheduler_table_dragEnd_td = scheduler_table_dragStart_td;\r
- //alert(scheduler_table_dragStart_tr);\r
- //var allCells = $("#tblReservation td");\r
- //dragStart = allCells.index($(this));\r
-\r
- if ( $(this).hasClass("free")){\r
- $(this).addClass("selected_tmp");\r
- $(this).siblings("td[data-groupid='" + $(this).data('groupid') + "']").addClass("selected_tmp");\r
- }\r
- schedulerTableIsDragging = true;\r
- //selectRange();\r
-\r
- if (typeof e.preventDefault != 'undefined') { e.preventDefault(); }\r
- document.documentElement.onselectstart = function () { return false; };\r
- }\r
- if (SchedulerData) console.timeEnd("mouse:rangeMouseDown");\r
-}\r
-\r
-function rangeMouseUp(e) {\r
- if (SchedulerData) console.time("mouse:rangeMouseUp");\r
- if (schedulerIsRightClick(e)) {\r
- return false;\r
- } else {\r
- //var allCells = $("#tblReservation td");\r
- //dragEnd = allCells.index($(this));\r
-\r
- scheduler_table_dragEnd_tr = $(this).parent().index();\r
- scheduler_table_dragEnd_td = $(this).index() -1;\r
-\r
- schedulerTableIsDragging = false;\r
- selectRange(false);\r
-\r
- document.documentElement.onselectstart = function () { return true; };\r
- }\r
- if (SchedulerData) console.timeEnd("mouse:rangeMouseUp");\r
-}\r
-\r
-function rangeMouseMove(e) {\r
- //if (SchedulerData) console.time("mouse:rangeMouseMove");\r
- if (schedulerTableIsDragging) {\r
- scheduler_table_dragEnd_tr = $(this).parent().attr('data-trindex');\r
- scheduler_table_dragEnd_td = $(this).attr('data-tdindex');\r
-\r
- //if (SchedulerData) this.SchedulerData('foo');\r
-\r
- if ((scheduler_table_dragEnd_tr != tmp_scheduler_table_dragEnd_tr) || (scheduler_table_dragEnd_td != tmp_scheduler_table_dragEnd_td)) {\r
- //console.log(scheduler_table_dragEnd_tr + " - " + tmp_scheduler_table_dragEnd_tr);\r
- //console.log(scheduler_table_dragEnd_td + " - " + tmp_scheduler_table_dragEnd_td);\r
- //selectRange(true);\r
- }\r
- }\r
- //if (SchedulerData) console.timeEnd("mouse:rangeMouseMove");\r
-}\r
-\r
-function selectRange(isTemp) {\r
- if (SchedulerData) console.time("mouse:---selectRange");\r
-\r
- if (!schedulerCtrlPressed)\r
- $("#" + schedulerTblId + " td.selected, #" + schedulerTblId + " td.selected_tmp").each(function() {\r
- $(this).removeClass('selected selected_tmp').addClass('free');\r
- $(this).siblings("td[data-groupid='" + $(this).data('groupid') + "']").removeClass('selected selected_tmp').addClass("free");\r
- schedulerFreeSlot($(this).data('slotid'), $(this).siblings('th').data('rowindex'), $(this).siblings('th').data('resourceindex'));\r
- });\r
-\r
- tmp_scheduler_table_dragStart_td = scheduler_table_dragStart_td;\r
- tmp_scheduler_table_dragStart_tr = scheduler_table_dragStart_tr;\r
- tmp_scheduler_table_dragEnd_td = scheduler_table_dragEnd_td;\r
- tmp_scheduler_table_dragEnd_tr = scheduler_table_dragEnd_tr;\r
-\r
- if (tmp_scheduler_table_dragStart_td > tmp_scheduler_table_dragEnd_td) {\r
- var tmp = tmp_scheduler_table_dragStart_td;\r
- tmp_scheduler_table_dragStart_td = tmp_scheduler_table_dragEnd_td;\r
- tmp_scheduler_table_dragEnd_td = tmp;\r
- }\r
-\r
- if (tmp_scheduler_table_dragStart_tr > tmp_scheduler_table_dragEnd_tr) {\r
- var tmp = tmp_scheduler_table_dragStart_tr;\r
- tmp_scheduler_table_dragStart_tr = tmp_scheduler_table_dragEnd_tr;\r
- tmp_scheduler_table_dragEnd_tr = tmp;\r
- }\r
- //var angularScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
- //alert("tmp_scheduler_table_dragStart_td:" + tmp_scheduler_table_dragStart_td + "\n tmp_scheduler_table_dragStart_tr:" + tmp_scheduler_table_dragStart_tr + "\n tmp_scheduler_table_dragEnd_td:" + tmp_scheduler_table_dragEnd_td + "\n tmp_scheduler_table_dragEnd_tr:" + tmp_scheduler_table_dragEnd_tr);\r
-\r
-\r
- for (var i = tmp_scheduler_table_dragStart_tr; i <= tmp_scheduler_table_dragEnd_tr; i++) {\r
- for (var j = tmp_scheduler_table_dragStart_td; j <= tmp_scheduler_table_dragEnd_td; j++) {\r
- //alert("i:" + i + "j:" + j);\r
- var cell = $('#' + schedulerTblId + ' tbody tr:eq(' + i + ') td:eq(' + j + ')');\r
- //$(cell)\r
- var curClass = $(cell).attr("class");\r
- curClass = curClass.replace('ng-scope','').trim();\r
- //alert(curClass);\r
- switch (curClass) {\r
- case "free_tmp":\r
- $(cell).removeClass('selected_tmp selected free_tmp free');\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
- if (isTemp){\r
- $(cell).addClass("free_tmp");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free");\r
- } else {\r
- schedulerFreeSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
- $(cell).addClass("free");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free");\r
- }\r
- break;\r
- case "free":\r
- $(cell).removeClass('selected_tmp selected free_tmp free');\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
- if (isTemp){\r
- $(cell).addClass("selected_tmp");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected_tmp");\r
- }else {\r
- schedulerSelectSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
- $(cell).addClass("selected");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected");\r
- }\r
- break;\r
- case "selected_tmp":\r
- $(cell).removeClass('selected_tmp selected free_tmp free');\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
- if (isTemp){\r
- $(cell).addClass("selected_tmp");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected_tmp");\r
- } else {\r
- schedulerSelectSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
- $(cell).addClass("selected");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("selected");\r
- }\r
- break;\r
- case "selected":\r
- $(cell).removeClass('selected_tmp selected free_tmp free');\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").removeClass("selected_tmp selected free_tmp free");\r
- if (isTemp){\r
- $(cell).addClass("free_tmp");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free_tmp");\r
- } else {\r
- schedulerFreeSlot($(cell).data('slotid'), $(cell).siblings('th').data('rowindex'), $(cell).siblings('th').data('resourceindex'));\r
- $(cell).addClass("free");\r
- $(cell).siblings("td[data-groupid='" + $(cell).data('groupid') + "']").addClass("free");\r
- }\r
- break;\r
- case "closed":\r
- //do nothing\r
- //alert("not allowed!");\r
- break;\r
- }\r
- }\r
- }\r
-\r
-\r
- /*if (dragEnd + 1 < dragStart) { // reverse select\r
- //alert(1);\r
- $("#tblReservation td:not([class='info'])").slice(dragEnd, dragStart + 1).addClass('selected');\r
- } else {\r
- alert(dragStart + "-" + dragEnd);\r
- $("#tblReservation td:not([class='info'])").slice(dragStart, dragEnd).addClass('selected');\r
- }*/\r
-\r
- if (SchedulerData) console.timeEnd("mouse:---selectRange");\r
-}\r
-\r
-function ClearTableSelection(){\r
- $('#' + schedulerTblId + ' .selected').addClass("free").removeClass("selected");\r
-}\r
-\r
-\r
#\r
*/\r
\r
+// XXX groupid = all slots those that go with a min granularity\r
+\r
/* some params */\r
var scheduler2;\r
var scheduler2Instance;\r
var schedulerCtrlPressed = false;\r
//table Id\r
var schedulerTblId = "scheduler-reservation-table";\r
-var schedulerTblFirstColWidth = 150;\r
-//Some Data\r
+var SCHEDULER_FIRST_COLWIDTH = 150;\r
+\r
+\r
+/* Number of scheduler slots per hour. Used to define granularity. Should be inferred from resources XXX */\r
var schedulerSlotsPerHour = 6;\r
+var RESOURCE_DEFAULT_GRANULARITY = 1800 /* s */; // should be computed automatically from resource information\r
+var DEFAULT_GRANULARITY = 1800 /* s */; // should be computed automatically from resource information. Test with 600\r
+var DEFAULT_PAGE_RANGE = 5;\r
+\r
var schedulerMaxRows = 12;\r
+\r
+/* All resources */\r
var SchedulerData = [];\r
+\r
+/* ??? */\r
var SchedulerSlots = [];\r
+\r
var SchedulerDateSelected = new Date();\r
+// Round to midnight\r
+SchedulerDateSelected.setHours(0,0,0,0);\r
+\r
+/* Filtered resources */\r
var SchedulerDataViewData = [];\r
+\r
var SchedulerSlotsViewData = [];\r
-var SchedulerTotalCells;\r
-var SchedulerTotalVisibleCells;\r
//Help Variables\r
var _schedulerCurrentCellPosition = 0;\r
-var _leasesDone = false;\r
-var _resourcesDone = false;\r
//Enable Debug\r
var schedulerDebug = true;\r
//tmp to delete\r
var tmpSchedulerLeases = [];\r
\r
+var SCHEDULER_COLWIDTH = 50;\r
+\r
(function($) {\r
scheduler2 = Plugin.extend({\r
\r
this.classname = "scheduler2";\r
// Call the parent constructor, see FAQ when forgotten\r
this._super(options, element);\r
+\r
+ var scope = this._get_scope()\r
+\r
+ // XXX not needed\r
scheduler2Instance = this;\r
+\r
// We need to remember the active filter for datatables filtering\r
+ // XXX not needed\r
this.filters = Array();\r
\r
+ // XXX BETTER !!!!\r
+ $(window).delegate('*', 'keypress', function (evt){\r
+ alert("erm");\r
+ });\r
\r
- SchedulerSlots = schedulerGetSlots(60 / schedulerSlotsPerHour);\r
- //selection from table \r
$(window).keydown(function(evt) {\r
if (evt.which == 17) { // ctrl\r
schedulerCtrlPressed = true;\r
schedulerCtrlPressed = false;\r
}\r
});\r
- $("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);\r
\r
- // Explain this will allow query events to be handled\r
- // What happens when we don't define some events ?\r
- // Some can be less efficient\r
+ // XXX naming\r
+ //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);\r
\r
- if (schedulerDebug) console.time("Listening_to_queries");\r
- /* Listening to queries */\r
+ this._resources_received = false;\r
+ this._leases_received = false;\r
+ \r
+ scope._leases_by_resource = {};\r
\r
- this.listen_query(options.query_uuid);\r
- //this.listen_query(options.query_all_uuid, 'all');\r
- this.listen_query(options.query_all_resources_uuid, 'all_resources');\r
- this.listen_query(options.query_lease_uuid, 'lease');\r
- this.listen_query(options.query_all_leases_uuid, 'all_leases');\r
- if (schedulerDebug) console.timeEnd("Listening_to_queries");\r
+ /* Listening to queries */\r
+ this.listen_query(options.query_uuid, 'resources');\r
+ this.listen_query(options.query_lease_uuid, 'leases');\r
+\r
+ /* Generate slots according to the default granularity. Should\r
+ * be updated when resources arrive. Should be the pgcd in fact XXX */\r
+ this._granularity = DEFAULT_GRANULARITY;\r
+ scope.granularity = this._granularity;\r
+ this._all_slots = this._generate_all_slots();\r
+\r
+ $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH);\r
+ //this get width might need fix depending on the template \r
+ var tblwidth = $('#scheduler-tab').parent().outerWidth();\r
+\r
+ /* Number of visible cells...*/\r
+ this._num_visible_cells = parseInt((tblwidth - SCHEDULER_FIRST_COLWIDTH) / SCHEDULER_COLWIDTH);\r
+ /* ...should be a multiple of the lcm of all encountered granularities. */\r
+ // XXX Should be updated everytime a new resource is added\r
+ this._lcm_colspan = this._lcm(this._granularity, RESOURCE_DEFAULT_GRANULARITY) / this._granularity;\r
+ this._num_visible_cells = this._num_visible_cells - this._num_visible_cells % this._lcm_colspan;\r
+ /* scope also needs this value */\r
+ scope.num_visible_cells = this._num_visible_cells;\r
+ scope.lcm_colspan = this._lcm_colspan;\r
+\r
+ scope.options = this.options;\r
+ scope.from = 0;\r
+\r
+ // A list of {id, time} dictionaries representing the slots for the given day\r
+ scope.slots = this._all_slots;\r
+ this.scope_resources_by_key = {};\r
+\r
+ this._initUI();\r
\r
},\r
\r
/* Handlers */\r
\r
- /* all_ev QUERY HANDLERS Start */\r
- on_all_ev_clear_records: function(data) {\r
- //alert('all_ev clear_records');\r
- },\r
- on_all_ev_query_in_progress: function(data) {\r
- // alert('all_ev query_in_progress');\r
- },\r
- on_all_ev_new_record: function(data) {\r
- //alert('all_ev new_record');\r
- },\r
- on_all_ev_query_done: function(data) {\r
- //alert('all_ev query_done');\r
+ _get_scope : function()\r
+ {\r
+ return angular.element(document.getElementById('SchedulerCtrl')).scope();\r
},\r
- //another plugin has modified something, that requires you to update your display. \r
- on_all_ev_field_state_changed: function(data) {\r
- //alert('all_ev query_done');\r
- },\r
- /* all_ev QUERY HANDLERS End */\r
- /* all_resources QUERY HANDLERS Start */\r
- on_all_resources_clear_records: function(data) {\r
- //data is empty on load\r
- },\r
- on_all_resources_query_in_progress: function(data) {\r
- //data is empty on load\r
+ \r
+ _scope_set_resources : function()\r
+ {\r
+ var self = this;\r
+ var scope = this._get_scope();\r
+\r
+ var records = manifold.query_store.get_records(this.options.query_uuid);\r
+\r
+ scope.resources = [];\r
+\r
+ $.each(records, function(i, record) {\r
+ if (!record.exclusive)\r
+ return true; // ~ continue\r
+\r
+ // copy not to modify original record\r
+ var resource = jQuery.extend(true, {}, record);\r
+\r
+ // Fix granularity\r
+ resource.granularity = typeof(resource.granularity) == "number" ? resource.granularity : RESOURCE_DEFAULT_GRANULARITY;\r
+ resource.leases = []; // a list of occupied timeslots\r
+\r
+ self.scope_resources_by_key[resource['urn']] = resource;\r
+ scope.resources.push(resource);\r
+ });\r
},\r
- on_all_resources_new_record: function(data) {\r
- //alert(data.toSource());\r
- if (data.exclusive == true) {\r
- var tmpGran = schedulerDebug && data.granularity == null ? 1800 : data.granularity;\r
- SchedulerData.push({\r
- id: data.urn,\r
- index: SchedulerData.length,\r
- name: data.hrn,\r
- granularity: tmpGran,\r
- leases: schedulerGetLeases(60 / schedulerSlotsPerHour, tmpGran),\r
- type: data.type,\r
- org_resource: data\r
- });\r
- /*if (schedulerDebug && SchedulerData[SchedulerData.length - 1].org_resource.network_hrn == 'omf') {\r
- SchedulerData[SchedulerData.length - 1].granularity = 1800;\r
- }*/\r
- }\r
- //alert(data.toSource());\r
+\r
+ _scope_clear_leases: function()\r
+ {\r
+ var self = this;\r
+ var scope = this._get_scope();\r
+\r
+ // Setup leases with a default free status...\r
+ $.each(this.scope_resources_by_key, function(resource_key, resource) {\r
+ resource.leases = [];\r
+ var colspan_lease = resource.granularity / self._granularity; //eg. 3600 / 1800 => 2 cells\r
+ for (i=0; i < self._all_slots.length / colspan_lease; i++) { // divide by granularity\r
+ resource.leases.push({\r
+ id: 'coucou',\r
+ status: 'free', // 'selected', 'reserved', 'maintenance' XXX pending ??\r
+ });\r
+ }\r
+ });\r
\r
},\r
- on_all_resources_query_done: function(data) {\r
- _resourcesDone = true;\r
- this._initScheduler();\r
+\r
+ _scope_set_leases: function()\r
+ {\r
+ var self = this;\r
+ var scope = this._get_scope();\r
+ \r
+ var leases = manifold.query_store.get_records(this.options.query_lease_uuid);\r
+ $.each(leases, function(i, lease) {\r
+\r
+ console.log("SET LEASES", new Date(lease.start_time* 1000));\r
+ console.log(" ", new Date(lease.end_time* 1000));\r
+ // XXX We should ensure leases are correctly merged, otherwise our algorithm won't work\r
+\r
+ // Populate leases by resource array: this will help us merging leases later\r
+ if (!(lease.resource in scope._leases_by_resource))\r
+ scope._leases_by_resource[lease.resource] = [];\r
+ scope._leases_by_resource[lease.resource].push(lease);\r
+\r
+ var resource = self.scope_resources_by_key[lease.resource];\r
+ var day_timestamp = SchedulerDateSelected.getTime() / 1000;\r
+\r
+ var id_start = (lease.start_time - day_timestamp) / resource.granularity;\r
+ if (id_start < 0) {\r
+ /* Some leases might be in the past */\r
+ id_start = 0;\r
+ }\r
+ \r
+ var id_end = (lease.end_time - day_timestamp) / resource.granularity - 1;\r
+ var colspan_lease = resource.granularity / self._granularity; //eg. 3600 / 1800 => 2 cells\r
+ if (id_end >= self._all_slots.length / colspan_lease) {\r
+ /* Limit the display to the current day */\r
+ id_end = self._all_slots.length / colspan_lease\r
+ }\r
+\r
+ for (i = id_start; i <= id_end; i++)\r
+ // the same slots might be affected multiple times.\r
+ // PENDING_IN + PENDING_OUT => IN \r
+ //\r
+ // RESERVED vs SELECTED !\r
+ //\r
+ // PENDING !!\r
+ resource.leases[i].status = 'selected'; \r
+ });\r
},\r
- //another plugin has modified something, that requires you to update your display. \r
- on_all_resources_field_state_changed: function(data) {\r
- //alert('all_resources query_done');\r
+\r
+ on_resources_query_done: function(data)\r
+ {\r
+ this._resources_received = true;\r
+ this._scope_set_resources();\r
+ this._scope_clear_leases();\r
+ if (this._leases_received)\r
+ this._scope_set_leases();\r
+ \r
+ this._get_scope().$apply();\r
},\r
- /* all_resources QUERY HANDLERS End */\r
- /* lease QUERY HANDLERS Start */\r
- on_lease_clear_records: function(data) { console.log('clear_records'); },\r
- on_lease_query_in_progress: function(data) { console.log('lease_query_in_progress'); },\r
- on_all_leases_new_record: function(data) {\r
- if (data.resource.indexOf("nitos") > -1) {\r
- tmpSchedulerLeases.push({\r
- id: schedulerGetSlotId(data.start_time, data.duration, data.granularity),\r
- end_id: schedulerGetSlotId(data.end_time, data.duration, data.granularity),\r
- slice: data.slice,\r
- status: 'reserved',\r
- resource: data.resource,\r
- network: data.network,\r
- start_time: new Date(data.start_time * 1000),\r
- start_time_unixtimestamp: data.start_time,\r
- end_time: new Date(data.end_time * 1000),\r
- end_time_unixtimestamp: data.end_time,\r
- lease_type: data.lease_type,\r
- granularity: data.granularity,\r
- duration: data.duration\r
- });\r
+\r
+ on_leases_query_done: function(data)\r
+ {\r
+ this._leases_received = true;\r
+ if (this._resources_received) {\r
+ this._scope_set_leases();\r
+ this._get_scope().$apply();\r
}\r
- //console.log(data.toSource()); console.log('lease_new_record');\r
},\r
- on_all_leases_query_done: function(data) {\r
- _leasesDone = true;\r
- this._initScheduler();\r
- // console.log('lease_query_done');\r
- },\r
- //another plugin has modified something, that requires you to update your display. \r
- on_lease_field_state_changed: function(data) { console.log('lease_field_state_changed'); },\r
- /* lease QUERY HANDLERS End */\r
\r
+ /* Filters on resources */\r
+ on_resources_filter_added: function(filter) { this._get_scope().$apply(); },\r
+ on_resources_filter_removed: function(filter) { this._get_scope().$apply(); },\r
+ on_resources_filter_clear: function() { this._get_scope().$apply(); },\r
\r
- // no prefix\r
- on_filter_added: function(filter) {\r
- this.filters.push(filter);\r
- this._SetFiletredResources(this.filters);\r
- //angular and UI\r
- var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
- if (SchedulerDataViewData.length == 0) {\r
- $("#plugin-scheduler").hide();\r
- $("#plugin-scheduler-empty").show();\r
- tmpScope.clearStuff();\r
- } else {\r
- $("#plugin-scheduler-empty").hide();\r
- $("#plugin-scheduler").show();\r
- tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
- }\r
- },\r
+ /* Filters on leases ? */\r
+ on_leases_filter_added: function(filter) { this._get_scope().$apply(); },\r
+ on_leases_filter_removed: function(filter) { this._get_scope().$apply(); },\r
+ on_leases_filter_clear: function() { this._get_scope().$apply(); },\r
\r
- on_filter_removed: function(filter) {\r
- // Remove corresponding filters\r
- this.filters = $.grep(this.filters, function(x) {\r
- return x == filter;\r
- });\r
- this._SetFiletredResources(this.filters);\r
- //angular and UI\r
- var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
- if (SchedulerDataViewData.length == 0) {\r
- $("#plugin-scheduler").hide();\r
- $("#plugin-scheduler-empty").show();\r
- tmpScope.clearStuff();\r
- } else {\r
- $("#plugin-scheduler-empty").hide();\r
- $("#plugin-scheduler").show();\r
- tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
- }\r
- },\r
+ /* INTERNAL FUNCTIONS */\r
\r
- on_filter_clear: function() {\r
- this.filters = [];\r
- this._SetFiletredResources(this.filters);\r
- //angular and UI\r
- var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
+/* XXX IN TEMPLATE XXX\r
if (SchedulerDataViewData.length == 0) {\r
$("#plugin-scheduler").hide();\r
$("#plugin-scheduler-empty").show();\r
} else {\r
$("#plugin-scheduler-empty").hide();\r
$("#plugin-scheduler").show();\r
+ // initSchedulerResources\r
tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
}\r
- },\r
-\r
- on_all_leases_filter_added: function(filter) {\r
- console.log("Filter on Leases added !");\r
- },\r
-\r
- // ... be sure to list all events here\r
-\r
- /* RECORD HANDLERS */\r
- on_all_new_record: function(record) {\r
- //alert('on_all_new_record');\r
- },\r
-\r
- debug: function(logTxt) {\r
- if (typeof window.console != 'undefined') {\r
- console.debug(logTxt);\r
- }\r
- },\r
+*/\r
\r
- /* INTERNAL FUNCTIONS */\r
- _initScheduler: function() {\r
- if (_resourcesDone && _leasesDone) {\r
- SchedulerDataViewData = SchedulerData;\r
- /* GUI setup and event binding */\r
- this._FixLeases();\r
- this._initUI();\r
- }\r
- },\r
+ /**\r
+ * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler.\r
+ */\r
+ _initUI: function() \r
+ {\r
+ var self = this;\r
\r
- _initUI: function() {\r
- //alert(1);\r
- if (schedulerDebug) console.time("_initUI");\r
- //init DatePicker Start\r
$("#DateToRes").datepicker({\r
dateFormat: "yy-mm-dd",\r
minDate: 0,\r
numberOfMonths: 3\r
}).change(function() {\r
- //Scheduler2.loadWithDate();\r
+ // the selected date\r
SchedulerDateSelected = $("#DateToRes").datepicker("getDate");\r
- if (SchedulerDateSelected != null && SchedulerDateSelected != '') {\r
- for (var i = 0; i < SchedulerData.length; i++) {\r
- SchedulerData[i].leases = schedulerGetLeases(60 / schedulerSlotsPerHour, SchedulerData[i].granularity);\r
- }\r
- scheduler2Instance._FixLeases();\r
- $('#tblSlider').slider('value', 0);\r
- var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
- tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-\r
- //console.log(SchedulerDateSelected);\r
- //console.log(SchedulerDateSelected.getTime()/1000);\r
- var tomorrow = new Date(SchedulerDateSelected);\r
- tomorrow.setDate(SchedulerDateSelected.getDate()+1);\r
- //console.log(tomorrow);\r
- //console.log(tomorrow.getTime()/1000);\r
- \r
- // Remove previous date interval\r
- manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_REMOVED, ['start_time', '>']);\r
- manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_REMOVED, ['start_time', '<']);\r
-\r
- // Add new date interval\r
- manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_ADDED, ['start_time', '>', SchedulerDateSelected.getTime()/1000]);\r
- manifold.raise_event(scheduler2Instance.options.query_all_leases_uuid, FILTER_ADDED, ['start_time', '<', tomorrow.getTime()/1000]);\r
- } else {\r
+ if (SchedulerDateSelected == null || SchedulerDateSelected == '') {\r
alert("Please select a date, so the scheduler can reserve leases.");\r
+ return;\r
}\r
+ // Set slider to origin\r
+ $('#tblSlider').slider('value', 0);\r
+ // Refresh leases\r
+ self._scope_clear_leases();\r
+ self._scope_set_leases();\r
+ // Refresh display\r
+ self._get_scope().$apply();\r
}).datepicker('setDate', SchedulerDateSelected);\r
- /*.click(function () {\r
- $("#ui-datepicker-div").css("z-index", 5);\r
- })*/\r
- //End init DatePicker\r
-\r
- //init Table\r
- this._FixTable();\r
- //End init Table\r
\r
//init Slider\r
$('#tblSlider').slider({\r
min: 0,\r
- max: SchedulerTotalCells - SchedulerTotalVisibleCells,\r
+ max: (this._all_slots.length - self._num_visible_cells) / self._lcm_colspan,\r
value: 0,\r
slide: function(event, ui) {\r
- //$("#amount").val("$" + ui.values[0] + " - $" + ui.values[1]);\r
- //console.log(ui.value);\r
- var angScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
- if (_schedulerCurrentCellPosition > ui.value) {\r
- angScope.moveBackSlot(ui.value, ui.value + SchedulerTotalVisibleCells);\r
- } else if (_schedulerCurrentCellPosition < ui.value) {\r
- angScope.moveFrontSlot(ui.value, ui.value + SchedulerTotalVisibleCells);\r
- }\r
- _schedulerCurrentCellPosition = ui.value;\r
- }\r
+ var scope = self._get_scope();\r
+ scope.from = ui.value * self._lcm_colspan;\r
+ scope.$apply();\r
+ }\r
});\r
- //End init Slider\r
-\r
-\r
- //btn Submit leases\r
- $('#btnSchedulerSubmit').click(function () {\r
- console.log("click btnSchedulerSubmit");\r
- var leasesForCommit = new Array();\r
- var tmpDateTime = SchedulerDateSelected;\r
- console.log(SchedulerData);\r
- for (var i = 0; i < SchedulerData.length; i++)\r
- {\r
- var tpmR = SchedulerData[i];\r
- //for capturing start and end of the lease\r
- var newLeaseStarted = false;\r
- for (var j = 0; j < tpmR.leases.length; j++) {\r
- var tpmL = tpmR.leases[j];\r
- if (newLeaseStarted == false && tpmL.status == 'selected') {\r
- //get date of the slot\r
- tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
- var unixStartTime = tmpDateTime.getTime() / 1000;\r
- //add lease object\r
- leasesForCommit.push({\r
- resource: tpmR.id,\r
- //granularity: tpmR.granularity,\r
- //lease_type: null,\r
- //slice: null,\r
- start_time: unixStartTime,\r
- end_time: null,\r
- //duration: null\r
- });\r
- console.log(tpmR.id);\r
- newLeaseStarted = true;\r
- } else if (newLeaseStarted == true && tpmL.status != 'selected') {\r
- //get date of the slot\r
- tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
- var unixEndTime = tmpDateTime.getTime() / 1000;\r
- //upate end_time\r
- var tmpCL = leasesForCommit[leasesForCommit.length - 1];\r
- tmpCL.end_time = unixEndTime;\r
- //tmpCL.duration = schedulerFindDuration(tmpCL.start_time, tmpCL.end_time, tmpCL.granularity);\r
- newLeaseStarted = false;\r
- }\r
- }\r
- }\r
- console.log(leasesForCommit);\r
- for (var i = 0; i < leasesForCommit.length; i++) {\r
- manifold.raise_event(scheduler2Instance.options.query_lease_uuid, SET_ADD, leasesForCommit[i]);\r
- }\r
- });\r
- //\r
-\r
\r
- //End btn Submit leases\r
+ $('#btnSchedulerSubmit').click(this._on_submit);\r
\r
- //other stuff\r
$("#plugin-scheduler-loader").hide();\r
$("#plugin-scheduler").show();\r
- //fixOddEvenClasses();\r
- //$("#" + schedulerTblId + " td:not([class])").addClass("free");\r
- if (schedulerDebug) console.timeEnd("_initUI");\r
},\r
\r
- _FixLeases : function () {\r
- for (var i = 0; i < tmpSchedulerLeases.length; i++) {\r
- var tmpLea = tmpSchedulerLeases[i];\r
- if ((schedulerCompareOnDay(tmpLea.start_time, SchedulerDateSelected) == 0) ||\r
- (tmpLea.start_time <= SchedulerDateSelected && SchedulerDateSelected <= tmpLea.end_time) || \r
- (schedulerCompareOnDay(tmpLea.end_time, SchedulerDateSelected) == 0)) {\r
- var tmpRes = schedulerFindResourceById(SchedulerData, tmpLea.resource);\r
- if (tmpRes != null) {\r
- //Replace Lease with current lease from the manifold\r
- var orgLease = tmpRes.leases[tmpLea.id];\r
- tmpLea['groupid'] = orgLease.groupid;\r
- tmpLea['groupIndex'] = orgLease.groupIndex;\r
- if (orgLease.groupIndex != 0) {\r
- if (!window.console) {\r
- console.warn('there is an error with the leases of the resource :' + tmpRes.name + '\n The lease start in the middle of the granularity!' + '\n The Scheduler plugin might not work!');\r
- }\r
- }\r
- tmpRes.leases[tmpLea.id] = tmpLea;\r
- this._ExtractLeaseSlots(tmpRes, tmpRes.leases[tmpLea.id]);\r
+ // GUI EVENTS\r
+\r
+ // TO BE REMOVED\r
+ _on_submit : function()\r
+ {\r
+ var leasesForCommit = new Array();\r
+ var tmpDateTime = SchedulerDateSelected;\r
+ for (var i = 0; i < SchedulerData.length; i++)\r
+ {\r
+ var tpmR = SchedulerData[i];\r
+ //for capturing start and end of the lease\r
+ var newLeaseStarted = false;\r
+ for (var j = 0; j < tpmR.leases.length; j++) {\r
+ var tpmL = tpmR.leases[j];\r
+ if (newLeaseStarted == false && tpmL.status == 'selected') {\r
+ //get date of the slot\r
+ tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
+ var unixStartTime = tmpDateTime.getTime() / 1000;\r
+ //add lease object\r
+ leasesForCommit.push({\r
+ resource: tpmR.id,\r
+ //granularity: tpmR.granularity,\r
+ //lease_type: null,\r
+ //slice: null,\r
+ start_time: unixStartTime,\r
+ end_time: null,\r
+ //duration: null\r
+ });\r
+ console.log(tpmR.id);\r
+ newLeaseStarted = true;\r
+ } else if (newLeaseStarted == true && tpmL.status != 'selected') {\r
+ //get date of the slot\r
+ tmpDateTime = schedulerGetDateTimeFromSlotId(tpmL.id, tmpDateTime);\r
+ var unixEndTime = tmpDateTime.getTime() / 1000;\r
+ //upate end_time\r
+ var tmpCL = leasesForCommit[leasesForCommit.length - 1];\r
+ tmpCL.end_time = unixEndTime;\r
+ //tmpCL.duration = schedulerFindDuration(tmpCL.start_time, tmpCL.end_time, tmpCL.granularity);\r
+ newLeaseStarted = false;\r
}\r
}\r
}\r
- },\r
-\r
- _ExtractLeaseSlots: function (tmpRes, lease) {\r
- var tmpStartDate = lease.start_time;\r
- var tmpEndDate = lease.end_time;\r
- var startLoop; var toLoop;\r
- if (schedulerCompareOnDay(lease.start_time,lease.end_time) == 0) {\r
- //in the same date\r
- startLoop = lease.id;\r
- toLoop = lease.end_id;\r
- } else if (lease.start_time < SchedulerDateSelected && SchedulerDateSelected < lease.end_time) {\r
- //one hole day (more than 3days)\r
- startLoop = 0;\r
- toLoop = tmpRes.leases.length;\r
- } else if (schedulerCompareOnDay(lease.start_time, SchedulerDateSelected) == 0) {\r
- //the same day and extends\r
- startLoop = lease.id;\r
- toLoop = tmpRes.leases.length;\r
- } else if (schedulerCompareOnDay(lease.end_time, SchedulerDateSelected) == 0) {\r
- //extends to the last say\r
- startLoop = 0;\r
- toLoop = lease.end_id;\r
- }\r
- //var minutGran = tmpRes.granularity * 60;\r
- for (var li = lease.id; li < toLoop; li++) {\r
- tmpRes.leases[li].status = 'reserved';\r
+ console.log(leasesForCommit);\r
+ for (var i = 0; i < leasesForCommit.length; i++) {\r
+ manifold.raise_event(scheduler2Instance.options.query_lease_uuid, SET_ADD, leasesForCommit[i]);\r
}\r
- \r
- //reserved\r
- //tmpRes.leases[tmpLea.id\r
},\r
+ \r
+ // PRIVATE METHODS\r
\r
- _FixTable: function () {\r
- var colWidth = 50;\r
- SchedulerTotalCells = SchedulerSlots.length;\r
- $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", schedulerTblFirstColWidth); //.css("display", "block");\r
- //this get width might need fix depending on the template \r
- var tblwidth = $('#scheduler-tab').parent().outerWidth();\r
- SchedulerTotalVisibleCells = parseInt((tblwidth - schedulerTblFirstColWidth) / colWidth);\r
-\r
- //if (SchedulerData.length == 0) {\r
- // //puth some test data\r
- // SchedulerData.push({ name: 'xyz+aaa', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+aaa', type: 'node' });\r
- // SchedulerData.push({ name: 'xyz+bbb', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+bbb', type: 'node' });\r
- // SchedulerData.push({ name: 'xyz+ccc', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'xyz+ccc', type: 'node' });\r
- // SchedulerData.push({ name: 'nitos1', leases: schedulerGetLeases(60 / schedulerSlotsPerHour), urn: 'nitos1', type: 'node' });\r
- //}\r
- var tmpScope = angular.element(document.getElementById('SchedulerCtrl')).scope();\r
- tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length);\r
-\r
+ /**\r
+ * Greatest common divisor\r
+ */\r
+ _gcd : function(x, y)\r
+ {\r
+ return (y==0) ? x : this._gcd(y, x % y);\r
},\r
\r
- _SetFiletredResources : function (filters) {\r
- if (filters.length > 0) {\r
- SchedulerDataViewData = new Array();\r
- var tmpAddIt = true;\r
- for (var i = 0; i < SchedulerData.length; i++) {\r
- loopfilters:\r
- for (var f = 0; f < filters.length; f++) {\r
- tmpAddIt = this._FilterResource(SchedulerData[i], filters[f]);\r
- if (tmpAddIt == false) break loopfilters;\r
- }\r
- if (tmpAddIt) {\r
- SchedulerDataViewData.push(SchedulerData[i]);\r
- }\r
- }\r
- } else {\r
- SchedulerDataViewData = SchedulerData;\r
- }\r
+ /**\r
+ * Least common multiple\r
+ */\r
+ _lcm : function(x, y)\r
+ {\r
+ return x * y / this._gcd(x, y);\r
+ },\r
+ \r
+ _pad_str : function(i)\r
+ {\r
+ return (i < 10) ? "0" + i : "" + i;\r
},\r
\r
- _FilterResource: function (resource, filter) {\r
- var key = filter[0];\r
- var op = filter[1];\r
- var value = filter[2];\r
- var colValue = resource.org_resource[key];\r
- var ret = true;\r
- if (schedulerDebug && colValue == 'omf') colValue = 'nitos';\r
-\r
- if (op == '=' || op == '==') {\r
- if (colValue != value || colValue == null || colValue == "" || colValue == "n/a")\r
- ret = false;\r
- } else if (op == 'included') {\r
- $.each(value, function (i, x) {\r
- if (x == colValue) {\r
- ret = true;\r
- return false;\r
- } else {\r
- ret = false;\r
- }\r
- });\r
- } else if (op == '!=') {\r
- if (colValue == value || colValue == null || colValue == "" || colValue == "n/a")\r
- ret = false;\r
+ /**\r
+ * Member variables used:\r
+ * _granularity\r
+ * \r
+ * Returns:\r
+ * A list of {id, time} dictionaries.\r
+ */\r
+ _generate_all_slots: function()\r
+ {\r
+ var slots = [];\r
+ // Start with a random date (a first of a month), only time will matter\r
+ var d = new Date(2014, 1, 1, 0, 0, 0, 0);\r
+ var i = 0;\r
+ // Loop until we change the day\r
+ while (d.getDate() == 1) {\r
+ // Nicely format the time...\r
+ var tmpTime = this._pad_str(d.getHours()) + ':' + this._pad_str(d.getMinutes());\r
+ /// ...and add the slot to the list of results\r
+ slots.push({ id: i, time: tmpTime });\r
+ // Increment the date with the granularity\r
+ d = new Date(d.getTime() + this._granularity * 1000);\r
+ i++;\r
}\r
+ return slots;\r
\r
- return ret;\r
},\r
-\r
- _SetPeriodInPage: function (start, end) {\r
- }\r
});\r
\r
- //Sched2 = new Scheduler2();\r
-\r
/* Plugin registration */\r
$.plugin('Scheduler2', scheduler2);\r
\r
- // TODO Here use cases for instanciating plugins in different ways like in the pastie.\r
-\r
-\r
})(jQuery);\r
\r
\r
+++ /dev/null
-/*\r
-#\r
-# Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece\r
-#\r
-# Permission is hereby granted, free of charge, to any person obtaining a copy\r
-# of this software and associated documentation files (the "Software"), to deal\r
-# in the Software without restriction, including without limitation the rights\r
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-# copies of the Software, and to permit persons to whom the Software is\r
-# furnished to do so, subject to the following conditions:\r
-#\r
-# The above copyright notice and this permission notice shall be included in\r
-# all copies or substantial portions of the Software.\r
-#\r
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
-# THE SOFTWARE.\r
-#\r
-#\r
-# This is a MySlice plugin for the NITOS Scheduler\r
-# Nitos Scheduler v1\r
-#\r
-*/\r
-\r
-/* some params */\r
-var init_start_visible_index = 10;\r
-var init_end_visible_index = 21;\r
-var rsvrTblNm = "scheduler-reservation-table";\r
-var SchedulerResources = [];\r
-var schdlr_totalColums = 0;\r
-var SetPerFun = null;\r
-var Sched2 = null;\r
-var Debug = true;\r
-var schdlr_PartsInOneHour = 6;\r
-\r
-(function ($) {\r
- var Scheduler2 = Plugin.extend({\r
-\r
- /** XXX to check\r
- * @brief Plugin constructor\r
- * @param options : an associative array of setting values\r
- * @param element : \r
- * @return : a jQuery collection of objects on which the plugin is\r
- * applied, which allows to maintain chainability of calls\r
- */\r
- init: function (options, element) {\r
- this.classname="scheduler2";\r
- // Call the parent constructor, see FAQ when forgotten\r
- this._super(options, element);\r
-\r
- schdlr_totalColums = $("#scheduler-reservation-table th").length;\r
-\r
- //selection from table \r
- $(window).keydown(function (evt) {\r
- if (evt.which == 17) { // ctrl\r
- ctrlPressed = true;\r
- }\r
- }).keyup(function (evt) {\r
- if (evt.which == 17) { // ctrl\r
- ctrlPressed = false;\r
- }\r
- });\r
- $("#" + rsvrTblNm).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove);\r
-\r
- // Explain this will allow query events to be handled\r
- // What happens when we don't define some events ?\r
- // Some can be less efficient\r
-\r
- if (Debug) console.time("Listening_to_queries");\r
- /* Listening to queries */\r
- this.listen_query(options.query_uuid, 'all_ev');\r
- this.listen_query(options.query_all_resources_uuid, 'all_resources');\r
- this.listen_query(options.query_lease_uuid, 'lease');\r
- //this.listen_query(options.query_lease_uuid, 'lease');\r
- if (Debug) console.timeEnd("Listening_to_queries");\r
-\r
- $("#ShedulerNodes tbody").html('<tr><td id="schdlr_frstTD" style="background:transparent; height:45px; border:none;"></td></tr>');\r
- },\r
-\r
- /* PLUGIN EVENTS */\r
- // on_show like in querytable\r
-\r
-\r
- /* GUI EVENTS */\r
-\r
- // a function to bind events here: click change\r
- // how to raise manifold events\r
-\r
-\r
- /* GUI MANIPULATION */\r
-\r
- // We advise you to write function to change behaviour of the GUI\r
- // Will use naming helpers to access content _inside_ the plugin\r
- // always refer to these functions in the remaining of the code\r
-\r
- show_hide_button: function () {\r
- // this.id, this.el, this.cl, this.elts\r
- // same output as a jquery selector with some guarantees\r
- },\r
-\r
- //drawResources: function () {\r
- drawLeases: function () {\r
- \r
- //if (Debug) this.debug('foo');\r
- if (Debug) console.time("each:SchedulerResources");\r
-\r
- //scheduler-reservation-table main table columns\r
- totalColums = $("#scheduler-reservation-table thead tr th").length;\r
- //var totalCell = [];\r
- //for (var i = 0; i < totalColums; i++) { totalCell.push("<td></td>"); }\r
- //var srt_body = [];\r
- var totalCell = "";\r
- for (var i = 0; i < totalColums; i++) totalCell +="<td></td>"; \r
- var srt_body = "";\r
- /*\r
- $.each(SchedulerResources, function (i, group) {\r
- console.log(group.groupName);\r
- //var groupTR = $("#ShedulerNodes tbody").html('<tr><td class="no-image verticalIndex" rowspan="' + group.resources.length + '"><div class="verticalText">' + group.groupName + '</div></td><td id="schdlr_frstTD" class="info fixed"></td></tr>');\r
- //var groupTR = $("#ShedulerNodes tbody").html('<tr><td class="no-image verticalIndex" rowspan="' + 30 + '"><div class="verticalText">' + group.groupName + '</div></td><td id="schdlr_frstTD" class="info fixed"></td></tr>');\r
- var groupTR = $("#ShedulerNodes tbody").html('<tr><td id="schdlr_frstTD" class="info fixed"></td></tr>');\r
- \r
- //$.each(group.resources.slice(0,30), function (i, resource) {\r
- $.each(group.resources, function (i, resource) {\r
- if (i == 0) {\r
- //$("#ShedulerNodes tbody tr:first").append('<td class="info fixed">' + resource.hostname + '</td>');\r
- $(groupTR).find("#schdlr_frstTD").html(resource.urn);\r
- //$(srt_body).html("<tr>" + totalCell + "</tr>");\r
- } else {\r
- $(groupTR).find("tr:last").after('<tr><td class="info fixed">' + resource.urn + '</td></tr>');\r
- //$(srt_body).find("tr:last").after("<tr>" + totalCell + "</tr>");\r
- }\r
- srt_body += "<tr>" + totalCell + "</tr>";\r
- //srt_body.push('<tr>'); srt_body = srt_body.concat(totalCell.concat()); srt_body.push('/<tr>');\r
- });\r
- });\r
- */\r
-\r
- srt_body += "<tr>" + totalCell + "</tr>";\r
- //$("#scheduler-reservation-table tbody").html(srt_body.join(""));\r
- //$("#scheduler-reservation-table tbody").append(srt_body);\r
-\r
- if (Debug) console.timeEnd("each:SchedulerResources");\r
- \r
-\r
- $("#" + rsvrTblNm + " tbody tr").each(function (index) { $(this).attr("data-trindex", index); });\r
-\r
- },\r
-\r
- /* TEMPLATES */\r
-\r
- // see in the html template\r
- // How to load a template, use of mustache\r
-\r
- /* QUERY HANDLERS */\r
- loadWithDate: function () {\r
- // only convention, not strictly enforced at the moment\r
- },\r
- // How to make sure the plugin is not desynchronized\r
- // He should manifest its interest in filters, fields or records\r
- // functions triggered only if the proper listen is done\r
-\r
- /* all_ev QUERY HANDLERS Start */\r
- on_all_ev_clear_records: function (data) {\r
- //alert('all_ev clear_records');\r
- },\r
- on_all_ev_query_in_progress: function (data) {\r
- // alert('all_ev query_in_progress');\r
- },\r
- on_all_ev_new_record: function (data) {\r
- //alert('all_ev new_record');\r
- },\r
- on_all_ev_query_done: function (data) {\r
- //alert('all_ev query_done');\r
- },\r
- //another plugin has modified something, that requires you to update your display. \r
- on_all_ev_field_state_changed: function (data) {\r
- //alert('all_ev query_done');\r
- },\r
- /* all_ev QUERY HANDLERS End */\r
- /* all_resources QUERY HANDLERS Start */\r
- on_all_resources_clear_records: function (data) {\r
- //data is empty on load\r
- },\r
- on_all_resources_query_in_progress: function (data) {\r
- //data is empty on load\r
- },\r
- on_all_resources_new_record: function (data) {\r
- $("#ShedulerNodes tbody").find("tr:last").after('<tr><td class="info fixed">' + data.urn + '</td></tr>');\r
- this.drawLeases();\r
- //console.log(data);\r
- var tmpGroup = lookup(SchedulerResources, 'groupName', data.type);\r
- if (tmpGroup == null) {\r
- tmpGroup = { groupName: data.type, resources: [] };\r
- SchedulerResources.push(tmpGroup);\r
- //if (data.type != "node") alert('not all node');\r
- }\r
- tmpGroup.resources.push(data);\r
- //alert('new_record');\r
- },\r
- on_all_resources_query_done: function (data) {\r
- //this.drawResources();\r
- //data is empty on load\r
- /* GUI setup and event binding */\r
- this._initUI();\r
- this._SetPeriodInPage(init_start_visible_index, init_end_visible_index);\r
- this.loadWithDate();\r
- },\r
- //another plugin has modified something, that requires you to update your display. \r
- on_all_resources_field_state_changed: function (data) {\r
- //alert('all_resources query_done');\r
- },\r
- /* all_resources QUERY HANDLERS End */\r
- /* lease QUERY HANDLERS Start */\r
- on_lease_clear_records: function (data) { console.log('clear_records'); },\r
- on_lease_query_in_progress: function (data) { console.log('lease_query_in_progress'); },\r
- on_lease_new_record: function (data) { console.log('lease_new_record'); },\r
- on_lease_query_done: function (data) { console.log('lease_query_done'); },\r
- //another plugin has modified something, that requires you to update your display. \r
- on_lease_field_state_changed: function (data) { console.log('lease_field_state_changed'); },\r
- /* lease QUERY HANDLERS End */\r
-\r
-\r
- // no prefix\r
-\r
- on_filter_added: function (filter) {\r
-\r
- },\r
-\r
- // ... be sure to list all events here\r
-\r
- /* RECORD HANDLERS */\r
- on_all_new_record: function (record) {\r
- //\r
- alert('on_all_new_record');\r
- },\r
-\r
- debug : function (log_txt) {\r
- if (typeof window.console != 'undefined') {\r
- console.debug(log_txt);\r
- }\r
- },\r
-\r
- /* INTERNAL FUNCTIONS */\r
- _initUI: function () {\r
- if (Debug) console.time("_initUI");\r
- //fix margins in tables\r
- mtNodesTbl = $("#" + rsvrTblNm + " tr:first").outerHeight() + 6;\r
- mtSchrollCon = $("#nodes").outerWidth();\r
- $("#nodes").css("margin-top", mtNodesTbl);\r
- $("#reservation-table-scroll-container").css("margin-left", mtSchrollCon);\r
- SetPerFun = this._SetPeriodInPage;\r
- //slider\r
- $("#time-range").slider({\r
- range: true,\r
- min: 0,\r
- max: 24,\r
- step: 0.5,\r
- values: [init_start_visible_index, init_end_visible_index],\r
- slide: function (event, ui) {\r
- SetPerFun(ui.values[0], ui.values[1]);\r
- }\r
- });\r
- $("#DateToRes").datepicker({\r
- dateFormat: "yy-mm-dd",\r
- minDate: 0,\r
- numberOfMonths: 3\r
- }).change(function () {\r
- //Scheduler2.loadWithDate();\r
- }).click(function () {\r
- $("#ui-datepicker-div").css("z-index", 5);\r
- });\r
- //other stuff\r
- fixOddEvenClasses();\r
- $("#" + rsvrTblNm + " td:not([class])").addClass("free");\r
- if (Debug) console.timeEnd("_initUI");\r
- },\r
- _SetPeriodInPage: function (start, end) {\r
- if (Debug) console.time("_SetPeriodInPage");\r
- ClearTableSelection();\r
- $("#lbltime").html(GetTimeFromInt(start) + " - " + GetTimeFromInt(end));\r
- \r
- var start_visible_index = (start * schdlr_PartsInOneHour) + 1;\r
- var end_visible_index = (end * schdlr_PartsInOneHour);\r
-\r
- //hide - show\r
- for (i = 0; i < start_visible_index; i++) {\r
- $("#" + rsvrTblNm + " td:nth-child(" + i + "), #" + rsvrTblNm + " th:nth-child(" + i + ")").hide();\r
- }\r
- for (i = end_visible_index + 1; i <= schdlr_totalColums; i++) {\r
- $("#" + rsvrTblNm + " td:nth-child(" + i + "), #" + rsvrTblNm + " th:nth-child(" + i + ")").hide();\r
- }\r
- /*$("#" + rsvrTblNm + " td:not([class*='info']), #" + rsvrTblNm + " th:not([class*='fixed'])").hide();*/\r
- for (i = start_visible_index; i <= end_visible_index; i++) {\r
- $("#" + rsvrTblNm + " td:nth-child(" + i + "), #" + rsvrTblNm + " th:nth-child(" + i + ")").show();\r
- }\r
-\r
- if ($("#" + rsvrTblNm + " th:visible:first").width() > 105) {\r
- $("#" + rsvrTblNm + " th span").css("display", "inline")\r
- } else {\r
- $("#" + rsvrTblNm + " th span").css("display", "block");\r
- }\r
- mtNodesTbl = $("#" + rsvrTblNm + " tr:first").outerHeight() + 6;\r
- $("#nodes").css("margin-top", mtNodesTbl);\r
- //$("#scroll_container").width($("#Search").width() - $("#nodes").width());\r
- //$("#nodes th").height($("#tblReservation th:visible:first").height() - 2);\r
- if (Debug) console.timeEnd("_SetPeriodInPage");\r
- }\r
- });\r
-\r
- //Sched2 = new Scheduler2();\r
-\r
- /* Plugin registration */\r
- $.plugin('Scheduler2', Scheduler2);\r
-\r
- // TODO Here use cases for instanciating plugins in different ways like in the pastie.\r
-\r
-\r
-})(jQuery);\r
-\r
-\r
-\r
<div id="tblSlider"></div>\r
</div>\r
<div class="table-responsive">\r
+\r
<table id="scheduler-reservation-table" class="table table-bordered table-condensed">\r
<thead>\r
<tr>\r
<th>#</th>\r
- <th ng-repeat="slot in slots">\r
+ <th ng-repeat="slot in slots | offset: from | limitTo: num_visible_cells ">\r
{[{ slot.time }]}\r
</th>\r
</tr>\r
</thead>\r
<tbody>\r
- <tr ng-repeat="resource in resources" ng-class-odd="''" ng-class-even="'even'">\r
- <th data-resourceid="{[{ resource.id }]}" data-rowindex="{[{ $index }]}" data-resourceindex="{[{ resource.index }]}" style="word-wrap: break-word; word-break: break-all; ">{[{ resource.name }]}</th>\r
- <td ng-repeat="lease in resource.leases" data-slotid="{[{ lease.id }]}" data-groupid="{[{ lease.groupid }]}" ng-class="{{ 'lease.status' }}"></td>\r
+ <tr ng-repeat="resource in resources | filter: filter_visible | offset: (current_page-1) * items_per_page | limitTo: items_per_page" \r
+ ng-class-odd="''" \r
+ ng-class-even="'even'">\r
+ <th data-resourceid="{[{ resource.urn }]}" \r
+ data-rowindex="{[{ $index }]}" \r
+ data-resourceindex="{[{ resource.index }]}" \r
+ style="word-wrap: break-word; word-break: break-all; ">\r
+ {[{ resource.hostname }]}\r
+ </th>\r
+ <td ng-repeat="lease in resource.leases | offset: from / lcm_colspan | limitTo: num_visible_cells / lcm_colspan"\r
+ data-slotid="{[{ lease.id }]}" \r
+ data-groupid="{[{ lease.groupid }]}" \r
+ ng-class="{{ 'lease.status' }}"\r
+ colspan="{[{resource.granularity / granularity}]}"\r
+ ng-click="select(from+$index, lease, $parent.resource)">\r
+ </td>\r
</tr>\r
</tbody>\r
</table>\r
+\r
<div class="row">\r
<div class="col-xs-5">\r
<div id="resources-list__table_length" class="dataTables_length">\r
- <label>total Pages : {[{totalPages}]}</label>\r
+ <label>Total pages : {[{page_count()}]}</label>\r
</div>\r
</div>\r
<div class="col-xs-7">\r
<div class="dataTables_paginate paging_bootstrap">\r
<ul class="pagination">\r
- <li class="prev disabled"><a href="#">← Previous</a></li>\r
- <li ng-cloak ng-repeat="t in getPageNumbers() track by $index" ng-class="{active: t==curPage+1}"><a href="#" ng-click="setPage(t-1)">{[{t}]}</a></li>\r
- <li class="next disabled"><a href="#">Next → </a></li>\r
+ <li ng-class="prevPageDisabled()">\r
+ <a href ng-click="prevPage()">« Prev</a>\r
+ </li>\r
+ <li ng-repeat="n in range()" \r
+ ng-class="{active: n==current_page}" \r
+ ng-click="setPage(n)"><a href="#">{[{n}]}</a></li>\r
+ <li ng-class="nextPageDisabled()">\r
+ <a href ng-click="nextPage()">Next »</a>\r
+ </li>\r
</ul>\r
</div>\r
</div>\r
-from django.template import RequestContext
-from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.shortcuts import render_to_response
-from manifold.core.query import Query, AnalyzedQuery
-from manifoldapi.manifoldapi import execute_query
+from manifold.core.query import Query, AnalyzedQuery
+from manifoldapi.manifoldapi import execute_query
-from django.views.generic.base import TemplateView
+from django.views.generic.base import TemplateView
-from unfold.loginrequired import LoginRequiredView
+from unfold.loginrequired import LoginRequiredView
from django.http import HttpResponse
from django.shortcuts import render
-from unfold.page import Page
+from unfold.page import Page
-from myslice.configengine import ConfigEngine
-from plugins.querytable import QueryTable
-from plugins.googlemap import GoogleMap
-from plugins.queryupdater import QueryUpdater
-from plugins.testbeds import TestbedsPlugin
-from plugins.scheduler2 import Scheduler2
-from plugins.columns_editor import ColumnsEditor
-from plugins.sladialog import SlaDialog
-from plugins.lists.simplelist import SimpleList
+from myslice.configengine import ConfigEngine
+
+from plugins.apply import ApplyPlugin
+from plugins.querytable import QueryTable
+from plugins.googlemap import GoogleMap
+#from plugins.queryupdater import QueryUpdater
+from plugins.filter_status import FilterStatusPlugin
+from plugins.testbeds import TestbedsPlugin
+from plugins.scheduler2 import Scheduler2
+from plugins.columns_editor import ColumnsEditor
+from plugins.sladialog import SlaDialog
+from plugins.lists.simplelist import SimpleList
from myslice.theme import ThemeView
# Example: select slice_hrn, resource.urn, lease.resource, lease.start_time, lease.end_time from slice where slice_hrn == "ple.upmc.myslicedemo"
main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
main_query.select(
+ 'slice_urn', # XXX We need the key otherwise the storage of records bugs !
'slice_hrn',
'resource.urn',
'resource.hostname', 'resource.type',
sq_lease = aq.subquery('lease')
query_resource_all = Query.get('resource').select(resource_fields)
- page.enqueue_query(query_resource_all)
+ #page.enqueue_query(query_resource_all)
# leases query
lease_md = metadata.details_by_object('lease')
# --------------------------------------------------------------------------
# QueryUpdater (Pending Operations)
- pending_resources = QueryUpdater(
- page = page,
- title = 'Pending operations',
- query = main_query,
- togglable = False,
- # start turned off, it will open up itself when stuff comes in
- toggled = False,
- domid = 'pending',
- outline_complete = True,
- username = request.user,
- )
+#DEPRECATED| pending_resources = QueryUpdater(
+#DEPRECATED| page = page,
+#DEPRECATED| title = 'Pending operations',
+#DEPRECATED| query = main_query,
+#DEPRECATED| togglable = False,
+#DEPRECATED| # start turned off, it will open up itself when stuff comes in
+#DEPRECATED| toggled = False,
+#DEPRECATED| domid = 'pending',
+#DEPRECATED| outline_complete = True,
+#DEPRECATED| username = request.user,
+#DEPRECATED| )
# --------------------------------------------------------------------------
# NETWORKS
#page.enqueue_query(query_network)
filter_testbeds = TestbedsPlugin(
- page = page,
- domid = 'testbeds-filter',
- title = 'Filter by testbeds',
- query = sq_resource,
- query_all = query_resource_all,
- #query_network = query_network,
- init_key = "network_hrn",
- checkboxes = True,
+ page = page,
+ domid = 'testbeds-filter',
+ title = 'Filter by testbeds',
+ query = sq_resource,
+ #query_network = query_network,
+ init_key = "network_hrn",
+ checkboxes = True,
datatables_options = {
'iDisplayLength': 25,
'bLengthChange' : True,
},
)
+ filter_status = FilterStatusPlugin(
+ page = page,
+ domid = "filter-status",
+ query = sq_resource,
+ )
+ apply = ApplyPlugin(
+ page = page,
+ domid = "apply",
+ query = sq_resource,
+ )
+
+
# --------------------------------------------------------------------------
# SLA View and accept dialog
template_env['columns_editor'] = filter_column_editor.render(self.request)
template_env['filter_testbeds'] = filter_testbeds.render(self.request)
+ template_env['filter_status'] = filter_status.render(self.request)
+ template_env['apply'] = apply.render(self.request)
+
template_env['map_resources'] = map_resources.render(self.request)
template_env['scheduler'] = resources_as_scheduler2.render(self.request)
- template_env['pending_resources'] = pending_resources.render(self.request)
+# template_env['pending_resources'] = pending_resources.render(self.request)
template_env['sla_dialog'] = sla_dialog.render(self.request)
template_env["theme"] = self.theme
template_env["username"] = request.user
from plugins.tabs import Tabs
from plugins.querytable import QueryTable
from plugins.querygrid import QueryGrid
-from plugins.queryupdater import QueryUpdater
+#DEPRECATED|from plugins.queryupdater import QueryUpdater
from plugins.googlemap import GoogleMap
from plugins.senslabmap import SensLabMap
from plugins.scheduler import Scheduler
)
# --------------------------------------------------------------------------
- # QueryUpdater (Pending Operations)
-
- main_stack.insert(QueryUpdater(
- page = page,
- title = 'Pending operations',
- query = main_query,
- togglable = True,
- # start turned off, it will open up itself when stuff comes in
- toggled = False,
- domid = 'pending',
- outline_complete = True,
- ))
+#DEPRECATED| # QueryUpdater (Pending Operations)
+#DEPRECATED|
+#DEPRECATED| main_stack.insert(QueryUpdater(
+#DEPRECATED| page = page,
+#DEPRECATED| title = 'Pending operations',
+#DEPRECATED| query = main_query,
+#DEPRECATED| togglable = True,
+#DEPRECATED| # start turned off, it will open up itself when stuff comes in
+#DEPRECATED| toggled = False,
+#DEPRECATED| domid = 'pending',
+#DEPRECATED| outline_complete = True,
+#DEPRECATED| ))
# --------------------------------------------------------------------------
# Filter Resources
{% insert_str prelude "js/class.js" %}
{% insert_str prelude "js/plugin-helper.js" %}
{% insert_str prelude "js/mustache.js" %}
+{% insert_str prelude "js/hashtable.js" %}
{% insert_str prelude "js/plugin.js" %}
{% insert_str prelude "js/manifold.js" %}
{% insert_str prelude "css/manifold.css" %}
{% insert_str prelude "js/class.js" %}
{% insert_str prelude "js/plugin-helper.js" %}
{% insert_str prelude "js/mustache.js" %}
+{% insert_str prelude "js/hashtable.js" %}
{% insert_str prelude "js/plugin.js" %}
{% insert_str prelude "js/manifold.js" %}
{% insert_str prelude "css/manifold.css" %}
{% insert_str prelude "js/class.js" %}
{% insert_str prelude "js/plugin-helper.js" %}
{% insert_str prelude "js/mustache.js" %}
+{% insert_str prelude "js/hashtable.js" %}
{% insert_str prelude "js/plugin.js" %}
{% insert_str prelude "js/manifold.js" %}
{% insert_str prelude "css/manifold.css" %}
<div class="col-md-12"><p class="alert-success">{{ msg }}</p></div>
{% endif %}
</div>
+
+ {{ filter_status }}{{ apply }}
+ <!--
<div class="list-group-item list-resources">
<span class="list-group-item-heading" style="padding-left: 0;">Resource status:</span>
<a class="list-group-item active" data-panel="resources" href="#" style='display: inline-block !important;'>All</a>
<a class="list-group-item" data-panel="reserved" href="#" style='display: inline-block !important;'>Reserved</a>
<a class="list-group-item" data-panel="pending" href="#" style='display: inline-block !important;'>Pending <span class="badge" id="badge-pending" data-number="0"></span></a>
</div>
-
+ -->
+
<div class="row">
<ul class="nav nav-pills nav-resources">
<li class="active"><a data-panel="resources" href="#">Table</a></li>
<div class="row" style="height:100%;">
<div id="resources" class="panel">
<!-- Button trigger modal - columns selector -->
- <button class="btn btn-primary btn-sm" style="float:right;" data-toggle="modal" data-target="#myModal">Select columns</button>
+ <button class="btn btn-primary btn-sm" style="float:right;" data-toggle="modal" data-target="#myModal">...</button>
{{list_resources}}
<!-- <table cellpadding="0" cellspacing="0" border="0" class="table" id="objectList"></table> -->
</div>
#interval_animation {
margin: 5px;
}
-.tooltip {
+.Atooltip {
display: none;
position: absolute;
border: 1px solid #333;
{% for json in queries_json %}manifold.insert_query({{ json|safe }});
{% endfor %}
-$(document).ready(function () {
-var query_exec_tuples = [];
-{% for tuple in query_exec_tuples %} query_exec_tuples.push({'query_uuid':"{{ tuple.query_uuid }}"});
-{% endfor %}
-manifold.asynchroneous_exec(query_exec_tuples);
-})
+//$(document).ready(function () {
+//var query_exec_tuples = [];
+//{% for tuple in query_exec_tuples %} query_exec_tuples.push({'query_uuid':"{{ tuple.query_uuid }}"});
+//{% endfor %}
+//manifold.asynchroneous_exec(query_exec_tuples);
+//})