Revision 4e03ba30

b/snf-astakos-app/astakos/api/urls.py
35 35

  
36 36
urlpatterns = patterns(
37 37
    'astakos.api.quotas',
38
    url(r'^quotas/?$', 'quotas'),
38
    url(r'^quotas/?$', 'quotas', name="astakos-api-quotas"),
39 39
    url(r'^service_quotas/?$', 'service_quotas'),
40 40
    url(r'^resources/?$', 'resources'),
41 41
    url(r'^commissions/?$', 'commissions'),
b/snf-astakos-app/astakos/im/static/im/js/jquery.cookie.js
1
/*!
2
 * jQuery Cookie Plugin v1.3.1
3
 * https://github.com/carhartl/jquery-cookie
4
 *
5
 * Copyright 2013 Klaus Hartl
6
 * Released under the MIT license
7
 */
8
(function (factory) {
9
	if (typeof define === 'function' && define.amd) {
10
		// AMD. Register as anonymous module.
11
		define(['jquery'], factory);
12
	} else {
13
		// Browser globals.
14
		factory(jQuery);
15
	}
16
}(function ($) {
17

  
18
	var pluses = /\+/g;
19

  
20
	function raw(s) {
21
		return s;
22
	}
23

  
24
	function decoded(s) {
25
		return decodeURIComponent(s.replace(pluses, ' '));
26
	}
27

  
28
	function converted(s) {
29
		if (s.indexOf('"') === 0) {
30
			// This is a quoted cookie as according to RFC2068, unescape
31
			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
32
		}
33
		try {
34
			return config.json ? JSON.parse(s) : s;
35
		} catch(er) {}
36
	}
37

  
38
	var config = $.cookie = function (key, value, options) {
39

  
40
		// write
41
		if (value !== undefined) {
42
			options = $.extend({}, config.defaults, options);
43

  
44
			if (typeof options.expires === 'number') {
45
				var days = options.expires, t = options.expires = new Date();
46
				t.setDate(t.getDate() + days);
47
			}
48

  
49
			value = config.json ? JSON.stringify(value) : String(value);
50

  
51
			return (document.cookie = [
52
				config.raw ? key : encodeURIComponent(key),
53
				'=',
54
				config.raw ? value : encodeURIComponent(value),
55
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
56
				options.path    ? '; path=' + options.path : '',
57
				options.domain  ? '; domain=' + options.domain : '',
58
				options.secure  ? '; secure' : ''
59
			].join(''));
60
		}
61

  
62
		// read
63
		var decode = config.raw ? raw : decoded;
64
		var cookies = document.cookie.split('; ');
65
		var result = key ? undefined : {};
66
		for (var i = 0, l = cookies.length; i < l; i++) {
67
			var parts = cookies[i].split('=');
68
			var name = decode(parts.shift());
69
			var cookie = decode(parts.join('='));
70

  
71
			if (key && key === name) {
72
				result = converted(cookie);
73
				break;
74
			}
75

  
76
			if (!key) {
77
				result[name] = converted(cookie);
78
			}
79
		}
80

  
81
		return result;
82
	};
83

  
84
	config.defaults = {};
85

  
86
	$.removeCookie = function (key, options) {
87
		if ($.cookie(key) !== undefined) {
88
			// Must not alter options, thus extending a fresh object...
89
			$.cookie(key, '', $.extend({}, options, { expires: -1 }));
90
			return true;
91
		}
92
		return false;
93
	};
94

  
95
}));
b/snf-astakos-app/astakos/im/static/im/js/mustache.js
1
/*!
2
 * mustache.js - Logic-less {{mustache}} templates with JavaScript
3
 * http://github.com/janl/mustache.js
4
 */
5

  
6
/*global define: false*/
7

  
8
(function (root, mustache) {
9
  if (typeof exports === "object" && exports) {
10
    module.exports = mustache; // CommonJS
11
  } else if (typeof define === "function" && define.amd) {
12
    define(mustache); // AMD
13
  } else {
14
    root.Mustache = mustache; // <script>
15
  }
16
}(this, (function () {
17

  
18
  var whiteRe = /\s*/;
19
  var spaceRe = /\s+/;
20
  var nonSpaceRe = /\S/;
21
  var eqRe = /\s*=/;
22
  var curlyRe = /\s*\}/;
23
  var tagRe = /#|\^|\/|>|\{|&|=|!/;
24

  
25
  var _test = RegExp.prototype.test;
26
  var _toString = Object.prototype.toString;
27

  
28
  // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
29
  // See https://github.com/janl/mustache.js/issues/189
30
  function testRe(re, string) {
31
    return _test.call(re, string);
32
  }
33

  
34
  function isWhitespace(string) {
35
    return !testRe(nonSpaceRe, string);
36
  }
37

  
38
  var isArray = Array.isArray || function (obj) {
39
    return _toString.call(obj) === '[object Array]';
40
  };
41

  
42
  function escapeRe(string) {
43
    return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
44
  }
45

  
46
  var entityMap = {
47
    "&": "&amp;",
48
    "<": "&lt;",
49
    ">": "&gt;",
50
    '"': '&quot;',
51
    "'": '&#39;',
52
    "/": '&#x2F;'
53
  };
54

  
55
  function escapeHtml(string) {
56
    return String(string).replace(/[&<>"'\/]/g, function (s) {
57
      return entityMap[s];
58
    });
59
  }
60

  
61
  function Scanner(string) {
62
    this.string = string;
63
    this.tail = string;
64
    this.pos = 0;
65
  }
66

  
67
  /**
68
   * Returns `true` if the tail is empty (end of string).
69
   */
70
  Scanner.prototype.eos = function () {
71
    return this.tail === "";
72
  };
73

  
74
  /**
75
   * Tries to match the given regular expression at the current position.
76
   * Returns the matched text if it can match, the empty string otherwise.
77
   */
78
  Scanner.prototype.scan = function (re) {
79
    var match = this.tail.match(re);
80

  
81
    if (match && match.index === 0) {
82
      this.tail = this.tail.substring(match[0].length);
83
      this.pos += match[0].length;
84
      return match[0];
85
    }
86

  
87
    return "";
88
  };
89

  
90
  /**
91
   * Skips all text until the given regular expression can be matched. Returns
92
   * the skipped string, which is the entire tail if no match can be made.
93
   */
94
  Scanner.prototype.scanUntil = function (re) {
95
    var match, pos = this.tail.search(re);
96

  
97
    switch (pos) {
98
    case -1:
99
      match = this.tail;
100
      this.pos += this.tail.length;
101
      this.tail = "";
102
      break;
103
    case 0:
104
      match = "";
105
      break;
106
    default:
107
      match = this.tail.substring(0, pos);
108
      this.tail = this.tail.substring(pos);
109
      this.pos += pos;
110
    }
111

  
112
    return match;
113
  };
114

  
115
  function Context(view, parent) {
116
    this.view = view || {};
117
    this.parent = parent;
118
    this._cache = {};
119
  }
120

  
121
  Context.make = function (view) {
122
    return (view instanceof Context) ? view : new Context(view);
123
  };
124

  
125
  Context.prototype.push = function (view) {
126
    return new Context(view, this);
127
  };
128

  
129
  Context.prototype.lookup = function (name) {
130
    var value = this._cache[name];
131

  
132
    if (!value) {
133
      if (name == '.') {
134
        value = this.view;
135
      } else {
136
        var context = this;
137

  
138
        while (context) {
139
          if (name.indexOf('.') > 0) {
140
            value = context.view;
141
            var names = name.split('.'), i = 0;
142
            while (value && i < names.length) {
143
              value = value[names[i++]];
144
            }
145
          } else {
146
            value = context.view[name];
147
          }
148

  
149
          if (value != null) break;
150

  
151
          context = context.parent;
152
        }
153
      }
154

  
155
      this._cache[name] = value;
156
    }
157

  
158
    if (typeof value === 'function') value = value.call(this.view);
159

  
160
    return value;
161
  };
162

  
163
  function Writer() {
164
    this.clearCache();
165
  }
166

  
167
  Writer.prototype.clearCache = function () {
168
    this._cache = {};
169
    this._partialCache = {};
170
  };
171

  
172
  Writer.prototype.compile = function (template, tags) {
173
    var fn = this._cache[template];
174

  
175
    if (!fn) {
176
      var tokens = mustache.parse(template, tags);
177
      fn = this._cache[template] = this.compileTokens(tokens, template);
178
    }
179

  
180
    return fn;
181
  };
182

  
183
  Writer.prototype.compilePartial = function (name, template, tags) {
184
    var fn = this.compile(template, tags);
185
    this._partialCache[name] = fn;
186
    return fn;
187
  };
188

  
189
  Writer.prototype.getPartial = function (name) {
190
    if (!(name in this._partialCache) && this._loadPartial) {
191
      this.compilePartial(name, this._loadPartial(name));
192
    }
193

  
194
    return this._partialCache[name];
195
  };
196

  
197
  Writer.prototype.compileTokens = function (tokens, template) {
198
    var self = this;
199
    return function (view, partials) {
200
      if (partials) {
201
        if (typeof partials === 'function') {
202
          self._loadPartial = partials;
203
        } else {
204
          for (var name in partials) {
205
            self.compilePartial(name, partials[name]);
206
          }
207
        }
208
      }
209

  
210
      return renderTokens(tokens, self, Context.make(view), template);
211
    };
212
  };
213

  
214
  Writer.prototype.render = function (template, view, partials) {
215
    return this.compile(template)(view, partials);
216
  };
217

  
218
  /**
219
   * Low-level function that renders the given `tokens` using the given `writer`
220
   * and `context`. The `template` string is only needed for templates that use
221
   * higher-order sections to extract the portion of the original template that
222
   * was contained in that section.
223
   */
224
  function renderTokens(tokens, writer, context, template) {
225
    var buffer = '';
226

  
227
    var token, tokenValue, value;
228
    for (var i = 0, len = tokens.length; i < len; ++i) {
229
      token = tokens[i];
230
      tokenValue = token[1];
231

  
232
      switch (token[0]) {
233
      case '#':
234
        value = context.lookup(tokenValue);
235

  
236
        if (typeof value === 'object') {
237
          if (isArray(value)) {
238
            for (var j = 0, jlen = value.length; j < jlen; ++j) {
239
              buffer += renderTokens(token[4], writer, context.push(value[j]), template);
240
            }
241
          } else if (value) {
242
            buffer += renderTokens(token[4], writer, context.push(value), template);
243
          }
244
        } else if (typeof value === 'function') {
245
          var text = template == null ? null : template.slice(token[3], token[5]);
246
          value = value.call(context.view, text, function (template) {
247
            return writer.render(template, context);
248
          });
249
          if (value != null) buffer += value;
250
        } else if (value) {
251
          buffer += renderTokens(token[4], writer, context, template);
252
        }
253

  
254
        break;
255
      case '^':
256
        value = context.lookup(tokenValue);
257

  
258
        // Use JavaScript's definition of falsy. Include empty arrays.
259
        // See https://github.com/janl/mustache.js/issues/186
260
        if (!value || (isArray(value) && value.length === 0)) {
261
          buffer += renderTokens(token[4], writer, context, template);
262
        }
263

  
264
        break;
265
      case '>':
266
        value = writer.getPartial(tokenValue);
267
        if (typeof value === 'function') buffer += value(context);
268
        break;
269
      case '&':
270
        value = context.lookup(tokenValue);
271
        if (value != null) buffer += value;
272
        break;
273
      case 'name':
274
        value = context.lookup(tokenValue);
275
        if (value != null) buffer += mustache.escape(value);
276
        break;
277
      case 'text':
278
        buffer += tokenValue;
279
        break;
280
      }
281
    }
282

  
283
    return buffer;
284
  }
285

  
286
  /**
287
   * Forms the given array of `tokens` into a nested tree structure where
288
   * tokens that represent a section have two additional items: 1) an array of
289
   * all tokens that appear in that section and 2) the index in the original
290
   * template that represents the end of that section.
291
   */
292
  function nestTokens(tokens) {
293
    var tree = [];
294
    var collector = tree;
295
    var sections = [];
296

  
297
    var token;
298
    for (var i = 0, len = tokens.length; i < len; ++i) {
299
      token = tokens[i];
300
      switch (token[0]) {
301
      case '#':
302
      case '^':
303
        sections.push(token);
304
        collector.push(token);
305
        collector = token[4] = [];
306
        break;
307
      case '/':
308
        var section = sections.pop();
309
        section[5] = token[2];
310
        collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
311
        break;
312
      default:
313
        collector.push(token);
314
      }
315
    }
316

  
317
    return tree;
318
  }
319

  
320
  /**
321
   * Combines the values of consecutive text tokens in the given `tokens` array
322
   * to a single token.
323
   */
324
  function squashTokens(tokens) {
325
    var squashedTokens = [];
326

  
327
    var token, lastToken;
328
    for (var i = 0, len = tokens.length; i < len; ++i) {
329
      token = tokens[i];
330
      if (token) {
331
        if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
332
          lastToken[1] += token[1];
333
          lastToken[3] = token[3];
334
        } else {
335
          lastToken = token;
336
          squashedTokens.push(token);
337
        }
338
      }
339
    }
340

  
341
    return squashedTokens;
342
  }
343

  
344
  function escapeTags(tags) {
345
    return [
346
      new RegExp(escapeRe(tags[0]) + "\\s*"),
347
      new RegExp("\\s*" + escapeRe(tags[1]))
348
    ];
349
  }
350

  
351
  /**
352
   * Breaks up the given `template` string into a tree of token objects. If
353
   * `tags` is given here it must be an array with two string values: the
354
   * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
355
   * course, the default is to use mustaches (i.e. Mustache.tags).
356
   */
357
  function parseTemplate(template, tags) {
358
    template = template || '';
359
    tags = tags || mustache.tags;
360

  
361
    if (typeof tags === 'string') tags = tags.split(spaceRe);
362
    if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', '));
363

  
364
    var tagRes = escapeTags(tags);
365
    var scanner = new Scanner(template);
366

  
367
    var sections = [];     // Stack to hold section tokens
368
    var tokens = [];       // Buffer to hold the tokens
369
    var spaces = [];       // Indices of whitespace tokens on the current line
370
    var hasTag = false;    // Is there a {{tag}} on the current line?
371
    var nonSpace = false;  // Is there a non-space char on the current line?
372

  
373
    // Strips all whitespace tokens array for the current line
374
    // if there was a {{#tag}} on it and otherwise only space.
375
    function stripSpace() {
376
      if (hasTag && !nonSpace) {
377
        while (spaces.length) {
378
          delete tokens[spaces.pop()];
379
        }
380
      } else {
381
        spaces = [];
382
      }
383

  
384
      hasTag = false;
385
      nonSpace = false;
386
    }
387

  
388
    var start, type, value, chr, token;
389
    while (!scanner.eos()) {
390
      start = scanner.pos;
391

  
392
      // Match any text between tags.
393
      value = scanner.scanUntil(tagRes[0]);
394
      if (value) {
395
        for (var i = 0, len = value.length; i < len; ++i) {
396
          chr = value.charAt(i);
397

  
398
          if (isWhitespace(chr)) {
399
            spaces.push(tokens.length);
400
          } else {
401
            nonSpace = true;
402
          }
403

  
404
          tokens.push(['text', chr, start, start + 1]);
405
          start += 1;
406

  
407
          // Check for whitespace on the current line.
408
          if (chr == '\n') stripSpace();
409
        }
410
      }
411

  
412
      // Match the opening tag.
413
      if (!scanner.scan(tagRes[0])) break;
414
      hasTag = true;
415

  
416
      // Get the tag type.
417
      type = scanner.scan(tagRe) || 'name';
418
      scanner.scan(whiteRe);
419

  
420
      // Get the tag value.
421
      if (type === '=') {
422
        value = scanner.scanUntil(eqRe);
423
        scanner.scan(eqRe);
424
        scanner.scanUntil(tagRes[1]);
425
      } else if (type === '{') {
426
        value = scanner.scanUntil(new RegExp('\\s*' + escapeRe('}' + tags[1])));
427
        scanner.scan(curlyRe);
428
        scanner.scanUntil(tagRes[1]);
429
        type = '&';
430
      } else {
431
        value = scanner.scanUntil(tagRes[1]);
432
      }
433

  
434
      // Match the closing tag.
435
      if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos);
436

  
437
      token = [type, value, start, scanner.pos];
438
      tokens.push(token);
439

  
440
      if (type === '#' || type === '^') {
441
        sections.push(token);
442
      } else if (type === '/') {
443
        // Check section nesting.
444
        if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start);
445
        var openSection = sections.pop();
446
        if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
447
      } else if (type === 'name' || type === '{' || type === '&') {
448
        nonSpace = true;
449
      } else if (type === '=') {
450
        // Set the tags for the next time around.
451
        tags = value.split(spaceRe);
452
        if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
453
        tagRes = escapeTags(tags);
454
      }
455
    }
