1 /* ---------------------------------------------------------------------------
2 # Copyright (C) 2008 Georgia Public Library Service
3 # Bill Erickson <erickson@esilibrary.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # --------------------------------------------------------------------------- */
15 dojo.require("dojo.parser");
16 dojo.require("dojo.io.iframe");
17 dojo.require("dijit.ProgressBar");
18 dojo.require("dijit.form.Button");
19 dojo.require("dijit.form.FilteringSelect");
20 dojo.require("dijit.layout.ContentPane");
21 dojo.require("dijit.layout.TabContainer");
22 dojo.require("dojo.cookie");
23 dojo.require("dojox.grid.Grid");
24 dojo.require("dojo.data.ItemFileReadStore");
25 dojo.require('dojo.date.locale');
26 dojo.require('dojo.date.stamp');
27 dojo.require("fieldmapper.Fieldmapper");
28 dojo.require("fieldmapper.dojoData");
29 dojo.require('openils.CGI');
30 dojo.require('openils.User');
31 dojo.require('openils.Event');
34 'vl-generic-progress',
35 'vl-generic-progress-with-total',
40 'vl-queue-select-div',
41 'vl-marc-upload-status-div'
45 var VANDELAY_URL = '/vandelay-upload';
47 var authAttrDefs = [];
48 var queuedRecords = [];
49 var queuedRecordsMap = {};
50 var bibAttrsFetched = false;
51 var authAttrsFetched = false;
52 var attrDefMap = {}; // maps attr def code names to attr def ids
54 var currentQueueId = null;
56 var currentMatchedRecords; // set of loaded matched bib records
57 var currentOverlayRecordsMap; // map of import record to overlay record
58 var currentImportRecId; // when analyzing matches, this is the current import record
61 var selectableGridRecords;
62 var cgi = new openils.CGI();
68 authtoken = dojo.cookie('ses') || cgi.param('ses');
69 var initNeeded = 4; // how many async responses do we need before we're init'd
70 var initCount = 0; // how many async reponses we've received
72 function checkInitDone() {
74 if(initCount == initNeeded)
78 // Fetch the bib and authority attribute definitions
79 fieldmapper.standardRequest(
80 ['open-ils.permacrud', 'open-ils.permacrud.search.vqbrad'],
82 params: [authtoken, {id:{'!=':null}}],
83 onresponse: function(r) {
84 var def = r.recv().content();
85 if(e = openils.Event.parse(def[0]))
87 bibAttrDefs.push(def);
89 oncomplete: function() {
90 bibAttrDefs = bibAttrDefs.sort(
92 if(a.id() > b.id()) return 1;
93 if(a.id() < b.id()) return -1;
102 fieldmapper.standardRequest(
103 ['open-ils.permacrud', 'open-ils.permacrud.search.vqarad'],
105 params: [authtoken, {id:{'!=':null}}],
106 onresponse: function(r) {
107 var def = r.recv().content();
108 if(e = openils.Event.parse(def[0]))
110 authAttrDefs.push(def);
112 oncomplete: function() {
113 authAttrDefs = authAttrDefs.sort(
115 if(a.id() > b.id()) return 1;
116 if(a.id() < b.id()) return -1;
125 fieldmapper.standardRequest(
126 ['open-ils.vandelay', 'open-ils.vandelay.bib_queue.owner.retrieve.atomic'],
129 oncomplete: function(r) {
130 var list = r.recv().content();
131 if(e = openils.Event.parse(list[0]))
133 userBibQueues = list;
139 fieldmapper.standardRequest(
140 ['open-ils.vandelay', 'open-ils.vandelay.authority_queue.owner.retrieve.atomic'],
143 oncomplete: function(r) {
144 var list = r.recv().content();
145 if(e = openils.Event.parse(list[0]))
147 userAuthQueues = list;
154 function displayGlobalDiv(id) {
155 for(var i = 0; i < globalDivs.length; i++) {
157 dojo.style(dojo.byId(globalDivs[i]), 'display', 'none');
159 alert('please define div ' + globalDivs[i]);
162 dojo.style(dojo.byId(id),'display','block');
165 function runStartupCommands() {
166 currentQueueId = cgi.param('qid');
167 currentType = cgi.param('qtype');
169 return retrievenueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
174 * asynchronously upload a file of MARC records
176 function uploadMARC(onload){
177 dojo.byId('vl-ses-input').value = authtoken;
178 displayGlobalDiv('vl-marc-upload-status-div');
179 dojo.io.iframe.send({
183 form: dojo.byId('vl-marc-upload-form'),
184 handle: function(data,ioArgs){
185 var content = data.documentElement.textContent;
192 * Creates a new vandelay queue
194 function createQueue(queueName, type, onload) {
195 fieldmapper.standardRequest(
196 ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.create'],
198 params: [authtoken, queueName, null, type],
199 oncomplete : function(r) {
200 var queue = r.recv().content();
201 if(e = openils.Event.parse(queue))
210 * Tells vendelay to pull a batch of records from the cache and explode them
211 * out into the vandelay tables
213 function processSpool(key, queueId, type, onload) {
214 fieldmapper.standardRequest(
215 ['open-ils.vandelay', 'open-ils.vandelay.'+type+'.process_spool'],
217 params: [authtoken, key, queueId],
218 oncomplete : function(r) {
219 var resp = r.recv().content();
220 if(e = openils.Event.parse(resp))
228 function retrieveQueuedRecords(type, queueId, onload) {
229 displayGlobalDiv('vl-generic-progress');
231 queuedRecordsMap = {};
232 currentOverlayRecordsMap = {};
233 selectableGridRecords = {};
234 resetVlQueueGridLayout();
236 fieldmapper.standardRequest(
237 ['open-ils.vandelay', 'open-ils.vandelay.'+type+'_queue.records.retrieve.atomic'],
239 params: [authtoken, queueId, {clear_marc:1}],
240 /* intermittent bug in streaming, multipart requests prevents use of onreponse for now...
241 onresponse: function(r) {
242 var rec = r.recv().content();
243 if(e = openils.Event.parse(rec))
245 queuedRecords.push(rec);
246 queuedRecordsMap[rec.id()] = rec;
249 oncomplete: function(r){
250 var recs = r.recv().content();
251 if(e = openils.Event.parse(recs[0]))
253 for(var i = 0; i < recs.length; i++) {
255 queuedRecords.push(rec);
256 queuedRecordsMap[rec.id()] = rec;
264 function vlLoadMatchUI(recId, attrCode) {
265 displayGlobalDiv('vl-generic-progress');
266 var matches = getRecMatchesFromAttrCode(queuedRecordsMap[recId], attrCode);
268 currentImportRecId = recId;
269 for(var i = 0; i < matches.length; i++)
270 records.push(matches[i].eg_record());
272 var retrieve = ['open-ils.search', 'open-ils.search.biblio.record_entry.slim.retrieve'];
273 var params = [records];
274 if(currentType == 'auth') {
275 retrieve = ['open-ils.cat', 'open-ils.cat.authority.record.retrieve'];
276 parmas = [authtoken, records, {clear_marc:1}];
279 fieldmapper.standardRequest(
283 oncomplete: function(r) {
284 var recs = r.recv().content();
285 if(e = openils.Event.parse(recs))
289 displayGlobalDiv('vl-match-div');
290 resetVlMatchGridLayout();
291 currentMatchedRecords = recs;
292 vlMatchGrid.setStructure(vlMatchGridLayout);
294 // build the data store or records with match information
295 var dataStore = bre.toStoreData(recs, null, {virtualFields:['field_type']});
296 for(var i = 0; i < dataStore.items.length; i++) {
297 var item = dataStore.items[i];
298 for(var j = 0; j < matches.length; j++) {
299 var match = matches[j];
300 if(match.eg_record() == item.id)
301 item.field_type = match.field_type();
304 // now populate the grid
305 vlPopulateGrid(vlMatchGrid, dataStore);
311 function vlPopulateGrid(grid, data) {
312 var store = new dojo.data.ItemFileReadStore({data:data});
313 var model = new dojox.grid.data.DojoData(
314 null, store, {rowsPerPage: 100, clientSort: true, query:{id:'*'}});
315 grid.setModel(model);
320 function vlLoadMARCHtml(recId) {
321 displayGlobalDiv('vl-generic-progress');
322 var api = ['open-ils.search', 'open-ils.search.biblio.record.html'];
323 if(currentType == 'auth')
324 api = ['open-ils.search', 'open-ils.search.authority.to_html'];
325 fieldmapper.standardRequest(
329 oncomplete: function(r) {
330 displayGlobalDiv('vl-match-html-div');
331 var html = r.recv().content();
332 dojo.byId('vl-match-record-html').innerHTML = html;
340 * Given a record, an attribute definition code, and a matching record attribute,
341 * this will determine if there are any import matches and build the UI to
342 * represent those matches. If no matches exist, simply returns the attribute value
344 function buildAttrColumnUI(rec, attrCode, attr) {
345 var matches = getRecMatchesFromAttrCode(rec, attrCode);
346 if(matches.length > 0) { // found some matches
347 return '<div class="match_div">' +
348 '<a href="javascript:void(0);" onclick="vlLoadMatchUI('+
349 rec.id()+',\''+attrCode+'\');">'+
350 attr.attr_value() + ' ('+matches.length+')</a></div>';
353 return attr.attr_value();
356 function getRecMatchesFromAttrCode(rec, attrCode) {
358 var attr = getRecAttrFromCode(rec, attrCode);
359 for(var j = 0; j < rec.matches().length; j++) {
360 var match = rec.matches()[j];
361 if(match.matched_attr() == attr.id())
367 function getRecAttrFromCode(rec, attrCode) {
368 var defId = attrDefMap[attrCode];
369 var attrs = rec.attributes();
370 for(var i = 0; i < attrs.length; i++) {
372 if(attr.field() == defId)
378 function getAttrValue(rowIdx) {
379 var data = this.grid.model.getRow(rowIdx);
381 var attrCode = this.field.split('.')[1];
382 var rec = queuedRecordsMap[data.id];
383 var attr = getRecAttrFromCode(rec, attrCode);
385 return buildAttrColumnUI(rec, attrCode, attr);
389 function vlGetDateTimeField(rowIdx) {
390 data = this.grid.model.getRow(rowIdx);
392 if(!data[this.field]) return '';
393 var date = dojo.date.stamp.fromISOString(data[this.field]);
394 return dojo.date.locale.format(date, {selector:'date'});
397 function vlGetCreator(rowIdx) {
398 data = this.grid.model.getRow(rowIdx);
400 var id = data.creator;
402 return userCache[id].usrname();
403 var user = fieldmapper.standardRequest(
404 ['open-ils.actor', 'open-ils.actor.user.retrieve'], [authtoken, id]);
405 if(e = openils.Event.parse(user))
407 userCache[id] = user;
408 return user.usrname();
411 function vlGetViewMARC(rowIdx) {
412 data = this.grid.model.getRow(rowIdx);
414 return this.value.replace('RECID', data.id);
417 function vlGetOverlayTargetSelector(rowIdx) {
418 data = this.grid.model.getRow(rowIdx);
420 var value = this.value.replace('ID', data.id);
421 var overlay = currentOverlayRecordsMap[currentImportRecId];
422 if(overlay && overlay == data.id)
423 value = value.replace('/>', 'checked="checked"/>');
429 * see if the user has enabled overlays for the current match set and,
430 * if so, map the current import record to the overlay target.
432 function vlHandleOverlayTargetSelected() {
433 if(vlOverlayTargetEnable.checked) {
434 for(var i = 0; i < currentMatchedRecords.length; i++) {
435 var matchRecId = currentMatchedRecords[i].id();
436 if(dojo.byId('vl-overlay-target-'+matchRecId).checked) {
437 console.log("found overlay target " + matchRecId);
438 currentOverlayRecordsMap[currentImportRecId] = matchRecId;
439 dojo.byId('vl-record-list-selected-' + currentImportRecId).checked = true;
440 dojo.byId('vl-record-list-selected-' + currentImportRecId).parentNode.className = 'overlay_selected';
445 delete currentOverlayRecordsMap[currentImportRecId];
446 dojo.byId('vl-record-list-selected-' + currentImportRecId).checked = false;
450 function buildRecordGrid(type) {
451 displayGlobalDiv('vl-queue-div');
453 currentOverlayRecordsMap = {};
455 if(queuedRecords.length == 0) {
456 dojo.style(dojo.byId('vl-queue-no-records'), 'display', 'block');
457 dojo.style(dojo.byId('vl-queue-div-grid'), 'display', 'none');
460 dojo.style(dojo.byId('vl-queue-no-records'), 'display', 'none');
461 dojo.style(dojo.byId('vl-queue-div-grid'), 'display', 'block');
464 var defs = (type == 'bib') ? bibAttrDefs : authAttrDefs;
465 for(var i = 0; i < defs.length; i++) {
467 attrDefMap[def.code()] = def.id();
469 name:def.description(),
470 field:'attr.' + def.code(),
473 //if(def.code().match(/title/i)) col.width = 'auto'; // this is hack.
474 vlQueueGridLayout[0].cells[0].push(col);
479 storeData = vqbr.toStoreData(queuedRecords);
481 storeData = vqar.toStoreData(queuedRecords);
483 var store = new dojo.data.ItemFileReadStore({data:storeData});
484 var model = new dojox.grid.data.DojoData(
485 null, store, {rowsPerPage: 100, clientSort: true, query:{id:'*'}});
487 vlQueueGrid.setModel(model);
488 vlQueueGrid.setStructure(vlQueueGridLayout);
489 vlQueueGrid.update();
494 alert(vlQueueGridLayout.picker);
495 vlQueueGridLayout.oils = {};
496 vlQueueGridLayout[0].cells[0].pop();
497 vlQueueGridLayout[0].cells[0].pop();
498 vlQueueGridLayout[0].cells[0].pop();
499 vlQueueGridLayout[0].cells[0].pop();
500 vlQueueGridLayout[0].cells[0].pop();
501 vlQueueGrid.setStructure(vlQueueGridLayout);
502 vlQueueGrid.update();
506 function vlQueueGridDrawSelectBox(rowIdx) {
507 var data = this.grid.model.getRow(rowIdx);
509 var domId = 'vl-record-list-selected-' +data.id;
510 selectableGridRecords[domId] = data.id;
511 return "<div><input type='checkbox' id='"+domId+"'/></div>";
514 function vlSelectAllGridRecords() {
515 for(var id in selectableGridRecords)
516 dojo.byId(id).checked = true;
518 function vlSelectNoGridRecords() {
519 for(var id in selectableGridRecords)
520 dojo.byId(id).checked = false;
523 var handleRetrieveRecords = function() {
524 buildRecordGrid(currentType);
527 function vlImportSelectedRecords() {
528 displayGlobalDiv('vl-generic-progress-with-total');
531 for(var id in selectableGridRecords) {
532 if(dojo.byId(id).checked) {
533 var recId = selectableGridRecords[id];
534 var rec = queuedRecordsMap[recId];
535 if(!rec.import_time())
540 fieldmapper.standardRequest(
541 ['open-ils.vandelay', 'open-ils.vandelay.'+currentType+'_record.list.import'],
543 params: [authtoken, records, {overlay_map:currentOverlayRecordsMap}],
544 onresponse: function(r) {
545 var resp = r.recv().content();
546 if(e = openils.Event.parse(resp))
548 vlControlledProgressBar.update({maximum:resp.total, progress:resp.progress});
550 oncomplete: function() {
551 return retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
559 * Create queue, upload MARC, process spool, load the newly created queue
561 function batchUpload() {
562 var queueName = dijit.byId('vl-queue-name').getValue();
563 currentType = dijit.byId('vl-record-type').getValue();
565 var handleProcessSpool = function() {
566 console.log('records uploaded and spooled');
567 retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
570 var handleUploadMARC = function(key) {
571 console.log('marc uploaded');
572 dojo.style(dojo.byId('vl-upload-status-processing'), 'display', 'block');
573 processSpool(key, currentQueueId, currentType, handleProcessSpool);
576 var handleCreateQueue = function(queue) {
577 console.log('queue created ' + queue.name());
578 currentQueueId = queue.id();
579 uploadMARC(handleUploadMARC);
582 if(vlUploadQueueSelector.getValue() && !queueName) {
583 currentQueueId = vlUploadQueueSelector.getValue();
584 console.log('adding records to existing queue ' + currentQueueId);
585 uploadMARC(handleUploadMARC);
587 createQueue(queueName, currentType, handleCreateQueue);
592 function vlFleshQueueSelect(selector, type) {
593 var data = (type == 'bib') ? vbq.toStoreData(userBibQueues) : vaq.toStoreData(userAuthQueues);
594 selector.store = new dojo.data.ItemFileReadStore({data:data});
595 selector.setValue(null);
596 selector.setDisplayedValue('');
598 selector.setValue(data[0].id());
601 function vlShowUploadForm() {
602 displayGlobalDiv('vl-marc-upload-div');
603 vlFleshQueueSelect(vlUploadQueueSelector, vlUploadRecordType.getValue());
606 function vlShowQueueSelect() {
607 displayGlobalDiv('vl-queue-select-div');
608 vlFleshQueueSelect(vlQueueSelectQueueList, vlQueueSelectType.getValue());
611 function vlFetchQueueFromForm() {
612 currentType = vlQueueSelectType.getValue();
613 currentQueueId = vlQueueSelectQueueList.getValue();
614 retrieveQueuedRecords(currentType, currentQueueId, handleRetrieveRecords);
617 dojo.addOnLoad(vlInit);