diff --git a/modules/bibedit/lib/bibedit_engine.py b/modules/bibedit/lib/bibedit_engine.py index 7a83bcf15..5658ca5ab 100644 --- a/modules/bibedit/lib/bibedit_engine.py +++ b/modules/bibedit/lib/bibedit_engine.py @@ -1,593 +1,599 @@ ## $Id$ ## ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. __revision__ = "$Id$" import cPickle import marshal import os import sre import time from invenio.bibedit_config import CFG_BIBEDIT_TMPFILENAMEPREFIX from invenio.bibedit_dblayer import marc_to_split_tag from invenio.bibrecord import record_xml_output, create_record, create_records, \ field_add_subfield, record_add_field, record_has_field, record_get_field_value from invenio.bibtask import task_low_level_submission from invenio.config import CFG_BINDIR, CFG_TMPDIR, CFG_BIBEDIT_TIMEOUT, \ CFG_BIBEDIT_LOCKLEVEL, CFG_BIBUPLOAD_EXTERNAL_OAIID_TAG as OAIID_TAG, \ CFG_BIBUPLOAD_EXTERNAL_SYSNO_TAG as SYSNO_TAG from invenio.dateutils import convert_datetext_to_dategui from invenio.dbquery import run_sql from invenio.shellutils import run_shell_command from invenio.search_engine import print_record, record_exists, get_fieldvalues import invenio.template bibedit_templates = invenio.template.load('bibedit') ## precompile some often-used regexp for speed reasons: re_tasks = sre.compile('ID="(\d+)"') re_file_option = sre.compile(r'^/') re_filename_suffix = sre.compile('_(\d+)\.xml$') re_date = sre.compile('\.(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)') def perform_request_index(ln, recid, cancel, delete, confirm_delete, uid, temp, format_tag, edit_tag, delete_tag, num_field, add, dict_value=None): """Returns the body of main page. """ errors = [] warnings = [] body = '' if cancel != 0: os.system("rm %s.tmp" % get_file_path(cancel)) if delete != 0: if confirm_delete != 0: body = bibedit_templates.tmpl_confirm(ln, 1, delete, temp, format_tag) else: (record, junk) = get_record(ln, delete, uid, "false") add_field(delete, uid, record, "980", "", "", "c", "DELETED") save_temp_record(record, uid, "%s.tmp" % get_file_path(delete)) save_xml_record(delete) body = bibedit_templates.tmpl_confirm(ln) else: if recid != 0 : if record_exists(recid) > 0: (record, body) = get_record(ln, recid, uid, temp) if record != '' and not record_locked_p(recid): if add == 3: body = '' if edit_tag is not None and dict_value is not None: record = edit_record(recid, uid, record, edit_tag, dict_value, num_field) if delete_tag is not None and num_field is not None: record = delete_field(recid, uid, record, delete_tag, num_field) if add == 4: tag = dict_value.get("add_tag" , '') ind1 = dict_value.get("add_ind1" , '') ind2 = dict_value.get("add_ind2" , '') subcode = dict_value.get("add_subcode", '') value = dict_value.get("add_value" , '') another = dict_value.get("addanother" , '') if tag != '' and subcode != '' and value != '': #add these in the record, take the instance number tag = tag[:3] new_field_number = record_add_field(record, tag, ind1, ind2, [(subcode,value)]) record = add_subfield(recid, uid, tag, record, new_field_number, subcode, value) if another and another != '': #if the user pressed 'another' instead of 'done', take to editing return perform_request_edit(ln, recid, uid, tag, new_field_number, 0, 'marc', True, None, 0, dict_value) body += bibedit_templates.tmpl_table_header(ln, "record", recid, temp, format_tag, add=add) keys = record.keys() keys.sort() for tag in keys: fields = record.get(str(tag), "empty") if fields != "empty": for field in fields: if field[0]: # Only display if has subfield(s) body += bibedit_templates.tmpl_table_value(ln, recid, tag, field, format_tag, "record", add) if add == 3: body += bibedit_templates.tmpl_table_value(ln, recid, '', [], format_tag, "record", add, 1) body += bibedit_templates.tmpl_table_footer(ln, "record", add, 1) elif record == '': body = bibedit_templates.tmpl_record_choice_box(ln, 2) elif CFG_BIBEDIT_LOCKLEVEL == 2: os.system("rm %s.tmp" % get_file_path(recid)) body = bibedit_templates.tmpl_record_choice_box(ln, 4) else: os.system("rm %s.tmp" % get_file_path(recid)) body = bibedit_templates.tmpl_record_choice_box(ln, 5) else: if record_exists(recid) == -1: body = bibedit_templates.tmpl_record_choice_box(ln, 3) else: body = bibedit_templates.tmpl_record_choice_box(ln, 1) else: body = bibedit_templates.tmpl_record_choice_box(ln, 0) return (body, errors, warnings) def perform_request_edit(ln, recid, uid, tag, num_field, num_subfield, format_tag, temp, act_subfield, add, dict_value): """Returns the body of edit page. """ errors = [] warnings = [] body = '' if record_exists(recid) in (-1, 0): body = bibedit_templates.tmpl_record_choice_box(ln, 0) return (body, errors, warnings) (record, junk) = get_record(ln, recid, uid, temp) if act_subfield is not None: if act_subfield == 'delete': record = delete_subfield(recid, uid, record, tag, num_field, num_subfield) if act_subfield == 'move_up': record = move_subfield('move_up', recid, uid, record, tag, num_field, num_subfield) if act_subfield == 'move_down': record = move_subfield('move_down', recid, uid, record, tag, num_field, num_subfield) if add == 2: subcode = dict_value.get("add_subcode", "empty") value = dict_value.get("add_value" , "empty") if subcode == '': subcode = "empty" if value == '': value = "empty" if value != "empty" and subcode != "empty": record = add_subfield(recid, uid, tag, record, num_field, subcode, value) body += bibedit_templates.tmpl_table_header(ln, "edit", recid, temp=temp, tag=tag, num_field=num_field, add=add) tag = tag[:3] fields = record.get(str(tag), 'empty') if fields != "empty": for field in fields: if field[4] == int(num_field) : body += bibedit_templates.tmpl_table_value(ln, recid, tag, field, format_tag, "edit", add) break body += bibedit_templates.tmpl_table_footer(ln, "edit", add) return (body, errors, warnings) def perform_request_submit(ln, recid): """Submits record to the database. """ save_xml_record(recid) errors = [] warnings = [] body = bibedit_templates.tmpl_submit(ln) return (body, errors, warnings) def perform_request_history(ln, recid, revid, action, uid, temp, format_tag, args): """ Performs historic operations on a record. """ errors = [] warnings = [] body = '' revision = get_revision(recid, revid) if revision != 0: if action == 'confirm_load': body = bibedit_templates.tmpl_confirm( ln, 2, recid, temp, format_tag, revid, revid2revdate(revid)) elif action == 'load': submit_record(recid, revision, uid) body = bibedit_templates.tmpl_confirm(ln, 3, recid) else: body = '
%s
' % get_text_marc(recid, revision).replace('\n', '
') else: warnings.append('No revision named %s for record #%s' % (revid, recid)) return (body, errors, warnings) def get_file_path(recid): """ return the file path of record. """ return "%s/%s_%s" % (CFG_TMPDIR, CFG_BIBEDIT_TMPFILENAMEPREFIX, str(recid)) def save_xml_record(recid): """Saves XML record file to database. """ file_path = get_file_path(recid) + os.system("rm -f %s.xml" % file_path) file_temp = open("%s.xml" % file_path, 'w') file_temp.write(record_xml_output(get_temp_record("%s.tmp" % file_path)[1])) file_temp.close() task_low_level_submission('bibupload', 'bibedit', '-P', '5', '-r', '%s.xml' % file_path) os.system("rm %s.tmp" % file_path) def save_temp_record(record, uid, file_path): """ Save record dict in temp file. """ file_temp = open(file_path, "w") cPickle.dump([uid, record], file_temp) file_temp.close() def get_temp_record(file_path): """Loads record dict from a temp file. """ file_temp = open(file_path) [uid, record] = cPickle.load(file_temp) file_temp.close() return (uid, record) def get_record(ln, recid, uid, temp): """Returns a record dict, and warning message in case of error. """ #FIXME: User doesn't get submit button if reloading BibEdit-page + #FIXME: User will get warning of changes being temporary when reloading + # BibEdit-page, even though no changes have been made. warning_temp_file = '' file_path = get_file_path(recid) if temp != "false": warning_temp_file = bibedit_templates.tmpl_warning_temp_file(ln) if os.path.isfile("%s.tmp" % file_path): (uid_record_temp, record) = get_temp_record("%s.tmp" % file_path) if uid_record_temp != uid: time_tmp_file = os.path.getmtime("%s.tmp" % file_path) time_out_file = int(time.time()) - CFG_BIBEDIT_TIMEOUT if time_tmp_file < time_out_file : os.system("rm %s.tmp" % file_path) record = create_record(print_record(recid, 'xm'))[0] save_temp_record(record, uid, "%s.tmp" % file_path) else: record = '' else: warning_temp_file = bibedit_templates.tmpl_warning_temp_file(ln) else: record = create_record(print_record(recid, 'xm'))[0] save_temp_record(record, uid, "%s.tmp" % file_path) return (record, warning_temp_file) ######### EDIT ######### def edit_record(recid, uid, record, edit_tag, dict_value, num_field): """Edits value of a record. """ for num_subfield in range( len(dict_value.keys())/3 ): # Iterate over subfield indices of field new_subcode = dict_value.get("subcode%s" % num_subfield, None) old_subcode = dict_value.get("old_subcode%s" % num_subfield, None) new_value = dict_value.get("value%s" % num_subfield, None) old_value = dict_value.get("old_value%s" % num_subfield, None) if new_value is not None and old_value is not None \ and new_subcode is not None and old_subcode is not None: # Make sure we actually get these values if new_value != '' and new_subcode != '': # Forbid empty values if new_value != old_value or \ new_subcode != old_subcode: # only change when necessary edit_tag = edit_tag[:5] record = edit_subfield(record, edit_tag, new_subcode, new_value, num_field, num_subfield) save_temp_record(record, uid, "%s.tmp" % get_file_path(recid)) return record def edit_subfield(record, tag, new_subcode, new_value, num_field, num_subfield): """Edits the value of a subfield. """ new_value = bibedit_templates.tmpl_clean_value(str(new_value), "html") (tag, ind1, ind2, junk) = marc_to_split_tag(tag) fields = record.get(str(tag), None) if fields is not None: i = -1 for field in fields: i += 1 if field[4] == int(num_field): subfields = field[0] j = -1 for subfield in subfields: j += 1 if j == num_subfield: # Rely on counted index to identify subfield to edit... record[tag][i][0][j] = (new_subcode, new_value) break break return record ######### ADD ######## def add_field(recid, uid, record, tag, ind1, ind2, subcode, value_subfield): """Adds a new field to the record. """ tag = tag[:3] new_field_number = record_add_field(record, tag, ind1, ind2) record = add_subfield(recid, uid, tag, record, new_field_number, subcode, value_subfield) save_temp_record(record, uid, "%s.tmp" % get_file_path(recid)) return record def add_subfield(recid, uid, tag, record, num_field, subcode, value): """Adds a new subfield to a field. """ tag = tag[:3] fields = record.get(str(tag)) i = -1 for field in fields: i += 1 if field[4] == int(num_field) : subfields = field[0] field_add_subfield(record[tag][i], subcode, value) break save_temp_record(record, uid, "%s.tmp" % get_file_path(recid)) return record ######### DELETE ######## def delete_field(recid, uid, record, tag, num_field): """Deletes field in record. """ (tag, junk, junk, junk) = marc_to_split_tag(tag) tmp = [] for field in record[tag]: if field[4] != int(num_field) : tmp.append(field) if tmp != []: record[tag] = tmp else: del record[tag] save_temp_record(record, uid, "%s.tmp" % get_file_path(recid)) return record def delete_subfield(recid, uid, record, tag, num_field, num_subfield): """Deletes subfield of a field. """ (tag, junk, junk, subcode) = marc_to_split_tag(tag) tmp = [] i = -1 deleted = False for field in record[tag]: i += 1 if field[4] == int(num_field): j = 0 for subfield in field[0]: if j != num_subfield: #if subfield[0] != subcode or deleted == True: tmp.append((subfield[0], subfield[1])) #else: # deleted = True j += 1 break record[tag][i] = (tmp, record[tag][i][1], record[tag][i][2], record[tag][i][3], record[tag][i][4]) save_temp_record(record, uid, "%s.tmp" % get_file_path(recid)) return record def move_subfield(direction, recid, uid, record, tag, num_field, num_subfield): """moves a subfield up in the field """ (tag, junk, junk, subcode) = marc_to_split_tag(tag) i = -1 for field in record[tag]: i += 1 if field[4] == int(num_field): j = -1 mysubfields = field[0] for subfield in mysubfields: j += 1 if direction == 'move_up' and num_subfield == j and j > 0: #swap this and the previous.. prevsubfield = field[0][j-1] field[0][j-1] = subfield field[0][j] = prevsubfield if direction == 'move_down' and num_subfield == j and j < len(mysubfields): #swap this and the next.. nextsubfield = field[0][j+1] field[0][j+1] = subfield field[0][j] = nextsubfield save_temp_record(record, uid, "%s.tmp" % get_file_path(recid)) return record def record_locked_p(recid): """Checks if record should be locked for editing, method based on CFG_BIBEDIT_LOCKLEVEL. """ # Check only for tmp-file (done in get_record). if CFG_BIBEDIT_LOCKLEVEL == 0: return 0 # Check for any scheduled bibupload tasks. if CFG_BIBEDIT_LOCKLEVEL == 2: cmd = """%s/bibsched status -t bibupload | grep -v 'USER="bibreformat"'""" % CFG_BINDIR bibsched_status = run_shell_command(cmd)[1] return bibsched_status.find('PROC="bibupload"') + 1 filenames = get_bibupload_filenames() # Check for match between name of XML-files and record. # Assumes that filename ends with _.xml. if CFG_BIBEDIT_LOCKLEVEL == 1: recids = [] for filename in filenames: filename_suffix = re_filename_suffix.search(filename) if filename_suffix: recids.append(int(filename_suffix.group(1))) return recid in recids # Check for match between content of files and record. if CFG_BIBEDIT_LOCKLEVEL == 3: while True: lock = record_in_files_p(recid, filenames) # Check if any new files were added while we were searching if not lock: filenames_updated = get_bibupload_filenames() for filename in filenames_updated: if not filename in filenames: break else: return lock else: return lock def get_bibupload_filenames(): """ Returns path to all files scheduled for bibupload, except by user bibreformat. """ cmd = """%s/bibsched status -t bibupload | grep -v 'USER="bibreformat"'""" % CFG_BINDIR bibsched_status = run_shell_command(cmd)[1] tasks = re_tasks.findall(bibsched_status) filenames = [] for task in tasks: res = run_sql("SELECT arguments FROM schTASK WHERE id=%s" % task) if res: record_options = marshal.loads(res[0][0]) for option in record_options[1:]: if re_file_option.search(option): filenames.append(option) return filenames def record_in_files_p(recid, filenames): """ Scans trough XML files searching for record. """ # Get id tags of record in question rec_oaiid = rec_sysno = -1 rec_oaiid_tag = get_fieldvalues(recid, OAIID_TAG) if rec_oaiid_tag: rec_oaiid = rec_oaiid_tag[0] rec_sysno_tag = get_fieldvalues(recid, SYSNO_TAG) if rec_sysno_tag: rec_sysno = rec_sysno_tag[0] # For each record in each file, compare ids and abort if match is found for filename in filenames: try: file_ = open(filename) records = create_records(file_.read(), 0, 0) for i in range(0, len(records)): (record, er, junk) = records[i] if record and er != 0: if record_has_field(record, '001'): if (record_get_field_value(record, '001', '%', '%') == str(recid)): return True if record_has_field(record, OAIID_TAG[0:3]): if (record_get_field_value( record, OAIID_TAG[0:3], OAIID_TAG[3], OAIID_TAG[4], OAIID_TAG[5]) == rec_oaiid): return True if record_has_field(record, SYSNO_TAG[0:3]): if (record_get_field_value( record, SYSNO_TAG[0:3], SYSNO_TAG[3], SYSNO_TAG[4], SYSNO_TAG[5]) == rec_sysno): return True file_.close() except IOError: continue return False def get_revision(recid, revid): """ Return previous revision of record. """ if revid and len(revid.split('.')) == 2: cmd = "%s/bibedit --list-revisions %s" % (CFG_BINDIR, recid) revids = run_shell_command(cmd)[1] if revid in revids.split('\n'): cmd = "%s/bibedit --get-revision %s" % (CFG_BINDIR, revid) return run_shell_command(cmd)[1] return 0 def revid2revdate(revid): """ Return date of revision in userfriendly format. """ date = re_date.search(revid) return convert_datetext_to_dategui('%s-%s-%s %s:%s:%s' % date.groups()) def get_text_marc(recid, xml_record): """ Return record in text MARC format. """ tmpfilepath = '%s/bibedit_tmpfile_%s' % (CFG_TMPDIR, recid) tmpfile = open(tmpfilepath, 'w') tmpfile.write(xml_record) tmpfile.close() cmd = ('%s/xmlmarc2textmarc %s') % (CFG_BINDIR, tmpfilepath) textmarc = run_shell_command(cmd)[1] run_shell_command('rm ' + tmpfilepath) return textmarc -def submit_record(recid, xml_record, uid): +def submit_record(recid, xml_record, uid=0): """ Submit a new revision of a record. - @param recid id of the record in question @param xml_record the new revision in XML MARC format - @uid user id of the calling user + @uid user id of the calling user (0 if called from command line) """ - file_path = get_file_path(recid) if os.path.isfile("%s.tmp" % file_path): os.system("rm %s.tmp" % file_path) record = create_record(xml_record)[0] save_temp_record(record, uid, "%s.tmp" % file_path) save_xml_record(recid) + +def get_revisions(recid): + """ Return list of revisions. """ + cmd = '%s/bibedit --list-revisions %s' % (CFG_BINDIR, recid) + return run_shell_command(cmd)[1].split('\n')[:-1] \ No newline at end of file diff --git a/modules/bibedit/lib/bibedit_templates.py b/modules/bibedit/lib/bibedit_templates.py index 38b17842d..e0420c3c6 100644 --- a/modules/bibedit/lib/bibedit_templates.py +++ b/modules/bibedit/lib/bibedit_templates.py @@ -1,652 +1,652 @@ ## $Id$ ## ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. __revision__ = "$Id$" from invenio.bibedit_dblayer import * from invenio.config import CFG_SITE_URL from invenio.messages import gettext_set_language ## Link of edit, move up and delete button: btn_delete_url = CFG_SITE_URL + "/img/iconcross.gif" btn_moveup_url = CFG_SITE_URL + "/img/arrow_up.gif" btn_movedown_url = CFG_SITE_URL + "/img/arrow_down.gif" btn_edit_url = CFG_SITE_URL + "/img/iconpen.gif" bibediturl = "%s/admin/bibedit/bibeditadmin.py" % CFG_SITE_URL class Template: def tmpl_table_header(self, ln, type_table, recid, temp="false", format_tag='marc', tag='', num_field=None, add=0): """ Return the Header of table. """ _ = gettext_set_language(ln) (tag, ind1, ind2, junk) = marc_to_split_tag(tag) tag = tag + ind1 + ind2 + "%" if type_table != "record": if add == 1: print_input_add_form = self.tmpl_input('hidden', 2, 'add') else: print_input_add_form = '' print_action_add_subfield = print_input_add_form + self.tmpl_input('hidden', str(num_field), 'num_field') print_action_edit_100 = print_input_add_form + self.tmpl_input('hidden', str(num_field), 'num_field') if add != 1: link_add_subfields = self.tmpl_link(ln, _("Add Subfield"), bibediturl, 'edit', {'recid' : str(recid), 'tag' : tag[:3], 'num_field' : str(num_field), 'format_tag' : format_tag, 'temp' : 'true', 'add' : 1}) link_edit_100 = "" print_action_edit_100 = "" #tag[:3] == 100x is never true, of course. This functionality TBD #and will be used in enrichment editing if str(tag[:3]) == "100x": #FIXME link_edit_100 = self.tmpl_link( ln, _("Edit institute"), bibediturl, 'edit', {'recid' : str(recid), 'tag' : tag[:3], 'num_field' : str(num_field), 'format_tag' : format_tag, 'temp' : 'true', 'add' : 1}) print_action_edit_100 = """ %(field)s: %(link_edit_100)s """ % {'field' : _("Field"), 'link_edit_100' : link_edit_100} print_action_add_subfield = """ %(field)s: %(link_add_subfields)s """ % {'field' : _("Field"), 'link_add_subfields' : link_add_subfields} if add == 1: link_form = "edit" else: link_form = "index" formcontents = """
%(input_recid)s %(input_temp)s %(input_ln)s
%(print_action_add_subfield)s %(print_action_edit_100)s
""" % {'bibediturl' : bibediturl, 'input_recid' : self.tmpl_input('hidden', recid, 'recid'), 'input_temp' : self.tmpl_input('hidden', 'true', 'temp'), 'input_ln' : self.tmpl_input('hidden', ln, 'ln'), 'link_form' : link_form, 'print_action_add_subfield' : print_action_add_subfield, 'print_action_edit_100' : print_action_edit_100} result = formcontents else: link_submit = '' link_add_field = self.tmpl_link(ln, _("Add Field"), bibediturl, 'index', {'recid' : str(recid), 'tag' : tag[:3], 'format_tag' : format_tag, 'temp' : 'true', 'add' : 3}, 'add') + " | " link_diplay_verbose = self.tmpl_link(ln, _("Verbose"), bibediturl, 'index', {'recid' : str(recid), 'format_tag' : 's', 'temp' : temp}) link_diplay_marc = self.tmpl_link(ln, "MARC", bibediturl, 'index', {'recid' : str(recid), 'format_tag' : 'marc', 'temp' : temp}) if temp != "false" and add != 3: link_submit = self.tmpl_link(ln, _("Submit"), bibediturl, 'submit', {'recid' : str(recid)}) + " | " if add == 3: link_add_field = '' result = '' else: link_cancel = self.tmpl_link(ln, _("Cancel"), bibediturl, 'index', {'cancel' : str(recid)}) link_delete = self.tmpl_link(ln, _("Delete"), bibediturl, 'index', {'delete' : str(recid), 'confirm_delete' :1, 'temp' : temp}) result = """
 %(action)s: %(link_submit)s%(link_cancel)s  %(record)s: %(link_add_field)s%(link_delete)s  %(display)s: %(link_diplay_verbose)s | %(link_diplay_marc)s