456

  
457
    // Make sure there are no open sections when we're done.
458
    var openSection = sections.pop();
459
    if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
460

  
461
    tokens = squashTokens(tokens);
462

  
463
    return nestTokens(tokens);
464
  }
465

  
466
  var mustache = {};
467

  
468
  mustache.name = "mustache.js";
469
  mustache.version = "0.7.2";
470
  mustache.tags = ["{{", "}}"];
471

  
472
  mustache.Scanner = Scanner;
473
  mustache.Context = Context;
474
  mustache.Writer = Writer;
475

  
476
  mustache.parse = parseTemplate;
477

  
478
  // Export the escaping function so that the user may override it.
479
  // See https://github.com/janl/mustache.js/issues/244
480
  mustache.escape = escapeHtml;
481

  
482
  // All Mustache.* functions use this writer.
483
  var _writer = new Writer();
484

  
485
  /**
486
   * Clears all cached templates and partials in the default writer.
487
   */
488
  mustache.clearCache = function () {
489
    return _writer.clearCache();
490
  };
491

  
492
  /**
493
   * Compiles the given `template` to a reusable function using the default
494
   * writer.
495
   */
496
  mustache.compile = function (template, tags) {
497
    return _writer.compile(template, tags);
498
  };
499

  
500
  /**
501
   * Compiles the partial with the given `name` and `template` to a reusable
502
   * function using the default writer.
503
   */
