PageRenderTime 83ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/client/static/js/models/datasets.js

https://github.com/pandaproject/panda
JavaScript | 547 lines | 393 code | 86 blank | 68 comment | 78 complexity | d3f43fefc3ad0818506eb3505d8b0a78 MD5 | raw file
  1. PANDA.models.Dataset = Backbone.Model.extend({
  2. /*
  3. * Equivalent of panda.models.Dataset.
  4. */
  5. urlRoot: PANDA.API + "/dataset",
  6. categories: null,
  7. creator: null,
  8. data_uploads: null,
  9. related_uploads: null,
  10. current_task: null,
  11. data: null,
  12. initialize: function(attributes) {
  13. if (attributes) {
  14. if ("categories" in attributes) {
  15. this.categories = new PANDA.collections.Categories(attributes.categories);
  16. } else {
  17. this.categories = new PANDA.collections.Categories();
  18. }
  19. if ("creator" in attributes) {
  20. this.creator = new PANDA.models.User(attributes.creator);
  21. }
  22. if ("current_task" in attributes) {
  23. this.current_task = new PANDA.models.Task(attributes.current_task);
  24. }
  25. if ("data_uploads" in attributes) {
  26. this.data_uploads = new PANDA.collections.DataUploads(attributes.data_uploads);
  27. } else {
  28. this.data_uploads = new PANDA.collections.DataUploads();
  29. }
  30. if ("related_uploads" in attributes) {
  31. this.related_uploads = new PANDA.collections.RelatedUploads(attributes.related_uploads);
  32. } else {
  33. this.related_uploads = new PANDA.collections.RelatedUploads();
  34. }
  35. }
  36. this.data = new PANDA.collections.Data();
  37. },
  38. parse: function(response) {
  39. /*
  40. * Extract embedded models from serialized data.
  41. */
  42. this.categories = new PANDA.collections.Categories(response.categories);
  43. if (response.creator != null) {
  44. this.creator = new PANDA.models.User(response.creator);
  45. }
  46. if (response.current_task != null) {
  47. this.current_task = new PANDA.models.Task(response.current_task);
  48. }
  49. this.data_uploads = new PANDA.collections.DataUploads(response.data_uploads);
  50. this.related_uploads = new PANDA.collections.RelatedUploads(response.related_uploads);
  51. delete response["categories"];
  52. delete response["creator"];
  53. delete response["current_task"];
  54. delete response["data_uploads"];
  55. delete response["related_uploads"];
  56. // Does this dataset have embedded search results?
  57. if (response.objects != null) {
  58. this.data.add(this.data.parse(response));
  59. delete response["meta"];
  60. delete response["objects"];
  61. }
  62. return response
  63. },
  64. toJSON: function(full) {
  65. /*
  66. * Append embedded models to serialized data.
  67. *
  68. * NOTE: never serialize embedded data from search results.
  69. */
  70. var js = Backbone.Model.prototype.toJSON.call(this);
  71. if (full) {
  72. js['categories'] = this.categories.toJSON();
  73. } else {
  74. js['categories'] = this.categories.map(function(cat) { return cat.id });
  75. }
  76. if (this.creator != null) {
  77. if (full) {
  78. js['creator'] = this.creator.toJSON();
  79. } else {
  80. js['creator'] = this.creator.id;
  81. }
  82. } else {
  83. js['creator'] = null;
  84. }
  85. if (this.current_task != null) {
  86. if (full) {
  87. js['current_task'] = this.current_task.toJSON();
  88. } else {
  89. js['current_task'] = this.current_task.id;
  90. }
  91. } else {
  92. js['current_task'] = null;
  93. }
  94. if (full) {
  95. js['data_uploads'] = this.data_uploads.toJSON();
  96. } else {
  97. js['data_uploads'] = this.data_uploads.map(function(data_uploads) { return data_uploads.id });
  98. }
  99. if (full) {
  100. js['related_uploads'] = this.related_uploads.toJSON();
  101. } else {
  102. js['related_uploads'] = this.related_uploads.map(function(related_uploads) { return related_uploads.id });
  103. }
  104. return js
  105. },
  106. results: function() {
  107. /*
  108. * Render this object with embedded search results data for templating.
  109. */
  110. var results = this.toJSON(true);
  111. _.extend(results, this.data.results());
  112. return results;
  113. },
  114. import_data: function(data_uploads_id, success_callback, error_callback) {
  115. /*
  116. * Kick off the dataset import and update the model with
  117. * the task id and status.
  118. *
  119. * NB: Runs synchronously.
  120. */
  121. Redd.ajax({
  122. url: this.url() + "import/" + data_uploads_id + "/",
  123. async: false,
  124. dataType: 'json',
  125. success: _.bind(function(response) {
  126. this.set(response);
  127. if (success_callback) {
  128. success_callback(this);
  129. }
  130. }, this),
  131. error: function(xhr, textStatus) {
  132. if (error_callback) {
  133. if (xhr.getResponseHeader("content-type") == "application/json") {
  134. error = JSON.parse(xhr.responseText);
  135. } else {
  136. error = { error_message: xhr.statusText }
  137. console.log(xhr)
  138. }
  139. error_callback(this, error);
  140. }
  141. }
  142. });
  143. },
  144. reindex_data: function(indexed, column_types, success_callback, error_callback) {
  145. /*
  146. * Kick off the dataset reindexing and update the model with
  147. * the task id and status.
  148. *
  149. * NB: Runs synchronously.
  150. */
  151. data = {
  152. typed_columns: typed_columns.join(','),
  153. column_types: column_types.join(',')
  154. };
  155. Redd.ajax({
  156. url: this.url() + "reindex/",
  157. async: false,
  158. dataType: 'json',
  159. data: data,
  160. success: _.bind(function(response) {
  161. this.set(response);
  162. if (success_callback) {
  163. success_callback(this);
  164. }
  165. }, this),
  166. error: function(xhr, textStatus) {
  167. error = JSON.parse(xhr.responseText);
  168. if (error_callback) {
  169. error_callback(this, error);
  170. }
  171. }
  172. });
  173. },
  174. export_data: function(query, since, success_callback, error_callback) {
  175. /*
  176. * Kick off the dataset export and update the model with
  177. * the task id and status.
  178. *
  179. * NB: Uses the "single dataset" export url, resulting in a CSV.
  180. */
  181. data = {};
  182. if (query) {
  183. data['q'] = query;
  184. }
  185. if (since != "all") {
  186. data['since'] = since;
  187. }
  188. Redd.ajax({
  189. url: this.url() + "export/",
  190. dataType: 'json',
  191. data: data,
  192. success: _.bind(function(response) {
  193. this.set(response);
  194. if (success_callback) {
  195. success_callback(this);
  196. }
  197. }, this),
  198. error: function(xhr, textStatus) {
  199. var error = JSON.parse(xhr.responseText);
  200. if (error_callback) {
  201. error_callback(this, error);
  202. }
  203. }
  204. });
  205. },
  206. patch: function(attributes, success_callback, error_callback) {
  207. /*
  208. * Update a dataset in place using the PATCH verb.
  209. *
  210. * A special-case for the dataset edit page so that readonly attributes
  211. * are not lost.
  212. */
  213. this.set(attributes || {});
  214. Redd.ajax({
  215. url: this.url() + "?patch=true",
  216. type: "PUT",
  217. data: JSON.stringify(this.toJSON()),
  218. contentType: "application/json",
  219. dataType: "json",
  220. async: false,
  221. success: _.bind(function(response) {
  222. this.set(response);
  223. if (success_callback) {
  224. success_callback(this);
  225. }
  226. }, this),
  227. error: _.bind(function(xhr, status, error) {
  228. if (error_callback) {
  229. error_callback(this, xhr.responseText);
  230. }
  231. }, this)
  232. });
  233. },
  234. search: function(query, since, limit, page, success_callback, error_callback) {
  235. /*
  236. * Query the dataset search endpoint.
  237. */
  238. if (limit) {
  239. this.data.meta.limit = limit;
  240. } else {
  241. this.data.meta.limit = PANDA.settings.DEFAULT_SEARCH_ROWS;
  242. }
  243. if (page) {
  244. this.data.meta.page = page;
  245. this.data.meta.offset = this.data.meta.limit * (this.data.meta.page - 1);
  246. } else {
  247. this.data.meta.page = 1;
  248. this.data.meta.offset = 0;
  249. }
  250. data = {
  251. q: query,
  252. limit: this.data.meta.limit,
  253. offset: this.data.meta.offset
  254. }
  255. if (since != "all") {
  256. data.since = since;
  257. }
  258. Redd.ajax({
  259. url: PANDA.API + "/dataset/" + this.get("slug") + "/data/",
  260. dataType: 'json',
  261. data: data,
  262. success: _.bind(function(response) {
  263. this.process_search_results(response);
  264. if (success_callback) {
  265. success_callback(this);
  266. }
  267. }, this),
  268. error: function(xhr, textStatus) {
  269. var error = JSON.parse(xhr.responseText);
  270. if (error_callback) {
  271. error_callback(this, error);
  272. }
  273. }
  274. });
  275. },
  276. process_search_results: function(response) {
  277. var objs = this.data.parse(response);
  278. delete response["meta"];
  279. delete response["objects"];
  280. this.set(response);
  281. this.data.reset(objs);
  282. }
  283. });
  284. PANDA.collections.Datasets = Backbone.Collection.extend({
  285. /*
  286. * A collection of PANDA.models.Dataset equivalents.
  287. */
  288. model: PANDA.models.Dataset,
  289. urlRoot: PANDA.API + "/dataset",
  290. meta: {
  291. page: 1,
  292. limit: PANDA.settings.DEFAULT_SEARCH_GROUPS,
  293. offset: 0,
  294. next: null,
  295. previous: null,
  296. total_count: 0
  297. },
  298. parse: function(response) {
  299. /*
  300. Parse page metadata in addition to objects.
  301. */
  302. this.meta = response.meta;
  303. this.meta.page = Math.floor(this.meta.offset / this.meta.limit) + 1;
  304. return response.objects;
  305. },
  306. search: function(category, query, since, limit, page, success_callback, error_callback) {
  307. /*
  308. * Query the data search endpoint.
  309. */
  310. if (limit) {
  311. this.meta.limit = limit;
  312. } else {
  313. this.meta.limit = PANDA.settings.DEFAULT_SEARCH_GROUPS;
  314. }
  315. if (page) {
  316. this.meta.page = page;
  317. this.meta.offset = this.meta.limit * (this.meta.page - 1);
  318. } else {
  319. this.meta.page = 1;
  320. this.meta.offset = 0;
  321. }
  322. data = {
  323. q: query,
  324. limit: this.meta.limit,
  325. offset: this.meta.offset
  326. }
  327. if (since != "all") {
  328. data.since = since;
  329. }
  330. if (category != "all") {
  331. data.category = category;
  332. }
  333. Redd.ajax({
  334. url: PANDA.API + "/data/",
  335. dataType: 'json',
  336. data: data,
  337. success: _.bind(function(response) {
  338. this.process_search_results(response);
  339. if (success_callback) {
  340. success_callback(this);
  341. }
  342. }, this),
  343. error: function(xhr, textStatus) {
  344. var error = JSON.parse(xhr.responseText);
  345. if (error_callback) {
  346. error_callback(this, error);
  347. }
  348. }
  349. });
  350. },
  351. process_search_results: function(response) {
  352. /*
  353. * Process global search results from the server.
  354. */
  355. var objs = this.parse(response);
  356. datasets = _.map(objs, function(obj) {
  357. d = new PANDA.models.Dataset();
  358. d.set(d.parse(obj));
  359. return d;
  360. });
  361. this.reset(datasets);
  362. },
  363. search_meta: function(category, query, limit, page, success_callback, error_callback) {
  364. /*
  365. * Query the metadata search endpoint. By default, returns everything.
  366. *
  367. * Note: Models returned from this are not complete.
  368. */
  369. if (limit) {
  370. this.meta.limit = limit;
  371. }
  372. if (page) {
  373. this.meta.page = page;
  374. this.meta.offset = this.meta.limit * (this.meta.page - 1);
  375. }
  376. var data = {
  377. offset: this.meta.offset,
  378. simple: "true"
  379. };
  380. if (query) {
  381. data['q'] = query;
  382. }
  383. if (limit) {
  384. data['limit'] = limit;
  385. }
  386. if (category) {
  387. data["category"] = category;
  388. }
  389. Redd.ajax({
  390. url: PANDA.API + "/dataset/",
  391. dataType: "json",
  392. data: data,
  393. success: _.bind(function(response) {
  394. this.process_search_meta_results(response);
  395. if (success_callback) {
  396. success_callback(this);
  397. }
  398. }, this),
  399. error: function(xhr, textStatus) {
  400. var error = JSON.parse(xhr.responseText);
  401. if (error_callback) {
  402. error_callback(this, error);
  403. }
  404. }
  405. });
  406. },
  407. process_search_meta_results: function(response) {
  408. var objs = this.parse(response);
  409. datasets = _.map(objs, function(obj) {
  410. d = new PANDA.models.Dataset();
  411. d.set(d.parse(obj));
  412. return d;
  413. });
  414. this.reset(datasets);
  415. },
  416. export_data: function(category, query, since, success_callback, error_callback) {
  417. /*
  418. * Kick off a search export.
  419. *
  420. * NB: Uses the cross-dataset export url, resulting in a ZIP file.
  421. */
  422. data = {
  423. "export": true
  424. };
  425. if (query) {
  426. data.q = query;
  427. }
  428. if (since != "all") {
  429. data.since = since;
  430. }
  431. if (category != "all") {
  432. data.category = category;
  433. }
  434. Redd.ajax({
  435. url: PANDA.API + "/data/",
  436. dataType: 'json',
  437. data: data,
  438. success: _.bind(function(response) {
  439. if (success_callback) {
  440. success_callback(this);
  441. }
  442. }, this),
  443. error: function(xhr, textStatus) {
  444. var error = JSON.parse(xhr.responseText);
  445. if (error_callback) {
  446. error_callback(this, error);
  447. }
  448. }
  449. });
  450. },
  451. results: function() {
  452. /*
  453. * Grab the current data in a simplified data structure appropriate
  454. * for templating.
  455. */
  456. return {
  457. meta: this.meta,
  458. datasets: _.invoke(this.models, "results")
  459. }
  460. }
  461. });