""" % {'action' : _("Action"), 'record' : _("Record"), 'display' : _("Display"), 'link_submit' : link_submit, 'link_add_field' : link_add_field, 'link_diplay_verbose' : link_diplay_verbose, 'link_diplay_marc' : link_diplay_marc, 'link_cancel' : link_cancel, 'link_delete' : link_delete} return """ """ % {'record' : _("Record"), 'recid' : str(recid), 'result' : result, 'num_field': self.tmpl_input('hidden', str(num_field), 'num_field')} def tmpl_table_value(self, ln, recid, tag, field, format_tag, type_table, add, form_add=0): """ Return a field to print in table. """ if form_add == 0: subfields = field[0] num_field = field[4] tag_field = split_tag_to_marc(tag, field[1], field[2]) if format_tag != 's': print_tag_field = tag_field[:-1] else: tag_field = tag_field[:-1] + '%' if get_name_tag(tag_field) != tag_field: print_tag_field = get_name_tag(tag_field) else: print_tag_field = tag_field[:-1] len_subfields = len(subfields) if type_table == "record" and add != 3: print_link_function = self.tmpl_link_function(ln, len_subfields, recid, tag, num_field, format_tag) print_tag_form = '' else: print_link_function = '' if add == 1: print_tag_form = self.tmpl_input('hidden', get_tag_name(print_tag_field), 'tag') else: print_tag_form = self.tmpl_input('hidden', get_tag_name(print_tag_field), 'edit_tag') if add == 1: len_subfields += 1 type_table = "record" try: result = """ %(subfield)s %(print_link_function)s """ % {'len_subfields' : len_subfields, 'print_tag_field' : print_tag_field, 'print_tag_form' : print_tag_form, 'subfield' : self.tmpl_subfields(ln, recid, subfields[0][0], subfields[0][1], tag_field, format_tag, type_table, 0, num_field, len_subfields), 'print_link_function' : print_link_function} except IndexError: raise "FIXME: BibEdit does not seem to be able to edit records with controlfields." if len_subfields != 1: num_value = -1 for subfield in subfields: num_value += 1 if num_value != 0: result += """ %s """ % self.tmpl_subfields(ln, recid, subfield[0], subfield[1], tag_field, format_tag, type_table, num_value, num_field, len_subfields) if add == 1: result += """ %s """ % self.tmpl_subfields(ln, add=add) else: #click on "add field" link on the top of index page. result = """ """ % {'bibediturl' : bibediturl, 'input_recid' : self.tmpl_input('hidden', recid, 'recid'), 'input_temp' : self.tmpl_input('hidden', 'true', 'temp'), 'input_add' : self.tmpl_input('hidden', 4, 'add'), 'input_ln' : self.tmpl_input('hidden', ln, 'ln'), 'recid' : str(recid), 'ln' : ln} result += "%s" % self.tmpl_subfields(ln, add=add) return """ %s """ % result def tmpl_table_footer(self, ln, type_table, add=0, another=0): """ Return a footer of table. """ _ = gettext_set_language(ln) dbutton = _("Done") abutton = _("Add another subfield") if type_table != "record" or add == 3: #add a done button and 'add another subfield' button in the form form = self.tmpl_input('submit', dbutton, class_css='formbutton') + "
" if another: form += self.tmpl_input('submit', abutton, 'addanother', class_css='formbutton') + "
" form += _("Tags and values should be filled before pressing Done or Add another subfield") form += "" else: form = '' return """
%(record)s #%(recid)s %(result)s %(num_field)s
%(print_tag_field)s %(print_tag_form)s
%(input_recid)s %(input_temp)s %(input_ln)s %(input_add)s Tag: ind1: ind2:
%(form)s
""" % {'form' : form} def tmpl_subfields(self, ln, recid='', tag_subfield='', value='', tag_field='', format_tag='marc', type_table='', num_value='', num_field='', len_subfields='', add=0): """ This function return the content of subfield. """ _ = gettext_set_language(ln) if add == 1 or add == 3: print_tag_subfield = " $$ " + self.tmpl_input('text', '', 'add_subcode', 1, 1) else: if type_table != "record": print_tag_subfield = " $$ " + self.tmpl_input('text', tag_subfield, 'subcode%s' % str(num_value), 1, 1) elif format_tag != 's': print_tag_subfield = "$$%s" % tag_subfield else: print_tag_subfield = "%s%s" % (tag_field[:-1], tag_subfield) if get_name_tag(print_tag_subfield) != print_tag_subfield: print_tag_subfield = get_name_tag(print_tag_subfield) else: print_tag_subfield = "$$%s" % tag_subfield value = self.tmpl_clean_value(value, "record") print_value = '' print_old_value = '' print_btn = '' print_bgcolor = '' print_old_subcode = '' if type_table != "record" or add == 3: if add == 1 or add == 3: print_value = self.tmpl_input('text', '', 'add_value', size="115%c" % '%') else: print_old_subcode = self.tmpl_input('hidden', tag_subfield, 'old_subcode%s' % str(num_value)) print_old_value = self.tmpl_input('hidden', value, 'old_value%s' % str(num_value)) if len(value) < 75: print_value = self.tmpl_input('text', value, 'value%s' % str(num_value), style="width:100%;") else: print_value = '' print_value %= {'num_value' : str(num_value), 'value' : value} if len_subfields > 1: print_btn = "%s" \ % self.tmpl_link(ln, '%s' % (btn_delete_url, _("Delete")), bibediturl, 'edit', {'recid' : str(recid), 'tag' : tag_field[:-1]+ tag_subfield, 'num_field' : num_field, 'format_tag' : format_tag, 'temp' : 'true', 'act_subfield' : 'delete', #delete 'num_subfield' : num_value}) if num_value > 0: print_btn += "%s" \ % self.tmpl_link(ln, '%s' % (btn_moveup_url, _("Move up")), bibediturl, 'edit', {'recid' : str(recid), 'tag' : tag_field[:-1]+ tag_subfield, 'num_field' : num_field, 'format_tag' : format_tag, 'temp' : 'true', 'act_subfield' : 'move_up', #move up 'num_subfield' : num_value}) else: print_btn += " " if num_value < len_subfields-1: print_btn += "%s" \ % self.tmpl_link(ln, '%s' % (btn_movedown_url, _("Move down")), bibediturl, 'edit', {'recid' : str(recid), 'tag' : tag_field[:-1]+ tag_subfield, 'num_field' : num_field, 'format_tag' : format_tag, 'temp' : 'true', 'act_subfield' : 'move_down', 'num_subfield' : num_value}) else: print_value = value print_bgcolor = " background: #FFF;" return """ %(print_tag_subfield)s %(print_old_subcode)s %(print_value)s %(print_old_value)s %(print_btn)s """ % {'print_tag_subfield' : print_tag_subfield, 'print_old_subcode' : print_old_subcode, 'print_bgcolor' : print_bgcolor, 'print_value' : print_value, 'print_old_value' : print_old_value, 'print_btn' : print_btn} def tmpl_link_function(self, ln, len_subfields, recid, tag, num_field, format_tag): """ Print button function to edit and delete information. """ _ = gettext_set_language(ln) btn_edit = self.tmpl_link(ln, '%s' % (btn_edit_url, _("Edit")), bibediturl, 'edit', {'recid' : str(recid), 'tag' : tag, 'num_field' : num_field, 'format_tag' : format_tag, 'temp' : 'true'}) btn_delete = self.tmpl_link(ln, '%s' % (btn_delete_url, _("Delete")), bibediturl, 'index', {'recid' : str(recid), 'delete_tag' : tag, 'num_field' : num_field, 'format_tag' : format_tag, 'temp' : 'true'}) return """ %(btn_edit)s %(btn_delete)s """ % {'len_subfields' : len_subfields, 'btn_edit' : btn_edit, 'btn_delete' : btn_delete} def tmpl_clean_value(self, value, format): """ This function clean value for HTML interface and inverse. """ if format != "html": value = value.replace('"', '"') value = value.replace('<', '<') value = value.replace('>', '>') else: value = value.replace('"', '"') value = value.replace('<', '<') value = value.replace('>', '>') return value def tmpl_warning_temp_file(self, ln): """ Return a warning message for user who use a temp file. """ _ = gettext_set_language(ln) return """ %(message1)s %(message2)s