504
  mustache.compilePartial = function (name, template, tags) {
505
    return _writer.compilePartial(name, template, tags);
506
  };
507

  
508
  /**
509
   * Compiles the given array of tokens (the output of a parse) to a reusable
510
   * function using the default writer.
511
   */
512
  mustache.compileTokens = function (tokens, template) {
513
    return _writer.compileTokens(tokens, template);
514
  };
515

  
516
  /**
517
   * Renders the `template` with the given `view` and `partials` using the
518
   * default writer.
519
   */
520
  mustache.render = function (template, view, partials) {
521
    return _writer.render(template, view, partials);
522
  };
523

  
524
  // This is here for backwards compatibility with 0.4.x.
525
  mustache.to_html = function (template, view, partials, send) {
526
    var result = mustache.render(template, view, partials);
527

  
528
    if (typeof send === "function") {
529
      send(result);
530
    } else {
531
      return result;
532
    }
533
  };
534

  
535
  return mustache;
536

  
537
}())));
b/snf-astakos-app/astakos/im/static/im/js/usage.js
39 39
    return sign + (j ? intPart.substr(0, j) + thousandsSep : '') + intPart.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep) + (decimals ? decPoint + Math.abs(number - intPart).toFixed(decimals).slice(2) : '');
40 40
  };
