/yourtopia/static/src/model.js

https://github.com/okfn/yourtopia · JavaScript · 250 lines · 162 code · 16 blank · 72 comment · 30 complexity · f2fc286faf52fa8e5072ba8793d0f83b MD5 · raw file

  1. window.YOURTOPIA = window.YOURTOPIA || {};
  2. YOURTOPIA.Model = function($) {
  3. var my = {};
  4. // make model capable of
  5. Backbone.Model.prototype.setByName = function(key, index, value) {
  6. this.set(key[index], value);
  7. };
  8. /**
  9. * Series
  10. *
  11. * Model representing a data series like e.g. "employment rate"
  12. * with respect to a certain time frame, country and region.
  13. */
  14. /* unused
  15. my.Series = Backbone.Model.extend({
  16. defaults: {
  17. title: null,
  18. description: null,
  19. // TODO: year, country, region, value, normalized_value
  20. data: []
  21. },
  22. initialize: function() {}
  23. });
  24. */
  25. /*
  26. // not sure if needed
  27. my.SeriesList = Backbone.Collection.extend({
  28. model: my.Series
  29. });
  30. */
  31. /**
  32. * Weights: User-defined weights for criteria
  33. *
  34. * User-created weights of certain categories, plus
  35. * user name, description text and creation date.
  36. */
  37. my.Weights = Backbone.Model.extend({
  38. defaults: {
  39. id: null,
  40. country: null,
  41. version: null,
  42. user_name: null,
  43. user_url: null,
  44. user_ip: null,
  45. description: null,
  46. created_at: null,
  47. weighted_measures: null
  48. },
  49. /**
  50. * If parameter data is given, the model is pre-populated
  51. */
  52. initialize: function(data) {
  53. this.set({
  54. weighted_measures: YOURTOPIA.config.indicator_categories,
  55. country: YOURTOPIA.config.country,
  56. version: YOURTOPIA.config.version
  57. });
  58. // create weight attributes according to configuration
  59. for (var c in YOURTOPIA.config.indicator_categories) {
  60. var category = YOURTOPIA.config.indicator_categories[c];
  61. this.set(category + '_weight', YOURTOPIA.config.category_value_default);
  62. }
  63. // in case we have a 'weights' attribute (which means we got
  64. // this pre-populated) we fill the values in the right places
  65. if (this.has('weights')) {
  66. var weights = JSON.parse(this.get('weights'));
  67. for (var w in weights) {
  68. this.set(w, weights[w]);
  69. }
  70. this.unset('weights');
  71. }
  72. }
  73. });
  74. /**
  75. * Calculated Index data series for one region
  76. * over time
  77. */
  78. my.SingleRegionTimeSeries = Backbone.Model.extend({
  79. defaults: {
  80. region_label: null,
  81. from_year: null,
  82. to_year: null,
  83. values: {},
  84. years: []
  85. },
  86. /**
  87. * Pass objects as parameter with year and value property
  88. * to populate the model
  89. */
  90. setYearValue: function(year, value) {
  91. var self = this;
  92. //console.log(self, self.id, year, value);
  93. if (self.get('from_year') === null || self.get('from_year') > year) {
  94. self.set({from_year: year}, {silent: true});
  95. }
  96. if (self.get('to_year') === null || self.get('to_year') < year) {
  97. self.set({to_year: year}, {silent: true});
  98. }
  99. var values = self.get('values');
  100. var years = self.get('years');
  101. if (typeof values[year] == 'undefined') {
  102. values[year] = value;
  103. years.push(year);
  104. }
  105. self.set('values', values, {silent: true});
  106. self.set('years', years, {silent: true});
  107. },
  108. setYearValues: function(vals) {
  109. var keys = [];
  110. for (var key in vals) {
  111. keys.push(key);
  112. }
  113. this.set('values', vals, {silent: true});
  114. this.set('years', keys, {silent: true});
  115. }
  116. });
  117. /**
  118. * Region index result data collection
  119. */
  120. my.AllRegionsTimeSeries = Backbone.Collection.extend({
  121. model: my.RegionTimeSeries,
  122. sourceData: null,
  123. sourceMetaData: null,
  124. weights: null,
  125. from_year: null,
  126. to_year: null,
  127. /**
  128. * Create collection items pased on the regions in d
  129. */
  130. setSourceData: function(d) {
  131. var self = this;
  132. this.sourceData = d;
  133. //console.log(d);
  134. // create an entry for each region
  135. for (var r in d.regions) {
  136. var srts = new my.SingleRegionTimeSeries({
  137. id: r,
  138. region_label: d.regions[r]
  139. });
  140. self.push(srts, {silent: true});
  141. }
  142. // store the lowest and highest year in the data
  143. var lowest_year = 1000000;
  144. var highest_year = 0;
  145. for (var s in d.series) {
  146. for (var c in d.series[s]) {
  147. for (var rg in d.series[s][c]) {
  148. for (var y in d.series[s][c][rg]) {
  149. y = parseInt(y, 10);
  150. if (y > highest_year) {
  151. highest_year = y;
  152. }
  153. if (y < lowest_year) {
  154. lowest_year = y;
  155. }
  156. }
  157. }
  158. }
  159. }
  160. self.from_year = lowest_year;
  161. self.to_year = highest_year;
  162. this.update();
  163. },
  164. setSourceMetaData: function(d) {
  165. this.sourceMetaData = d;
  166. this.update();
  167. },
  168. setWeights: function(w) {
  169. this.weights = w;
  170. this.update();
  171. },
  172. /**
  173. * This calcualates the weighted index values.
  174. */
  175. update: function() {
  176. //console.log('AllRegionsTimeSeries.update() called!');
  177. if (this.sourceData === null || this.sourceMetaData === null || this.weights === null) {
  178. // too early for this.
  179. //console.log('Exiting AllRegionsTimeSeries.update() - incomplete data');
  180. return;
  181. }
  182. var self = this;
  183. var categories = this.weights.get('weighted_measures');
  184. // iterate regions
  185. //this.each(function(region_timeseries, num){
  186. for (var num=0; num < self.length; num++) {
  187. var region_timeseries = self.at(num);
  188. var region_code = region_timeseries.id;
  189. var values_per_year = {};
  190. //console.log(num, region_code);
  191. // iterate years
  192. for (var y=self.from_year; y<=self.to_year; y++) {
  193. //console.log(num, region_code, y);
  194. var index_value = 0.0;
  195. var num_index_values = 0;
  196. // iterate categories
  197. for (var c in categories) {
  198. var category_weight = self.weights.get(categories[c] + '_weight');
  199. for (var series_id in self.sourceData.series[categories[c]]) {
  200. if (typeof self.sourceData.series[categories[c]][series_id][region_code] !== 'undefined' &&
  201. typeof self.sourceData.series[categories[c]][series_id][region_code][y] !== 'undefined') {
  202. var value = self.sourceData.series[categories[c]][series_id][region_code][y].value_normalized;
  203. if (typeof value === 'number') {
  204. //console.log(num, region_code, y, series_id, value);
  205. var subWeight = 1.0; // default single measure factor
  206. if (self.weights.has(series_id + '_weight')) {
  207. subWeight = self.weights.get(series_id + '_weight');
  208. }
  209. //console.log(value, subWeight, category_weight, self.sourceMetaData[series_id].high_is_good);
  210. if (self.sourceMetaData[series_id].high_is_good) {
  211. index_value += value * subWeight * category_weight;
  212. } else {
  213. index_value += (1 - value) * subWeight * category_weight;
  214. }
  215. num_index_values++;
  216. }
  217. }
  218. }
  219. }
  220. var final_year_region_value = index_value / num_index_values;
  221. //console.log(region_timeseries, region_code, y, final_year_region_value);
  222. // finally store value for this year
  223. region_timeseries.setYearValue(y, final_year_region_value);
  224. values_per_year[y] = final_year_region_value;
  225. }
  226. region_timeseries.setYearValues(values_per_year);
  227. //console.log(region_code, values_per_year);
  228. }
  229. // finally inform all who care about the update
  230. this.trigger('change');
  231. }
  232. });
  233. return my;
  234. }(jQuery);