""" % {'message1' : _("Your changes are TEMPORARY."), 'message2' : _("To save this record, please click on submit.")} def tmpl_record_choice_box(self, ln, message): """ Return a little for; for choice a record to edit. """ _ = gettext_set_language(ln) if message == 1: result = """ %(message1)s %(message2)s

""" % {'message1' : _("This record does not exist."), 'message2' : _("Please try another record ID.")} elif message == 2: result = """ %(message1)s %(message2)s

""" % {'message1' : _("This record is currently being edited by another user."), 'message2' : _("Please try again later.")} elif message == 3: result = """ %(message1)s

""" % {'message1' : _("Cannot edit deleted record.")} elif message == 4: result = """ %(message1)s %(message2)s

""" % {'message1' : _("There are record revisions not yet synchronized with the database."), 'message2' : _("Please try again in a few minutes.")} elif message == 5: result = """ %(message1)s %(message2)s

""" % {'message1' : _("A new revision of this record is not yet synchronized with the database."), 'message2' : _("Please try again in a few minutes.")} else: result = '' result += """
%(input_ln)s %(message)s: %(input_recid)s %(input_button)s
""" % {'bibediturl' : bibediturl, 'message' : _("Please enter the ID of the record you want to edit"), 'input_ln' : self.tmpl_input('hidden', ln, 'ln'), 'input_recid' : self.tmpl_input('text' , '', 'recid'), 'input_button' : self.tmpl_input('submit', _("Edit"), class_css='formbutton')} return result def tmpl_submit(self, ln): """ Return a end message of Bibedit. """ _ = gettext_set_language(ln) out = _("Your modifications have now been submitted. They will be processed as soon as the task queue is empty.") + '

' out += '

' + _("Edit another record") + '

' out += self.tmpl_record_choice_box(ln=ln, message=0) return out def tmpl_confirm(self, ln, message='', recid='', temp='', format_tag='', revid='', revdate=''): """ Ask for confirmation of or confirm some critical action. """ _ = gettext_set_language(ln) if message == 1: return """ %(message)s
%(input_ln)s %(input_button_yes)s
%(input_ln)s %(input_button_no)s
""" % {'message' : _("Do you really want to delete this record?"), 'bibediturl' : bibediturl, 'recid' : str(recid), 'input_ln' : self.tmpl_input('hidden', ln, 'ln'), 'input_button_yes' : self.tmpl_input('submit', _("Yes"), class_css='formbutton'), 'input_button_no' : self.tmpl_input('submit', _("No"), class_css='formbutton'), 'temp' : temp, 'format_tag' : format_tag} if message == 2: - question = _('Do you really want to load revision %(revdate)s of record #%(recid)s?' + question = _('Do you really want to revert to revision %(revdate)s of record #%(recid)s?' ) % {'revdate': revdate, 'recid': recid} warning_1 = _('The current version will be replaced with a copy of revision %(revdate)s' ) % {'revdate': revdate} warning_2 = _('You will also lose any unsubmitted changes for this record!') return """ %(question)s
%(warning_1)s
%(warning_2)s