41 41

  
42
function UsageClient(settings) {
42

  
43
DO_LOG = false
44

  
45
LOG = DO_LOG ? _.bind(console.log, console) : function() {};
46
WARN = DO_LOG ? _.bind(console.warn, console) : function() {};
47

  
48
var default_usage_cls_map = {
49
  0: 'green',
50
  33: 'yellow',
51
  66: 'red'
52
}
53

  
54
function UsageView(settings) {
43 55
  this.settings = settings;
44 56
  this.url = this.settings.url;
45 57
  this.container = $(this.settings.container);
58
  this.meta = this.settings.meta;
59
  this.groups = this.settings.groups;
60
  this.el = {};
61
  this.usage_cls_map = this.settings.usage_cls_map || default_usage_cls_map;
62
  this.initialize();
46 63
}
47 64

  
48
UsageClient.prototype.load = function() {
49
  var self = this;
50
  $.ajax(this.url, {
51
    'success': function(data) {
52
      self.update(data);
53
    }
54
  })
55
}
56 65

  
57
function setText(el, valueFrom, valueTo, direction, modifier) {
58
  //valueTo = parseInt(valueTo);
59
  //text = valueFrom;
66
_.extend(UsageView.prototype, {
67
  tpls: {
68
      'main': '<div class="stats clearfix"><ul></ul></div>',
69
      'quotas': "#quotaTpl"
70
  },
60 71

  
61
  //if (valueFrom >= valueTo) {
62
    //valueFrom = valueTo;
63
  //}
64
  
65
  var text = valueTo;
66
  if (modifier) {
67
    text = modifier(text);
68
  }
69
  el.html(text);
72
  initialize: function() {
73
    LOG("Initializing UsageView", this.settings);
74
    this.initResources();
70 75

  
71
  //if (valueTo > valueFrom) {
72
    //window.setTimeout(function() {
73
      //setText(el, parseInt(valueFrom) + step, parseInt(valueTo));
74
    //}, 10)
75
  //}
76
}
76
    // initial usage set ????
77
    this.quotas = {};
78
    if (this.settings.quotas && _.keys(this.settings.quotas).length > 0) {
79
      this.setQuotas(this.settings.quotas);
80
    }
81
    this.initLayout();
82
    this.updateQuotas();
83
  },
84
  
85
  $: function(selector) {
86
    return this.container;
87
  },
88

  
89
  render: function(tpl, params) {
90
    LOG("Rendering", tpl, params);
91
    var tpl = this.tpls[tpl];
92
    if (/^[#\.]/.exec(tpl)) { 
93
      tpl = $(tpl).html();
94
    }
95
    var rendered = Mustache.render(tpl, params);
96
    return $(rendered);
97
  },
98

  
99
  initLayout: function() {
100
    LOG("Initializing layout");
101
    this.el.main = this.render('main');
102
    this.container.append(this.el.main);
103
    var ul = this.container.find("ul");
104
    this.el.list = this.render('quotas', {
105
      'resources':this.resources_ordered
106
    });
107
    ul.append(this.el.list);
108
  },
109
  
110
  initResources: function() {
111
    var ordered = this.meta.resources_order;
112
    var resources = {};
113
    var resources_ordered = [];
114

  
115
    _.each(this.meta.resources, function(group, index) {
116
      _.each(group[1], function(resource, rindex) {
117
        var resource_index = ordered.length;
118
        if (!_.contains(ordered, resource.name)) {
119
          ordered.push(resource.name);
120
        } else {
121
          resource_index = ordered.indexOf(resource.name);
122
        }
123
        resource.index = resource_index;
124
        resource.resource_name = resource.name.split(".")[1];
125
        resources[resource.name] = resource;
126
      })
127
    });
128
      
129
    resources_ordered = _.map(ordered, function(rk) { return resources[rk] });
130
    this.resources = resources;
131
    this.resources_ordered = resources_ordered;
132

  
133
    LOG("Resources initialized", this.resources_ordered, this.resources);
134
  },
135

  
136
  updateLayout: function() {
137
    LOG("Updating layout", this.quotas);
138
    var self = this;
139
    _.each(this.quotas, function(value, key) {
140
      var usage = self.getUsage(key);
141
      var el = self.$().find("li[data-resource='"+key+"']");
142
      self.updateResourceElement(el, usage);
143
    })
144
  },
145

  
146
  updateResourceElement: function(el, usage) {
147
    el.find(".currValue").text(usage.curr);
148
    el.find(".maxValue").text(usage.max);
149
    el.find(".bar span").css({width:usage.perc+"%"});
150
    el.find(".bar .value").text(usage.perc+"%");
151
    var left = usage.label_left == 'auto' ? 
152
               usage.label_left : usage.label_left + "%";
153
    el.find(".bar .value").css({left:left});
154
    el.find(".bar .value").css({color:usage.label_color});
155
    el.removeClass("green yellow red");
156
    el.addClass(usage.cls);
157
  },
158
    
159
  getUsage: function(resource_name) {
160
    var resource = this.quotas[resource_name];
161
    var resource_meta = this.resources[resource_name];
162
    var value, limit, percentage; 
163
    
164
    limit = resource.limit;
165
    value = resource.limit - resource.available;
166
    if (value < 0 ) { value = 0 }
167
  
168
    percentage = (value/limit) * 100;
169
    if (value == 0) { percentage = 0 }
170
    if (resource.available <= 0) {
171
      percentage = 100;
172
    }
77 173

  
78
UsageClient.prototype.updateEntry = function(key, data) {
174
    if (resource_meta.unit == 'bytes') {
175
      value = humanize.filesize(value);
176
      limit = humanize.filesize(limit);
177
    }
79 178

  
80
  var entry = $('li[data-resourcekey=\''+key+'\']');
81
  var currentEl = entry.find("span.currValue");
82
  var maxEl = entry.find("span.maxValue");
83
  var ratioEl = entry.find("div.bar");
84
  var barEl = entry.find("div.bar span");
85
  var percentageEl = ratioEl.find("em");
86
  var units = entry.data("units");
87
  var infoEl = entry.find(".info");
179
    var cls = 'green';
180
    _.each(this.usage_cls_map, function(ucls, u){
181
      if (percentage >= u) {
182
        cls = ucls
183
      }
184
    })
88 185
  
89
  var current = data.currValue;
90
  var max = data.maxValue;
186
    var label_left = percentage >= 30 ? percentage - 17 : 'auto'; 
187
    var label_col = label_left == 'auto' ? 'inherit' : '#fff';
188
    percentage = humanize.numberFormat(percentage, 0);
189
    qdata = {'curr': value, 'max': limit, 'perc': percentage, 'cls': cls,
190
             'label_left': label_left, 'label_color': label_col}
191
    _.extend(qdata, resource);
192
    console.log(resource_name, value, resource);
193
    return qdata
194
  },
195

  
196
  setQuotas: function(data) {
197
    LOG("Set quotas", data);
198
    var self = this;
199
    this.quotas = data;
200
    _.each(this.quotas, function(v, k) {
201
      var r = self.resources[k];
202
      var usage = self.getUsage(k);
203
      r.usage = usage;
204
      self.resources[k].usage = usage;
205
      self.resources_ordered[r.index].usage = usage;
206
    });
207
  },
208

  
209
  _ajaxOptions: function() {
210
    var token = $.cookie(this.settings.cookie_name).split("|")[1];
211
    return {
212
      'url': this.url,
213
      'headers': {
214
        'X-Auth-Token': token
215
      },
216
    }
217
  },
91 218
  
92
  modifier = function(v) { return v; }
93
  if (units == 'bytes') {
94
      modifier = humanize.filesize;
219
  updateQuotas: function() {
220
    LOG("Updating quotas");
221
    var self = this;
222
    this.getQuotas(function(data){
223
      self.setQuotas(data.system);
224
      self.updateLayout();
225
    })
226
  },
227

  
228
  getQuotas: function(callback) {
229
    var options = this._ajaxOptions();
230
    options.success = callback;
231
    LOG("Calling quotas API", options);
232
    $.ajax(options);
95 233
  }
96

  
97
  setText(maxEl, infoEl.data('maxvalue'), max, infoEl.data('maxvalue') > max, 
98
          modifier);
99
  setText(currentEl, infoEl.data('currvalue'), current, 
100
          infoEl.data('currvalue') > current, modifier);
101 234
  
102
  var percentage = humanize.numberFormat(data.ratio, 1);
103
  setText(percentageEl, percentageEl.data('value'), 
104
          percentage, percentageEl.data('value') > percentage, 
105
          function(v) { return v + '&#37; &nbsp;&nbsp;'});
106

  
107
  var width = data.ratio;
108
  if (width > 100) { width = 100; }
109
  if (width < 0) { width = 0; }
110

  
111
  width = humanize.numberFormat(width, 1);
112
  barEl.css({'width': width + '%'});
113
  width = width*342/100;
114

  
115
  if (percentage > 22) {
116
      percentageEl.addClass("hovered"); 
117
      var left = width-percentageEl.width() -10;
118
      percentageEl.css({'left': left+"px", 'color':'#fff'});
119
  } else {
120
      percentageEl.removeClass("hovered");
121
      percentageEl.css({'left': width+"px", 'color':'#222'});
122
  }
123
  percentageEl.data('value', percentage);
124

  
125
  entry.removeClass("red green yellow");
126
  entry.addClass(data.load_class);
127

  
128
  entry.find(".info").data("currvalue", data.currValue);
129
  entry.find(".info").data("maxvalue", data.maxValue);
130
}
131

  
132
UsageClient.prototype.update = function(data) {
133
  var usage = {}, self = this;
134
  _.each(data, function(e) { usage[e.name] = e});
135

  
136
  _.each(usage, function(data, key) {
137
      self.updateEntry(key, data);
138
  });
139
}
235
});
140 236

  
141
window.UsageClient = UsageClient;
237
window.UsageView = UsageView;
142 238
})();
b/snf-astakos-app/astakos/im/templates/im/base.html
52 52
      <script src="{{ IM_STATIC_URL }}js/jquery.dropkick-1.0.0.js"></script>
53 53
      <script src="{{ IM_STATIC_URL }}js/jquery-ui-1.8.21.custom.min.js"></script>
54 54
      <script src="{{ IM_STATIC_URL }}js/jquery.tablesorter.js"></script>	 	 
55
      <script src="{{ IM_STATIC_URL }}js/jquery.cookie.js"></script>	 	 
56
      <script src="{{ IM_STATIC_URL }}js/mustache.js"></script>	 	 
55 57
      <script src="{{ IM_STATIC_URL }}js/common.js"></script> 
56 58
  {% endblock headjs %}
57 59
  {% block endhead %}{% endblock endhead %}
b/snf-astakos-app/astakos/im/templates/im/resource_usage.html
1 1
{% extends "im/account_base.html" %}
2 2

  
3
{% load filters %}
3
{% load filters astakos_tags %}
4 4

  
5 5
{% block headjs %}
6 6
{{ block.super }}
......
8 8
{% endblock %}
9 9

  
10 10
{% block page.body %}
11
<div class="maincol {% block innerpage.class %}{% endblock %}"> 
12 11

  
13
	<h2>RESOURCE USAGE</h2>
14
	<div class="stats clearfix">
15
		<ul>
16
			{% for rdata in resource_usage %}
17
            <li class="clearfix  {{ rdata.load_class }} {{ rdata.name|get_value_after_dot }}" 
18
                    data-resourcekey="{{ rdata.name }}" data-units="{{ rdata.unit }}">
19
		 			<div class="img-wrap">&nbsp;</div>
20
                    <div class="info" data-currvalue="{{ rdata.currValue }}"
21
                                      data-maxvalue="{{ rdata.maxValue }}">
22
						<h3>{{ rdata.report_desc }}</h3>
23
						<p>							
24
						{% if rdata.unit == 'bytes' %}
25
                        <span class="currValue">
26
                            {{ rdata.currValue }}
27
                        </span> out of  
28
                        <span class="maxValue">
29
                            {{ rdata.maxValue }}
30
                        </span>
31
                        {% else %}
32
                        <span class="currValue">
33
                            <span class="value">{{ rdata.currValue }}</span>
34
                        </span>
35
                        out of  
36
                        <span class="maxValue">
37
                            <span class="value">{{ rdata.maxValue }}</span>
38
                            <span class="unit">{{ rdata.unit }}</span>
39
                        </span>
40
						{% endif %}
41
                        {{rdata.pluralized_display_name}}
42
						</p>
43
					</div>
44
					<div class="bar" data-steps="">
45
						<div>
46
							
47
							<span style="width:{{ rdata.ratio_limited|floatformat }}%;" {% if rdata.ratio > 22 %}class="hovered"{% endif %}>
48
                               
49
							</span>
50
							  <em data-value="{{ rdata.ratio }}" class="value">{{ rdata.ratio|floatformat }}&#37; &nbsp;&nbsp;</em>	
51
						</div>
52
					</div>
53
		 		</li>
54
		 	{% endfor%}
55
		</ul>
56
			 
57
	</div>    
12
<script id="quotaTpl" type="text/template">
13
  {% verbatim %}
14
  {{#resources}}
15
  <li class="clearfix {{ resource_name }} {{ usage.cls }}" 
16
      data-resource="{{ name }}">
17
  <div class="img-wrap">&nbsp;</div>
18
  <div class="info" data-currvalue="" data-maxvalue="">
19
    <h3>{{ report_desc }}</h3>
20
    <p>
21
      <span class="currValue">{{ usage.curr }}</span> out of 
22
      <span class="maxValue">{{ usage.max }}</span> {{ report_desc }}
23
    </p>
24
  </div>
25
  <div class="bar">
26
    <div>
27
        <span style="width:{{ usage.perc }}%"></span>
28
        <em 
29
            class="value" 
30
            style="left: {{ usage.label_left }}%; color: {{ usage.label_color }}">
31
            {{ usage.perc }} %
32
        </em>
33
    </div>
34
  </div>
35
  </li>
36
  {{/resources}}
37
  {% endverbatim %}
38
</script>
39

  
40
<div class="maincol {% block innerpage.class %}{% endblock %}"> 
41
    <h2>RESOURCE USAGE</h2>
42
    <div id="quota-container">
43
    </div>
58 44
</div>
59 45
<script>
60
    $(document).ready(function(){
61
        var usageClient = new UsageClient({
62
            'url': '{% url resource_usage %}?json=1',
63
            'dataType': 'json',
64
            'container': 'div.stats'
65
        });
66
        
67
        window.setInterval(function() {
68
            usageClient.load();
69
        }, {{ usage_update_interval }});
70
        usageClient.load();
71
    })
46
$(document).ready(function(){
47
  var usageView = new UsageView({
48
    'url': '{% url astakos-api-quotas %}',
49
    'cookie_name': '{{ token_cookie_name|safe }}',
50
    'dataType': 'json',
51
    'container': '#quota-container',
52
    'quotas': {{ current_usage|safe }},
53
    'meta': {
54
      'resources': {{ resource_catalog|safe }},
55
      'groups': {{ resource_groups|safe }},
56
      'resources_order': {{ resources_order|safe }}
57
    }
58
  });
59
  window.usageView = usageView;
60

  
61
  window.setInterval(function(){
62
    window.usageView.updateQuotas();
63
  }, {{ usage_update_interval }});
72 64

  
65
})
73 66
</script>
74 67
{% endblock %}
b/snf-astakos-app/astakos/im/templatetags/astakos_tags.py
268 268

  
269 269
    content = render_to_string(template, tpl_context)
270 270
    return content
271

  
272

  
273
class VerbatimNode(template.Node):
274

  
275
    def __init__(self, text):
276
        self.text = text
277

  
278
    def render(self, context):
279
        return self.text
280

  
281

  
282
@register.tag
283
def verbatim(parser, token):
284
    text = []
285
    while 1:
286
        token = parser.tokens.pop(0)
287
        if token.contents == 'endverbatim':
288
            break
289
        if token.token_type == template.TOKEN_VAR:
290
            text.append('{{')
291
        elif token.token_type == template.TOKEN_BLOCK:
292
            text.append('{%')
293
        text.append(token.contents)
294
        if token.token_type == template.TOKEN_VAR:
295
            text.append('}}')
296
        elif token.token_type == template.TOKEN_BLOCK:
297
            text.append('%}')
298
    return VerbatimNode(''.join(text))
b/snf-astakos-app/astakos/im/views.py
116 116
from astakos.im import auth_providers as auth
117 117
from snf_django.lib.db.transaction import commit_on_success_strict
118 118
from astakos.im.ctx import ExceptionHandler
119
from astakos.im import quotas
119 120

  
120 121
logger = logging.getLogger(__name__)
121 122

  
......
862 863
@valid_astakos_user_required
863 864
def resource_usage(request):
864 865

  
865
    def with_class(entry):
866
         entry['load_class'] = 'red'
867
         max_value = float(entry['maxValue'])
868
         curr_value = float(entry['currValue'])
869
         entry['ratio_limited']= 0
870
         if max_value > 0 :
871
             entry['ratio'] = (curr_value / max_value) * 100
872
         else:
873
             entry['ratio'] = 0
874
         if entry['ratio'] < 66:
875
             entry['load_class'] = 'yellow'
876
         if entry['ratio'] < 33:
877
             entry['load_class'] = 'green'
878
         if entry['ratio']<0:
879
             entry['ratio'] = 0
880
         if entry['ratio']>100:
881
             entry['ratio_limited'] = 100
882
         else:
883
             entry['ratio_limited'] = entry['ratio']
884
         return entry
885

  
886
    def pluralize(entry):
887
        entry['plural'] = engine.plural(entry.get('name'))
888
        return entry
889

  
890
    resource_usage = None
891
    result = callpoint.get_user_usage(request.user.id)
892
    if result.is_success:
893
        resource_usage = result.data
894
        backenddata = map(with_class, result.data)
895
        backenddata = map(pluralize , backenddata)
896
    else:
897
        messages.error(request, result.reason)
898
        backenddata = []
899
        resource_usage = []
900

  
901
    if request.REQUEST.get('json', None):
902
        return HttpResponse(json.dumps(backenddata),
903
                            mimetype="application/json")
866
    current_usage = quotas.get_user_quotas(request.user)
867
    current_usage = json.dumps(current_usage['system'])
868
    resource_catalog, resource_groups = _resources_catalog(request)
869
    resource_catalog = json.dumps(resource_catalog)
870
    resource_groups = json.dumps(resource_groups)
871
    resources_order = json.dumps(presentation.RESOURCES.get('resources_order'))
904 872

  
905 873
    return render_response('im/resource_usage.html',
906 874
                           context_instance=get_context(request),
907
                           resource_usage=backenddata,
908
                           usage_update_interval=astakos_settings.USAGE_UPDATE_INTERVAL,
909
                           result=result)
875
                           resource_catalog=resource_catalog,
876
                           resource_groups=resource_groups,
877
                           resources_order=resources_order,
878
                           current_usage=current_usage,
879
                           token_cookie_name=astakos_settings.COOKIE_NAME,
880
                           usage_update_interval=
881
                           astakos_settings.USAGE_UPDATE_INTERVAL)
882

  
910 883

  
911 884
# TODO: action only on POST and user should confirm the removal
912 885
@require_http_methods(["GET", "POST"])

Also available in: Unified diff