%(input_ln)s %(input_button_yes)s
%(input_ln)s %(input_button_no)s
""" % {'question' : question, 'warning_1' : warning_1, 'warning_2' : warning_2, 'bibediturl' : bibediturl, 'revid' : revid, 'recid' : recid, 'input_ln' : self.tmpl_input('hidden', ln, 'ln'), 'input_button_yes': self.tmpl_input('submit', _("Yes"), class_css='formbutton'), 'input_button_no' : self.tmpl_input('submit', _("No"), class_css='formbutton'), 'temp' : temp, 'format_tag' : format_tag} if message == 3: out = _("The new revision is being synchronized with the database and will be ready shortly.") + '

' out += '

' + _("Edit another record") + '

' out += self.tmpl_record_choice_box(ln=ln, message=0) return out else: out = _("The record will be deleted as soon as the task queue is empty.") + '

' out += '

' + _("Edit another record") + '

' out += self.tmpl_record_choice_box(ln=ln, message=0) return out def tmpl_link(self, ln, text, url, dest, dict_args='', ancre=''): """ Return a link. """ if dict_args == '': link_args = '' else: link_args = '?' list_args = dict_args.items() for arg in list_args: link_args += "%(name)s=%(value)s&" % {'name' : str(arg[0]), 'value' : str(arg[1])} link_args += "ln=%s" % ln if ancre != '': ancre = '#' + ancre return '%(text)s' % {'text' : text, 'url' : url, 'dest' : dest, 'args' : link_args, 'ancre' : ancre} def tmpl_input(self, type_input, value='', name='', maxlength='', size='', class_css='', style=''): """ Return a input form. """ if value != '': value = 'value="%s"' % str(value) if name != '': name = 'name="%s"' % str(name) if maxlength != '': maxlength = 'maxlength="%s"' % str(maxlength) if size != '': size = 'size="%s"' % str(size) if class_css != '': class_css = 'class="%s"' % str(class_css) if style != '': style = 'style="%s"' % str(style) out = '' out %= {'type' : type_input, 'value' : value, 'name' : name, 'maxlength' : maxlength, 'size' : size, 'class_css' : class_css, 'style' : style} return out diff --git a/modules/bibedit/lib/bibeditcli.py b/modules/bibedit/lib/bibeditcli.py index f6135c2dc..d79c9d058 100644 --- a/modules/bibedit/lib/bibeditcli.py +++ b/modules/bibedit/lib/bibeditcli.py @@ -1,189 +1,248 @@ # -*- coding: utf-8 -*- ## ## $Id$ ## ## This file is part of CDS Invenio. ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 CERN. ## ## CDS Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of the ## License, or (at your option) any later version. ## ## CDS Invenio is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """ BibEdit CLI tool. Usage: bibedit [options] General options: -h, --help print this help -V, --version print version number Options to inspect record history: --list-revisions [recid] list all revisions of a record --get-revision [recid.revdate] print MARCXML of given record revision --diff-revisions [recidA.revdateB] print MARCXML difference between record A [recidC.revdateD] dated B and record C dated D + --revert-revision [recid.revdate] submit given record revision to become + current revision """ __revision__ = "$Id$" +import os import re import sys +import time import zlib import difflib _RE_RECORD_REVISION_FORMAT = re.compile(r'^(\d+)\.(\d{14})$') +from invenio.config import CFG_BIBEDIT_TIMEOUT from invenio.dbquery import run_sql +from invenio.bibedit_engine import get_file_path, record_locked_p, \ + save_temp_record, save_xml_record +from invenio.bibrecord import create_record def print_usage(): """Print help.""" print __doc__ def print_version(): """Print version information.""" print __revision__ def list_record_revisions(recid): """ Return list of all known record revisions (=RECID.REVDATE) for record RECID in chronologically decreasing order (latest first). """ out = [] res = run_sql("""SELECT id_bibrec, DATE_FORMAT(job_date, '%%Y%%m%%d%%H%%i%%s') FROM hstRECORD WHERE id_bibrec=%s ORDER BY job_date DESC""", (recid,)) for row in res: out.append("%s.%s" % (row[0], row[1])) return out def revision_valid_p(revid): """ Predicate to test validity of revision ID format (=RECID.REVDATE). """ if _RE_RECORD_REVISION_FORMAT.match(revid): return True return False def get_marcxml_of_record_revision(revid): """ Return MARCXML string with corresponding to revision REVID (=RECID.REVDATE) of a record. Return empty string if revision does not exist. REVID is assumed to be washed already. """ out = "" match = _RE_RECORD_REVISION_FORMAT.match(revid) recid = match.group(1) revdate = match.group(2) job_date = "%s-%s-%s %s:%s:%s" % (revdate[0:4], revdate[4:6], revdate[6:8], revdate[8:10], revdate[10:12], revdate[12:14],) res = run_sql("""SELECT marcxml FROM hstRECORD WHERE id_bibrec=%s AND job_date=%s""", (recid, job_date)) if res: for row in res: out += zlib.decompress(row[0]) + "\n" return out def cli_list_revisions(recid): """ Print list of all known record revisions (=RECID.REVDATE) for record RECID. """ try: recid = int(recid) except ValueError: print "ERROR: record ID must be integer, not %s." % recid sys.exit(1) print "\n".join(list_record_revisions(recid)) def cli_get_revision(revid): """ Return MARCXML for revision REVID (=RECID.REVDATE) of a record. Exit if things go wrong. """ if not revision_valid_p(revid): print "ERROR: revision %s is invalid; " \ "must be NNN.YYYYMMDDhhmmss." % revid sys.exit(1) out = get_marcxml_of_record_revision(revid) if out: print out else: print "ERROR: Revision %s not found." % revid def cli_diff_revisions(revid1, revid2): """ Return diffs of MARCXML record revisions REVID1, REVID2. Exit if things go wrong. """ for revid in [revid1, revid2]: if not revision_valid_p(revid): print "ERROR: revision %s is invalid; " \ "must be NNN.YYYYMMDDhhmmss." % revid sys.exit(1) xml1 = get_marcxml_of_record_revision(revid1) xml2 = get_marcxml_of_record_revision(revid2) print "".join(difflib.unified_diff(xml1.splitlines(1), xml2.splitlines(1), revid1, revid2,)) +def cli_revert_revision(revid): + """ + Submits specified revision for bibupload, to replace current version. + """ + # Is the revision valid? + if not revision_valid_p(revid): + print "ERROR: revision %s is invalid; " \ + "must be NNN.YYYYMMDDhhmmss." % revid + sys.exit(1) + + xmlrecord = get_marcxml_of_record_revision(revid) + + # Does the revision exist? + if xmlrecord == '': + print "ERROR: Revision %s does not exist. " % revid + sys.exit(1) + + match = _RE_RECORD_REVISION_FORMAT.match(revid) + recid = match.group(1) + file_path = get_file_path(recid) + + # Does a tmp file already exist? + if os.path.isfile("%s.tmp" % file_path): + time_tmp_file = os.path.getmtime("%s.tmp" % file_path) + time_out_file = int(time.time()) - CFG_BIBEDIT_TIMEOUT + + # Is it expired? + if time_tmp_file > time_out_file : + print "ERROR: Record %s is currently being edited by another " \ + "user. Please try again later." % recid + sys.exit(1) + + os.system("rm %s.tmp" % file_path) + + # Is the record locked for editing? + if record_locked_p(recid): + print "ERROR: Record %s is currently locked for editing. Please try " \ + "again in a few minutes." % recid + sys.exit(1) + + record = create_record(xmlrecord)[0] + save_temp_record(record, 0, "%s.tmp" % file_path) + save_xml_record(recid) + def main(): """Main entry point.""" if '--help' in sys.argv or \ '-h' in sys.argv: print_usage() elif '--version' in sys.argv or \ '-V' in sys.argv: print_version() else: try: cmd = sys.argv[1] opts = sys.argv[2:] if not opts: raise IndexError except IndexError: print_usage() sys.exit(1) if cmd == '--list-revisions': try: recid = opts[0] except IndexError: print_usage() sys.exit(1) cli_list_revisions(recid) elif cmd == '--get-revision': try: revid = opts[0] except IndexError: print_usage() sys.exit(1) cli_get_revision(revid) elif cmd == '--diff-revisions': try: revid1 = opts[0] revid2 = opts[1] except IndexError: print_usage() sys.exit(1) cli_diff_revisions(revid1, revid2) + elif cmd == '--revert-revision': + try: + revid = opts[0] + except IndexError: + print_usage() + sys.exit(1) + cli_revert_revision(revid) else: print """ERROR: Please specify a command. Please see '--help'.""" sys.exit(1) if __name__ == '__main__': main()