Exemple #1
0
def CaseEDS(parameters, curdir, form, user_info=None):
    """
       This function compares the content of a file to different values and
       directly goes to a different step in the action according to the value.

       This function may be used if the treatment to be done after a
       submission depends on a field entered by the user. Typically
       this is used in an approval interface. If the referee approves
       then we do this. If he rejects, then we do other thing.  More
       specifically, the function gets the value from the file named
       [casevariable] and compares it with the values stored in
       [casevalues]. If a value matches, the function directly goes to
       the corresponding step stored in [casesteps]. If no value is
       matched, it goes to step [casedefault].

       @param parameters: (dictionary) of parameters (relating to the given
        doctype/action) that are to be passed to the function:

           + casevariable: This parameters contains the name of the
                           file in which the function will get the
                           chosen value.
                           Eg:"decision"

           + casevalues: Contains the list of recognized values to
                         match with the chosen value. Should be a
                         comma separated list of words.
                         Eg:"approve,reject"

           + casesteps: Contains the list of steps corresponding to
                        the values matched in [casevalue]. It should
                        be a comma separated list of numbers.
                        Eg:"2,3"

                        In this example, if the value stored in the
                        file named"decision" is "approved", then the
                        function launches step 2 of this action. If it
                        is "reject", then step 3 is launched.

           + casedefault: Contains the step number to go by default if
                          no match is found.
                          Eg:"4"

                          In this example, if the value stored in the
                          file named "decision" is not "approved" nor
                          "reject", then step 4 is launched.

       @return: (string) - empty string
    """
    ## Get the values of the parameters passed to this function via the
    ## parameters array:
    casevariable = parameters['casevariable']
    casevalue = parameters['casevalues']
    casestep = parameters['casesteps']
    casedefault = parameters['casedefault']

    casevalues = casevalue.split(",")
    casesteps = casestep.split(",")
    cases = {}
    for a, b in map(None, casevalues, casesteps):
        cases[a] = b
    nextstep = ""
    if not os.path.exists("%s/%s" % (curdir, casevariable)):
        nextstep = casedefault
    else:
        fp = open("%s/%s" % (curdir, casevariable), "r")
        value = fp.read()
        fp.close()
        if value in cases:
            nextstep = cases[value]
        else:
            nextstep = casedefault
    if nextstep != "":
        t = "<b>Please wait...</b>"
        t = """
<SCRIPT LANGUAGE="JavaScript1.1">
    document.forms[0].action="/submit";
    document.forms[0].step.value=%s;
    user_must_confirm_before_leaving_page = false;
    document.forms[0].submit();
</SCRIPT>""" % nextstep
        raise InvenioWebSubmitFunctionStop(t)
    else:
        raise InvenioWebSubmitFunctionError("Case function: Could not " \
                                            "determine next action step")
    return ""
Exemple #2
0
def Make_Dummy_MARC_XML_Record(parameters, curdir, form, user_info=None):
    """
    Make a dummy MARC XML record and store it in a submission's working-
    directory.
    This dummy record is not intended to be inserted into the Invenio
    repository. Rather, it is intended as a way for other submission-
    related functionalities to have access to the data submitted without
    necessarily having to know the names of the files in which the
    values were stored.
    An example could be the publiline service: by using a dummy record
    in the submission's directory in would be able to access an item's
    information (e.g. title, etc) without having to know the name of the
    title file, etc.
    Another use for the dummy record could be, for example, creating a
    preview of the submitted record information with bibconvert.

    @param parameters: (dictionary) - must contain:

          + dummyrec_source_tpl: (string) - the name of the bibconvert
            source template used for the creation of the dummy record.

          + dummyrec_create_tpl: (string) - the name of the bibconvert
            create template used for the creation of the dummy record.

    @param curdir: (string) - the current submission's working
                              directory.

    @param form: (dictionary) - form fields.

    @param user_info: (dictionary) - various information about the
                                     submitting user (includes the
                                     apache req object).

    @return: (string) - empty string.

    @Exceptions raised: InvenioWebSubmitFunctionError when an
                        unexpected error is encountered.
    """
    ## Get the apache request object from user_info: (we may use it for
    ## error reporting)
    try:
        req_obj = user_info['req']
    except (KeyError, TypeError):
        req_obj = None

    ## Strip whitespace from the names of the source and creation templates:
    source_tpl = parameters['dummyrec_source_tpl'].replace(" ", "")
    create_tpl = parameters['dummyrec_create_tpl'].replace(" ", "")

    ## Call bibconvert to create the MARC XML record:
    cmd_bibconvert_call = "%s/bibconvert -l1 -d'%s' -Cs'%s/%s' -Ct'%s/%s' " \
                          "> %s/%s 2>/dev/null" \
                          % (CFG_BINDIR, \
                             curdir, \
                             CFG_WEBSUBMIT_BIBCONVERTCONFIGDIR, \
                             source_tpl, \
                             CFG_WEBSUBMIT_BIBCONVERTCONFIGDIR, \
                             create_tpl, \
                             curdir, \
                             CFG_WEBSUBMIT_DUMMY_XML_NAME)
    errcode_bibconvert = os.system(cmd_bibconvert_call)
    if errcode_bibconvert:
        ## There was a problem creating the dummy MARC XML record. Fail.
        err_msg = "Error: Unable to create dummy MARC XML record [%s/%s]. " \
                  "Bibconvert failed with error code [%s]." \
                  % (curdir, \
                     CFG_WEBSUBMIT_DUMMY_XML_NAME, \
                     errcode_bibconvert)
        raise InvenioWebSubmitFunctionError(err_msg)

    ## Bibconvert doesn't escape stuff for XML. Read the dummy record into
    ## memory, replace any "&" or "<" with "&amp;" and "&lt;", then re-write
    ## the dummy MARC XML record to the current dir:
    try:
        fp_dummyrec = open("%s/%s" % (curdir, \
                                      CFG_WEBSUBMIT_DUMMY_XML_NAME), "r")
        record_text = fp_dummyrec.read()
        fp_dummyrec.close()
    except IOError:
        ## Couldn't read the contents of dummy_marcxml_rec.
        err_msg = "Error: Unable to create dummy MARC XML record [%s/%s]. " \
                  "Bibconvert reported no error, but the record was " \
                  "unreadable later." % (curdir, CFG_WEBSUBMIT_DUMMY_XML_NAME)
        register_exception(req=req_obj, prefix=err_msg)
        raise InvenioWebSubmitFunctionError(err_msg)

    # Escape XML-reserved chars and clean the unsupported ones (mainly
    # control characters)
    record_text = wash_for_xml(record_text)
    ## Replace the "&":
    record_text = record_text.replace("&amp;", "&")
    record_text = record_text.replace("&", "&amp;")
    ## Now replace the "<":
    record_text = record_text.replace("<", "&lt;")
    ## Having replaced "<" everywhere in the record, put it back in known
    ## MARC XML tags:
    record_text = record_text.replace("&lt;record", "<record")
    record_text = record_text.replace("&lt;/record", "</record")
    record_text = record_text.replace("&lt;datafield", "<datafield")
    record_text = record_text.replace("&lt;/datafield", "</datafield")
    record_text = record_text.replace("&lt;controlfield", "<controlfield")
    record_text = record_text.replace("&lt;/controlfield", "</controlfield")
    record_text = record_text.replace("&lt;subfield", "<subfield")
    record_text = record_text.replace("&lt;/subfield", "</subfield")

    ## Finally, re-write the dummy MARC XML record to the current submission's
    ## working directory:
    try:
        fp_dummyrec = open("%s/%s" % (curdir, \
                                      CFG_WEBSUBMIT_DUMMY_XML_NAME), "w")
        fp_dummyrec.write(record_text)
        fp_dummyrec.flush()
        fp_dummyrec.close()
    except IOError as err:
        ## Unable to write the dummy MARC XML record to curdir.
        err_msg = "Error: Unable to create dummy MARC XML record [%s/%s]. " \
                  "After having escaped its data contents for XML, it could " \
                  "not be written back to the submission's working directory." \
                  % (curdir, CFG_WEBSUBMIT_DUMMY_XML_NAME)
        register_exception(req=req_obj, prefix=err_msg)
        raise InvenioWebSubmitFunctionError(err_msg)
    ## Return an empty string:
    return ""
Exemple #3
0
def Ask_For_Record_Details_Confirmation(parameters, \
                                        curdir, \
                                        form, \
                                        user_info=None):
    """
       Display the details of a record on which some operation is to be carried
       out and prompt for the user's confirmation that it is the correct record.
       Upon the clicking of the confirmation button, augment step by one.

       Given the "recid" (001) of a record, retrieve the basic metadata
       (title, report-number(s) and author(s)) and display them in the
       user's browser along with a prompt asking them to confirm that
       it is indeed the record that they expected to see.

       The function depends upon the presence of the "sysno" global and the
       presence of the "step" field in the "form" parameter.
       When the user clicks on the "confirm" button, step will be augmented by
       1 and the form will be submitted.
       @parameters: None.
       @return: None.
       @Exceptions raise: InvenioWebSubmitFunctionError if problems are
        encountered;
        InvenioWebSubmitFunctionStop in order to display the details of the
        record and the confirmation message.
    """
    global sysno

    ## Make sure that we know the current step:
    try:
        current_step = int(form['step'])
    except TypeError:
        ## Can't determine step.
        msg = "Unable to determine submission step. Cannot continue."
        raise InvenioWebSubmitFunctionError(msg)
    else:
        newstep = current_step + 1

    ## Make sure that the sysno is valid:
    try:
        working_recid = int(sysno)
    except TypeError:
        ## Unable to find the details of this record - cannot query the database
        msg = "Unable to retrieve details of record - record id was invalid."
        raise InvenioWebSubmitFunctionError(msg)

    if not record_exists(working_recid):
        ## Record doesn't exist.
        msg = "Unable to retrieve details of record [%s] - record does not " \
              "exist." % working_recid
        raise InvenioWebSubmitFunctionError(msg)

    ## Retrieve the details to be displayed:
    ##
    ## Author(s):
    rec_authors = ""
    rec_first_author = print_record(int(sysno), 'tm', "100__a")
    rec_other_authors = print_record(int(sysno), 'tm', "700__a")
    if rec_first_author != "":
        rec_authors += "".join(["%s<br />\n" % cgi.escape(author.strip()) for \
                                author in rec_first_author.split("\n")])
    if rec_other_authors != "":
        rec_authors += "".join(["%s<br />\n" % cgi.escape(author.strip()) for \
                                author in rec_other_authors.split("\n")])

    ## Title:
    rec_title = "".join(["%s<br />\n" % cgi.escape(title.strip()) for title in \
                          print_record(int(sysno), 'tm', "245__a").split("\n")])

    ## Report numbers:
    rec_reportnums = ""
    rec_reportnum = print_record(int(sysno), 'tm', "037__a")
    rec_other_reportnums = print_record(int(sysno), 'tm', "088__a")
    if rec_reportnum != "":
        rec_reportnums += "".join(["%s<br />\n" % cgi.escape(repnum.strip()) \
                                   for repnum in rec_reportnum.split("\n")])
    if rec_other_reportnums != "":
        rec_reportnums += "".join(["%s<br />\n" % cgi.escape(repnum.strip()) \
                                   for repnum in \
                                   rec_other_reportnums.split("\n")])

    raise InvenioWebSubmitFunctionStop(CFG_DOCUMENT_DETAILS_MESSAGE % \
                                  { 'report-numbers' : rec_reportnums, \
                                    'title'          : rec_title, \
                                    'author'         : rec_authors, \
                                    'newstep'        : newstep, \
                                    'admin-email'    : CFG_SITE_ADMIN_EMAIL, \
                                  }   )
Exemple #4
0
def Convert_RecXML_to_RecALEPH_DELETE(parameters,
                                      curdir,
                                      form,
                                      user_info=None):
    """
       Function to create an ALEPH 500 MARC DELETE record from a MARC XML
       record.

       This function depends upon the following:

         * "recmysql" is a file that already exists in the working
            submission directory. I.e. "Make_Record" has already been called and
            the MARC XML record created.

         * "recmysql" must contain an ALEPH 500 SYS in the field "970__a". That
            is to say, the function "Allocate_ALEPH_SYS" should have been called
            and an ALEPH 500 SYS allocated to this record.
            *** NOTE: "xmlmarc2textmarc" is left to check for this in the record
                      It is run in --aleph-marc=d mode, which creates an ALEPH
                      "delete" record.

       Given the valid "recmysql" in the working submission directory, this
       function will use the "xmlmarc2textmarc" tool to convert that record into
       the ALEPH MARC record. The record will then be written into the file
       "recaleph500" in the current working submission directory.

       @parameters: None
       @return: (string) - Empty string.
    """
    ## If recmysql does not exist in the current working submission directory,
    ## or it is not readable, fail by raising a InvenioWebSubmitFunctionError:
    if not access("%s/recmysql" % curdir, R_OK | W_OK):
        ## FAIL - recmysql cannot be accessed:
        msg = """No recmysql in submission dir %s - """ \
              """Cannot create recaleph500!""" % curdir
        raise InvenioWebSubmitFunctionError(msg)

    ## Wash possible xml-invalid characters in recmysql
    recmysql_fd = file(os.path.join(curdir, 'recmysql'), 'r')
    recmysql = recmysql_fd.read()
    recmysql_fd.close()

    recmysql = wash_for_xml(recmysql)

    recmysql_fd = file(os.path.join(curdir, 'recmysql'), 'w')
    recmysql_fd.write(recmysql)
    recmysql_fd.close()

    ## Command to perform conversion of recmysql -> recaleph500:
    convert_cmd = \
     """%(bindir)s/xmlmarc2textmarc --aleph-marc=d %(curdir)s/recmysql > """ \
     """%(curdir)s/recaleph500""" \
     % { 'bindir' : CFG_BINDIR,
         'curdir' : curdir,
       }
    ## Perform the conversion of MARC XML record to ALEPH500 record:
    pipe_in, pipe_out, pipe_err = os.popen3("%s" % convert_cmd)
    pipe_in.close()
    pipe_out.close()
    conversion_errors = pipe_err.readlines()
    pipe_err.close()

    ## Check that the conversion was performed without error:
    if conversion_errors != []:
        ## It was not possible to successfully create the ALEPH500
        ## record, quit:
        msg = """An error was encountered when attempting to """ \
              """convert %s/recmysql into recaleph500 - stopping [%s]""" \
              % (curdir, "".join(conversion_errors))
        raise InvenioWebSubmitFunctionError(msg)

    ## Check for presence of recaleph500 in the current
    ## working submission directory:
    if not access("%s/recaleph500" % curdir, R_OK | W_OK):
        ## Either not present, or not readable - ERROR
        msg = """An error was encountered when attempting to convert """ \
              """%s/recmysql into recaleph500. After the conversion, """ \
              """recaleph500 could not be accessed.""" % curdir
        raise InvenioWebSubmitFunctionError(msg)

    ## Everything went OK:
    return ""
Exemple #5
0
def Get_Recid(parameters, curdir, form, user_info=None):
    """
    Given the report number of a record (the global "rn"), retrieve the
    "recid" (001).
    The function first of all checks for the existence of the file "SN" in
    the current submission's working directory. If it exists, it is read in
    and used as the "recid".
    Otherwise, this function will contact the database in order to obtain the
    recid of a record. In this case, a check will be done in order to assure
    that the recid can be handled by this submission.

    Parameters: record_search_pattern - this enforces restrictions on which type
    of documents can be modified via a certain submission interface. If the
    record_search_pattern is not defined, no restriction will be enforced.
    The record_search_pattern can be anything that can be used by
    search_pattern to search for. Also, one can use variables stored locally,
    like &lt;comboDEMOJRN&gt; to denote the category or subcategory.
    Ex:
        reportnumber:DEMO-&lt;comboDEMOJRN&gt;-*
        collection:ATLANTISTIMESNEWS
        reportnumber:DEMO-&lt;comboDEMOJRN&gt;-* | collection:ATLANTISTIMESNEWS
    As a note, you can test your pattern, using the search engine
    and see if it retrieves the expected results.

    WARNING: this check is not applied if a SN file already exists in
    curdir.

    Unless file curdir/SN exists, the function depends upon the global
    value 'rn' having been set (for eg. by calling Get_Report_Number'
    prior to this function) It will use this value when searching for
    a record. Note: If 'rn' is empty, the search for the document will
    not be conducted.

    Exceptions raised:
        + InvenioWebSubmitFunctionError:
                          - if unable to open curdir/SN for reading;
                          - if unable to open curdir/SN for writing;
        + InvenioWebSubmitFunctionStop:
                          - if the global 'rn' is empty (no rn to search with);
                          - if no recid found for 'rn' value;
                          - if multiple recids found for 'rn' value;
                          - if recid should not be handled by the current submission;
    """
    global rn, sysno
    ## initialize sysno
    sysno = ""

    if access("%s/SN" % curdir, F_OK|R_OK):
        ## SN exists and should contain the recid; get it from there.
        try:
            fptr = open("%s/SN" % curdir, "r")
        except IOError:
            ## Unable to read the SN file's contents
            msg = """Unable to correctly read the current submission's recid"""
            raise InvenioWebSubmitFunctionError(msg)
        else:
            ## read in the submission details:
            sysno = fptr.read().strip()
            fptr.close()
    else:
        ## SN doesn't exist; Check the DB for a record with this reportnumber.

        ## First, if rn is empty, don't conduct the search:
        if rn.strip() in ("", None):
            ## No report-numer provided:
            raise InvenioWebSubmitFunctionStop(CFG_ALERT_DOCUMENT_NOT_FOUND \
                                               % "NO REPORT NUMBER PROVIDED")

        ## Get a list of recids of LIVE records associated with the report num
        recids = get_existing_records_for_reportnumber(rn)

        ## There should only be 1 _existing_ record for the report-number:
        if len(recids) == 1:
            ## Only one record found - save it to a text file called SN
            ## in the current submission's working directory:
            try:
                fptr = open("%s/SN" % curdir, "w")
            except IOError:
                ## Unable to read the SN file's contents
                msg = """Unable to save the recid for report [%s]""" \
                         % rn
                raise InvenioWebSubmitFunctionError(msg)
            else:
                ## Save recid to SN and to the global scope:
                sysno = recids[0]
                fptr.write("%s" % sysno)
                fptr.flush()
                fptr.close()
        elif len(recids) < 1:
            ## No recid found for this report number:
            msg = CFG_ALERT_DOCUMENT_NOT_FOUND % rn
            raise InvenioWebSubmitFunctionStop(msg)
        else:
            ## Multiple recids found for this report-number:
            msg = CFG_ALERT_MULTIPLE_DOCUMENTS_FOUND % rn
            raise InvenioWebSubmitFunctionStop(msg)

        ## Everything seems to have run smoothly:
        ## check if the record needs to comply to any restriction
        ## basically checks if this record can/should be handled by this submission
        if parameters['record_search_pattern']:
            if not is_record_matching_pattern(parameters['record_search_pattern'], sysno, curdir):
                # delete the SN file and reset the sysno,
                # because this record is not the good record to be hadled by this submission
                rename("%s/SN" % curdir, "%s/SN_WRONG" % curdir)
                sysno = ""
                raise InvenioWebSubmitFunctionStop(CFG_ALERT_WRONG_RECORD_FOR_THIS_SUBMISSION)
    return ""
def Register_Approval_Request(parameters, curdir, form, user_info=None):
    """
    This function is used at the time of a "request for approval" submission
    in order to register the request in the WebSubmit "Approvals" DB
    (sbmAPPROVAL).
    At the time of approval request, the document could be in one of
    several different approval "states" and depending upon that state,
    the action taken by this function differs. The states are as
    follows:
          * Approval for the document has never been requested.
             -> In this case, a new row for the document is inserted into the
                approvals table with the "waiting" state.
          * Approval of the document has previously been requested and it is
            still in the "waiting" state.
             -> In this case, the date of last request for the document is
                updated in the approvals table.
          * Approval of the document has previously been requested, but the
            document was rejected.
             -> In this case, the function will halt the submission with a
                message informing the user that approval of the document was
                already rejected.
          * Approval of the document has previously been requested and it has
            been approved.
             -> In this case, the function will halt the submission with a
                message informing the user that the document has already
                been approved and that no further action is necessary.
          * Approval of the document has previously been requested, but the
            request withdrawn.
             -> In this case, the function will update the "approval status"
                of the document to "waiting" and will return a message
                informing the user that although the approval request was
                previously withdrawn, it has been requested again.

    @param categ_file_appreq: (string) - some document types are
           separated into different categories, each of which has its own
           referee(s).
           In such document types, it's necessary to know the document-
           type's category in order to choose the referee.
           This parameter provides a means by which the category information
           can be extracted from a file in the current submission's working
           directory. It should therefore be a filename.

    @param categ_rnseek_appreq: (string) - some document types are
           separated into different categories, each of which has its own
           referee(s).
           In such document types, it's necessary to know the document-
           type's category in order to choose the referee.
           This parameter provides a means by which the category information
           can be extracted from the document's reference number.
           It is infact a string that will be compiled into a regexp and
           an attempt will be made to match it agains the document's reference
           number starting from the left-most position.
           The only pre-requisite is that the segment in which the category is
           sought should be indicated with <CATEGORY>.
           Thus, an example might be as follows:
              ATL(-COM)?-<CATEGORY>-.+
           This would allow "PHYS" in the following reference number to be
           recognised as the category:
              ATL-COM-PHYS-2008-001

    @param note_file_appreq: (string) - the name of the file from which
           any "notes" to be added into the approval request's "note" field in
           the database are to be read. (File must be in the current submission's
           working directory.)

    @return: (string) - a message for the user.

    @Exceptions raised: + InvenioWebSubmitFunctionStop when the submission
                          should be halted.
                        + InvenioWebSubmitFunctionError when an unexpected
                          error has been encountered and execution cannot
                          continue.
    """
    ## Get the reference number (as global rn - sorry!) and the document type:
    global rn
    doctype = form['doctype']

    ## A string variable to contain any information that should be displayed
    ## in the user's browser:
    info_out = ""

    ########
    ## Get the parameters from the list:

    ########
    ## Get the name of the category file:
    #######
    try:
        ## If it has been provided, get the name of the file in which the
        ## category is stored:
        category_file = parameters["categ_file_appreq"]
    except KeyError:
        ## No value given for the category file:
        category_file = None
    else:
        if category_file is not None:
            category_file = str(category_file)
            category_file = os.path.basename(category_file).strip()
            if category_file == "":
                category_file = None
    ########
    ## Get the name of the "note" file and read its value:
    ########
    note = ""  ## variable to hold a note to be added to the approval request's
    ## details in the DB.
    try:
        note_file = parameters["note_file_appreq"]
    except KeyError:
        ## No value given for the category file:
        note_file = None
    else:
        if note_file is not None:
            note_file = str(note_file)
            note_file = os.path.basename(note_file).strip()
            if note_file == "":
                note_file = None
    if note_file is not None:
        note = ParamFromFile("%s/%s" % (curdir, note_file))
    ########
    ## Get the regexp that is used to find the category in the report number:
    ########
    try:
        ## If it has been provided, get the regexp used for identifying
        ## a document-type's category from its reference number:
        category_rn_regexp = parameters["categ_rnseek_appreq"]
    except KeyError:
        ## No value given for the category regexp:
        category_rn_regexp = None
    else:
        if category_rn_regexp is not None:
            category_rn_regexp = str(category_rn_regexp).strip()
        if category_rn_regexp == "":
            category_rn_regexp = None

    #######
    ## Resolve the document type's category:
    ##
    ## This is a long process. The end result is that the category is extracted
    ## either from a file in curdir, or from the report number.
    ## If it's taken from the report number, the admin must configure the
    ## function to accept a regular expression that is used to find the
    ## category in the report number.
    ##
    if category_file is not None and category_rn_regexp is not None:
        ## It is not valid to have both a category file and a pattern
        ## describing how to extract the category from a report number.
        ## raise an InvenioWebSubmitFunctionError
        msg = "Error in Register_Approval_Request function: received " \
              "instructions to search for the document's category in " \
              "both its report number AND in a category file. Could " \
              "not determine which to use - please notify the " \
              "administrator."
        raise InvenioWebSubmitFunctionError(msg)
    elif category_file is not None:
        ## Attempt to recover the category information from a file in the
        ## current submission's working directory:
        category = ParamFromFile("%s/%s" % (curdir, category_file))
        if category is not None:
            category = category.strip()
        if category in (None, ""):
            ## The category cannot be resolved.
            msg = "Error in Register_Approval_Request function: received " \
                  "instructions to search for the document's category in " \
                  "a category file, but could not recover the category " \
                  "from that file. An approval request therefore cannot " \
                  "be registered for the document."
            raise InvenioWebSubmitFunctionError(msg)
    elif category_rn_regexp is not None:
        ## Attempt to recover the category information from the document's
        ## reference number using the regexp in category_rn_regexp:
        ##
        ## Does the category regexp contain the key-phrase "<CATEG>"?
        if category_rn_regexp.find("<CATEG>") != -1:
            ## Yes. Replace "<CATEG>" with "(?P<category>.+?)".
            ## For example, this:
            ##    ATL(-COM)?-<CATEG>-
            ## Will be transformed into this:
            ##    ATL(-COM)?-(?P<category>.+?)-
            category_rn_final_regexp = \
                category_rn_regexp.replace("<CATEG>", r"(?P<category>.+?)", 1)
        else:
            ## The regexp for category didn't contain "<CATEG>", but this is
            ## mandatory.
            msg = "Error in Register_Approval_Request function: The " \
                  "[%(doctype)s] submission has been configured to search " \
                  "for the document type's category in its reference number, " \
                  "using a poorly formed search expression (no marker for " \
                  "the category was present.) Since the document's category " \
                  "therefore cannot be retrieved, an approval request cannot " \
                  "be registered for it. Please report this problem to the " \
                  "administrator." \
                  % { 'doctype' : doctype, }
            raise InvenioWebSubmitFunctionError(msg)
        ##
        try:
            ## Attempt to compile the regexp for finding the category:
            re_categ_from_rn = re.compile(category_rn_final_regexp)
        except sre_constants.error:
            ## The expression passed to this function could not be compiled
            ## into a regexp. Register this exception and raise an
            ## InvenioWebSubmitFunctionError:
            exception_prefix = "Error in Register_Approval_Request function: " \
                               "The [%(doctype)s] submission has been " \
                               "configured to search for the document type's " \
                               "category in its reference number, using the " \
                               "following regexp: /%(regexp)s/. This regexp, " \
                               "however, could not be compiled correctly " \
                               "(created it from %(categ-search-term)s.)" \
                               % { 'doctype'       : doctype, \
                                   'regexp'        : category_rn_final_regexp, \
                                   'categ-search-term' : category_rn_regexp, }
            register_exception(prefix=exception_prefix)
            msg = "Error in Register_Approval_Request function: The " \
                  "[%(doctype)s] submission has been configured to search " \
                  "for the document type's category in its reference number, " \
                  "using a poorly formed search expression. Since the " \
                  "document's category therefore cannot be retrieved, an " \
                  "approval request cannot be registered for it. Please " \
                  "report this problem to the administrator." \
                  % { 'doctype' : doctype, }
            raise InvenioWebSubmitFunctionError(msg)
        else:
            ## Now attempt to recover the category from the RN string:
            m_categ_from_rn = re_categ_from_rn.match(rn)
            if m_categ_from_rn is not None:
                ## The pattern matched in the string.
                ## Extract the category from the match:
                try:
                    category = m_categ_from_rn.group("category")
                except IndexError:
                    ## There was no "category" group. That group is mandatory.
                    exception_prefix = \
                       "Error in Register_Approval_Request function: The " \
                       "[%(doctype)s] submission has been configured to " \
                       "search for the document type's category in its " \
                       "reference number using the following regexp: " \
                       "/%(regexp)s/. The search produced a match, but " \
                       "there was no \"category\" group in the match " \
                       "object although this group is mandatory. The " \
                       "regexp was compiled from the following string: " \
                       "[%(categ-search-term)s]." \
                       % { 'doctype'           : doctype, \
                           'regexp'            : category_rn_final_regexp, \
                           'categ-search-term' : category_rn_regexp, }
                    register_exception(prefix=exception_prefix)
                    msg = "Error in Register_Approval_Request function: The " \
                          "[%(doctype)s] submission has been configured to " \
                          "search for the document type's category in its " \
                          "reference number, using a poorly formed search " \
                          "expression (there was no category marker). Since " \
                          "the document's category therefore cannot be " \
                          "retrieved, an approval request cannot be " \
                          "registered for it. Please report this problem to " \
                          "the administrator." \
                          % { 'doctype' : doctype, }
                    raise InvenioWebSubmitFunctionError(msg)
                else:
                    category = category.strip()
                    if category == "":
                        msg = "Error in Register_Approval_Request function: " \
                              "The [%(doctype)s] submission has been " \
                              "configured to search for the document type's " \
                              "category in its reference number, but no " \
                              "category was found. The request for approval " \
                              "cannot be registered. Please report this " \
                              "problem to the administrator." \
                              % { 'doctype' : doctype, }
                        raise InvenioWebSubmitFunctionError(msg)
            else:
                ## No match. Cannot find the category and therefore cannot
                ## continue:
                msg = "Error in Register_Approval_Request function: The " \
                      "[%(doctype)s] submission has been configured to " \
                      "search for the document type's category in its " \
                      "reference number, but no match was made. The request " \
                      "for approval cannot be registered. Please report " \
                      "this problem to the administrator." \
                      % { 'doctype' : doctype, }
                raise InvenioWebSubmitFunctionError(msg)
    else:
        ## The document type has no category.
        category = ""
    ##
    ## End of category recovery
    #######

    #######
    ##
    ## Query the "approvals" DB table to determine whether approval of this
    ## document has already been requested:
    approval_status = get_simple_approval_status(doctype, rn)
    if approval_status is None:
        ## Approval has never been requested for this document. Register the
        ## new request.
        register_new_approval_request(doctype, category, rn, note)
    elif approval_status.lower() == "approved":
        ## This document has already been approved. Stop and inform the user
        ## of this.
        msg = """
<br />
<div>
<span style="color: red;">Note:</span> The document %s has already been
 Approved.<br />
No further approval is necessary - no further action will be taken.
</div>
""" % cgi.escape(rn)
        raise InvenioWebSubmitFunctionStop(msg)
    elif approval_status.lower() == "rejected":
        ## This document has already been rejected. Stop and inform the user
        ## of this.
        msg = """
<br />
<div>
<span style="color: red;">Note:</span> Approval of the document [%s] has
 previously been rejected.<br />
Approval has NOT been resubmitted and no further action will be taken.<br />
If you believe this to be an error, please contact %s, quoting the<br />
document's report-number [%s] and describing the problem.
</div>
""" % (cgi.escape(rn), cgi.escape(CFG_SITE_SUPPORT_EMAIL), cgi.escape(rn))
        raise InvenioWebSubmitFunctionStop(msg)
    elif approval_status.lower() == "withdrawn":
        ## An approval request for this document type was already made at some
        ## point. Update it and inform the user that the approval request has
        ## been logged despite having been previously withdrawn:
        update_approval_request_status(doctype, rn, note=note)
        info_out += """
<br />
<div>
<span style="color: red;">Note:</span> An approval request for this document
 had previously been withdrawn.<br />
Approval has been requested again.
</div>
"""
    elif approval_status.lower() == "waiting":
        ## An approval request for this document has already been registered
        ## but it is awaiting a decision.
        ## Update the date/time of the last request and inform the user that
        ## although approval had already been requested for this document,
        ## their approval request has been made again.
        update_approval_request_status(doctype, rn, note=note)
        info_out += """
<br />
<div>
<span style="color: red;">Note:</span> Although a request for the approval
 of this document had already been submitted, your new request has been
 registered.<br />
</div>
"""
    else:
        ## The document had an unrecognised "status". Raise an error.
        msg = "Error in Register_Approval_Request function: The " \
              "[%(reportnum)s] document has an unknown approval status " \
              "(%(status)s). Unable to request its approval. Please report " \
              "this problem to the administrator." \
              % { 'reportnum' : rn,
                  'status'    : approval_status, }
        raise InvenioWebSubmitFunctionError(msg)
    ##
    ## Finished - return any message to be displayed on the user's screen.
    return info_out
Exemple #7
0
def Link_Records(parameters, curdir, form, user_info=None):
    """
    This function create a MARC link between two records (the 1st specified in the
    edsrn file or SN, the second specified by edsrn2 file, where you can store
    the reportnumber or directly the recid.

    Parameters:

     * edsrn: the file containing the report number or recid of the
       first record (A) to be linked.

     * edsrn2: the file containing the report number(s) or recid(s) of
       the second record(s) (B) to be linked (one value per line).

     * In "directRelationship" you should specify either the name of a file (by using
       <pa>file:filename</pa>) or directly, what is the relationship
       of the second record to be stored in the metadata of the 1st record (A->B).
       Use the keyword "none" if you explicitely want to skip the recording of
       this relation (no modification of record A).

     * In the value/file "reverseRelationship" you can similarly specify the other
       direction of the arrow (B->A)
       Use the keyword "none" if you explicitely want to skip the recording of
       this relation (no modification of record(s) B).

     * keep_original_edsrn2: if edsrn2 is a report number, should we
       use it as label when linking, or shall we use instead the
       report number retrieved from the matching record?

     * directRelationshipMARC: in which MARC tag + indicators shall we
       store the relation in the first record (A). By default uses the
       value found in tag name "other relationship entry" or 7870_.
       The value can be directly provided or specifed in file (using
       <pa>file:filename</pa>)

     * reverseRelationshipMARC: in which MARC tag + indicators shall we
       store the relation in the second record (B). By default uses the
       value found in tag name "other relationship entry" or 7870_.
       The value can be directly provided or specifed in file (using
       <pa>file:filename</pa>)

     * bibuploadMode: shall the created XML be sent in --append mode
       (default) or using --correct. Possible values are:
         * append (or leave empty)
         * correct
         This setting will depend on how you have set up your
         submisson workflow.

     * silentFailures: if set to "True", do not raise an exception
       when the linking fails due to impossibility to retrieve the
       corresponding "remote" record(s) B (for eg. non-existing report
       number, report number matching several records, etc.). In these
       cases the faulty report number is ignored.

     * considerEmpty: when using bibuploadMode with 'correct', should
       missing linking information (edsrn2 values) removes the linking
       or simply not do anything? You might want to tweak this setting
       depending on how the submission is presenting MBI pages (either
       full form, or selected fields).  If False (or empty), and no
       linking information is provided, the linking is not removed
       from the original record.  If True (or any other value), and no
       linking information is provided, the linking is removed from
       the record.  The value can be directly provided or specifed in
       file (using <pa>file:filename</pa>)
    """
    global sysno
    edsrn = parameters["edsrn"]
    edsrn2 = parameters["edsrn2"]
    direct_relationship = parameters["directRelationship"]
    reverse_relationship = parameters["reverseRelationship"]
    keep_original_edsrn2 = parameters.get("keep_original_edsrn2", "True")
    if keep_original_edsrn2 == "True":
        keep_original_edsrn2 = True
    elif keep_original_edsrn2 == "False":
        keep_original_edsrn2 = False
    else:
        keep_original_edsrn2 = True
    direct_relationship_MARC = parameters["directRelationshipMARC"]
    reverse_relationship_MARC = parameters["reverseRelationshipMARC"]
    bibupload_mode = parameters["bibuploadMode"]
    if not bibupload_mode in ('append', 'correct'):
        bibupload_mode = 'append'
    silent_failures_p = parameters.get("silentFailures", "True") == 'True'

    consider_empty_p = parameters.get("considerEmpty", "False")
    g = RE_FILENAME.match(consider_empty_p)
    if g:
        filename = g.group(1)
        if exists(join(curdir, filename)):
            consider_empty_p = open(join(curdir, filename)).read().strip()
        else:
            consider_empty_p = ''
    if consider_empty_p in ('False', ''):
        consider_empty_p = False
    else:
        consider_empty_p = True

    recid_a = int(sysno)
    if exists(join(curdir, edsrn)):
        rn_a = open(join(curdir, edsrn)).read().strip()
    else:
        rn_a = ""
    if not rn_a:
        try:
            recid_a, rn_a = get_recid_and_reportnumber(recid=sysno)
        except ValueError as err:
            raise InvenioWebSubmitFunctionError(
                "Error in finding the current record and its reportnumber: %s"
                % err)

    g = RE_FILENAME.match(direct_relationship)
    if g:
        filename = g.group(1)
        if exists(join(curdir, filename)):
            direct_relationship = open(join(curdir, filename)).read().strip()
    if not direct_relationship:
        raise InvenioWebSubmitFunctionError(
            "Can not retrieve direct relationship")
    elif direct_relationship == 'none':
        direct_relationship = None

    g = RE_FILENAME.match(reverse_relationship)
    if g:
        filename = g.group(1)
        if exists(join(curdir, filename)):
            reverse_relationship = open(join(curdir, filename)).read().strip()
    if not reverse_relationship:
        raise InvenioWebSubmitFunctionError(
            "Can not retrieve reverse relationship")
    elif reverse_relationship == 'none':
        reverse_relationship = None

    g = RE_FILENAME.match(direct_relationship_MARC)
    if g:
        filename = g.group(1)
        if exists(join(curdir, filename)):
            direct_relationship_MARC = open(join(curdir,
                                                 filename)).read().strip()

    g = RE_FILENAME.match(reverse_relationship_MARC)
    if g:
        filename = g.group(1)
        if exists(join(curdir, filename)):
            reverse_relationship_MARC = open(join(curdir,
                                                  filename)).read().strip()

    recids_and_rns_b = []
    if exists(join(curdir, edsrn2)):
        for rn_b in open(join(curdir, edsrn2)).readlines():
            rn_b = rn_b.strip()
            if not rn_b:
                continue

            if rn_b.isdigit():
                recid_b = int(rn_b)
                rn_b = ""
                try:
                    recid_b, rn_b = get_recid_and_reportnumber(recid=recid_b)
                except ValueError, err:
                    if silent_failures_p:
                        continue
                    raise
            else:
                try:
                    recid_b, rn_b = get_recid_and_reportnumber(
                        reportnumber=rn_b,
                        keep_original_reportnumber=keep_original_edsrn2)
                except ValueError, err:
                    if silent_failures_p:
                        continue
                    raise
            recids_and_rns_b.append((recid_b, rn_b))
def Mail_Approval_Request_to_Referee(parameters, curdir, form, user_info=None):
    """
    This function sends an email to the referee of a document informing
    him/her that a request for its approval has been submitted by the
    user.

    @param categ_file_appreq: (string) - some document types are
          separated into different categories, each of which has its own
          referee(s).
          In such document types, it's necessary to know the document-
          type's category in order to choose the referee.
          This parameter provides a means by which the category information
          can be extracted from a file in the current submission's working
          directory. It should therefore be a filename.

    @param categ_rnseek_appreq: (string) - some document types are
          separated into different categories, each of which has its own
          referee(s).
          In such document types, it's necessary to know the document-
          type's category in order to choose the referee.
          This parameter provides a means by which the category information
          can be extracted from the document's reference number.
          It is infact a string that will be compiled into a regexp and
          an attempt will be made to match it agains the document's reference
          number starting from the left-most position.
          The only pre-requisite is that the segment in which the category is
          sought should be indicated with <CATEGORY>.
          Thus, an example might be as follows:
             ATL(-COM)?-<CATEGORY>-.+

          This would allow "PHYS" in the following reference number to be
          recognised as the category:
             ATL-COM-PHYS-2008-001

    @param edsrn: (string) - the name of the field in which the report
          number should be placed when the referee visits the form for making
          a decision.

    @return: (string) - empty string.
    """
    ## Get the reference number (as global rn - sorry!) and the document type:
    global sysno, rn
    doctype = form['doctype']

    ########
    ## Get the parameters from the list:

    ########
    ## Get the name of the report-number file:
    ########
    try:
        edsrn_file = parameters["edsrn"]
    except KeyError:
        ## No value given for the edsrn file:
        msg = "Error in Mail_Approval_Request_to_Referee function: unable " \
              "to determine the name of the file in which the document's " \
              "report number should be stored."
        raise InvenioWebSubmitFunctionError(msg)
    else:
        edsrn_file = str(edsrn_file)
        edsrn_file = os.path.basename(edsrn_file).strip()
        if edsrn_file == "":
            msg = "Error in Mail_Approval_Request_to_Referee function: " \
                  "unable to determine the name of the file in which " \
                  "the document's report number should be stored."
            raise InvenioWebSubmitFunctionError(msg)
    ########
    ## Get the name of the category file:
    #######
    try:
        ## If it has been provided, get the name of the file in which the
        ## category is stored:
        category_file = parameters["categ_file_appreq"]
    except KeyError:
        ## No value given for the category file:
        category_file = None
    else:
        if category_file is not None:
            category_file = str(category_file)
            category_file = os.path.basename(category_file).strip()
            if category_file == "":
                category_file = None
    ########
    ## Get the regexp that is used to find the category in the report number:
    ########
    try:
        ## If it has been provided, get the regexp used for identifying
        ## a document-type's category from its reference number:
        category_rn_regexp = parameters["categ_rnseek_appreq"]
    except KeyError:
        ## No value given for the category regexp:
        category_rn_regexp = None
    else:
        if category_rn_regexp is not None:
            category_rn_regexp = str(category_rn_regexp).strip()
        if category_rn_regexp == "":
            category_rn_regexp = None
    #######
    ## Resolve the document type's category:
    ##
    ## This is a long process. The end result is that the category is extracted
    ## either from a file in curdir, or from the report number.
    ## If it's taken from the report number, the admin must configure the
    ## function to accept a regular expression that is used to find the
    ## category in the report number.
    ##
    if category_file is not None and category_rn_regexp is not None:
        ## It is not valid to have both a category file and a pattern
        ## describing how to extract the category from a report number.
        ## raise an InvenioWebSubmitFunctionError
        msg = "Error in Register_Approval_Request function: received " \
              "instructions to search for the document's category in " \
              "both its report number AND in a category file. Could " \
              "not determine which to use - please notify the " \
              "administrator."
        raise InvenioWebSubmitFunctionError(msg)
    elif category_file is not None:
        ## Attempt to recover the category information from a file in the
        ## current submission's working directory:
        category = ParamFromFile("%s/%s" % (curdir, category_file))
        if category is not None:
            category = category.strip()
        if category in (None, ""):
            ## The category cannot be resolved.
            msg = "Error in Register_Approval_Request function: received " \
                  "instructions to search for the document's category in " \
                  "a category file, but could not recover the category " \
                  "from that file. An approval request therefore cannot " \
                  "be registered for the document."
            raise InvenioWebSubmitFunctionError(msg)
    elif category_rn_regexp is not None:
        ## Attempt to recover the category information from the document's
        ## reference number using the regexp in category_rn_regexp:
        ##
        ## Does the category regexp contain the key-phrase "<CATEG>"?
        if category_rn_regexp.find("<CATEG>") != -1:
            ## Yes. Replace "<CATEG>" with "(?P<category>.+?)".
            ## For example, this:
            ##    ATL(-COM)?-<CATEG>-
            ## Will be transformed into this:
            ##    ATL(-COM)?-(?P<category>.+?)-
            category_rn_final_regexp = \
                category_rn_regexp.replace("<CATEG>", r"(?P<category>.+?)", 1)
        else:
            ## The regexp for category didn't contain "<CATEG>", but this is
            ## mandatory.
            msg = "Error in Register_Approval_Request function: The " \
                  "[%(doctype)s] submission has been configured to search " \
                  "for the document type's category in its reference number, " \
                  "using a poorly formed search expression (no marker for " \
                  "the category was present.) Since the document's category " \
                  "therefore cannot be retrieved, an approval request cannot " \
                  "be registered for it. Please report this problem to the " \
                  "administrator." \
                  % { 'doctype' : doctype, }
            raise InvenioWebSubmitFunctionError(msg)
        ##
        try:
            ## Attempt to compile the regexp for finding the category:
            re_categ_from_rn = re.compile(category_rn_final_regexp)
        except sre_constants.error:
            ## The expression passed to this function could not be compiled
            ## into a regexp. Register this exception and raise an
            ## InvenioWebSubmitFunctionError:
            exception_prefix = "Error in Register_Approval_Request function: " \
                               "The [%(doctype)s] submission has been " \
                               "configured to search for the document type's " \
                               "category in its reference number, using the " \
                               "following regexp: /%(regexp)s/. This regexp, " \
                               "however, could not be compiled correctly " \
                               "(created it from %(categ-search-term)s.)" \
                               % { 'doctype'       : doctype, \
                                   'regexp'        : category_rn_final_regexp, \
                                   'categ-search-term' : category_rn_regexp, }
            register_exception(prefix=exception_prefix)
            msg = "Error in Register_Approval_Request function: The " \
                  "[%(doctype)s] submission has been configured to search " \
                  "for the document type's category in its reference number, " \
                  "using a poorly formed search expression. Since the " \
                  "document's category therefore cannot be retrieved, an " \
                  "approval request cannot be registered for it. Please " \
                  "report this problem to the administrator." \
                  % { 'doctype' : doctype, }
            raise InvenioWebSubmitFunctionError(msg)
        else:
            ## Now attempt to recover the category from the RN string:
            m_categ_from_rn = re_categ_from_rn.match(rn)
            if m_categ_from_rn is not None:
                ## The pattern matched in the string.
                ## Extract the category from the match:
                try:
                    category = m_categ_from_rn.group("category")
                except IndexError:
                    ## There was no "category" group. That group is mandatory.
                    exception_prefix = \
                       "Error in Register_Approval_Request function: The " \
                       "[%(doctype)s] submission has been configured to " \
                       "search for the document type's category in its " \
                       "reference number using the following regexp: " \
                       "/%(regexp)s/. The search produced a match, but " \
                       "there was no \"category\" group in the match " \
                       "object although this group is mandatory. The " \
                       "regexp was compiled from the following string: " \
                       "[%(categ-search-term)s]." \
                       % { 'doctype'           : doctype, \
                           'regexp'            : category_rn_final_regexp, \
                           'categ-search-term' : category_rn_regexp, }
                    register_exception(prefix=exception_prefix)
                    msg = "Error in Register_Approval_Request function: The " \
                          "[%(doctype)s] submission has been configured to " \
                          "search for the document type's category in its " \
                          "reference number, using a poorly formed search " \
                          "expression (there was no category marker). Since " \
                          "the document's category therefore cannot be " \
                          "retrieved, an approval request cannot be " \
                          "registered for it. Please report this problem to " \
                          "the administrator." \
                          % { 'doctype' : doctype, }
                    raise InvenioWebSubmitFunctionError(msg)
                else:
                    category = category.strip()
                    if category == "":
                        msg = "Error in Register_Approval_Request function: " \
                              "The [%(doctype)s] submission has been " \
                              "configured to search for the document type's " \
                              "category in its reference number, but no " \
                              "category was found. The request for approval " \
                              "cannot be registered. Please report this " \
                              "problem to the administrator." \
                              % { 'doctype' : doctype, }
                        raise InvenioWebSubmitFunctionError(msg)
            else:
                ## No match. Cannot find the category and therefore cannot
                ## continue:
                msg = "Error in Register_Approval_Request function: The " \
                      "[%(doctype)s] submission has been configured to " \
                      "search for the document type's category in its " \
                      "reference number, but no match was made. The request " \
                      "for approval cannot be registered. Please report " \
                      "this problem to the administrator." \
                      % { 'doctype' : doctype, }
                raise InvenioWebSubmitFunctionError(msg)
    else:
        ## The document type has no category.
        category = ""
    ##
    ## End of category recovery
    #######
    #######
    ## Get the title and author(s) from the record:
    #######
    ## Author(s):
    rec_authors = ""
    rec_first_author = print_record(int(sysno), 'tm', "100__a")
    rec_other_authors = print_record(int(sysno), 'tm', "700__a")
    if rec_first_author != "":
        rec_authors += "".join(["%s\n" % author.strip() for \
                                author in rec_first_author.split("\n")])
    if rec_other_authors != "":
        rec_authors += "".join(["%s\n" % author.strip() for \
                                author in rec_other_authors.split("\n")])
    ## Title:
    rec_title = "".join(["%s\n" % title.strip() for title in \
                          print_record(int(sysno), 'tm', "245__a").split("\n")])
    ##
    #######
    ## the normal approval action
    approve_act = 'APP'
    ## Get notes about the approval request:
    approval_notes = get_approval_request_notes(doctype, rn)
    ## Get the referee email address:
    if CFG_CERN_SITE:
        ## The referees system in CERN now works with listbox membership.
        ## List names should take the format
        ## "*****@*****.**"
        ## Make sure that your list exists!
        ## FIXME - to be replaced by a mailing alias in webaccess in the
        ## future.
        if doctype == 'ATN':  ## Special case of 'RPR' action for doctype ATN
            action = ParamFromFile("%s/%s" % (curdir, 'act')).strip()
            if action == 'RPR':
                notetype = ParamFromFile("%s/%s" %
                                         (curdir, 'ATN_NOTETYPE')).strip()
                if notetype not in ('SLIDE', 'PROC'):
                    raise InvenioWebSubmitFunctionError(
                        'ERROR function Mail_Approval_Request_to_Referee:: do not recognize notetype '
                        + notetype)
                if notetype == 'PROC':
                    approve_act = 'APR'  # RPR PROC requires APR action to approve
                    referee_listname = "*****@*****.**"
                elif notetype == 'SLIDE':  ## SLIDES approval
                    approve_act = 'APS'  # RPR SLIDE requires APS action to approve
                    referee_listname = "*****@*****.**"
                else:
                    raise InvenioWebSubmitFunctionError(
                        'ERROR function Mail_Approval_Request_to_Referee:: do not understand notetype: '
                        + notetype)
            else:
                referee_listname = "service-cds-referee-%s" % doctype.lower()
                if category != "":
                    referee_listname += "-%s" % category.lower()
            mailto_addresses = referee_listname + "@cern.ch"
            if category == 'CDSTEST':
                referee_listname = "service-cds-referee-%s" % doctype.lower()
                referee_listname += "-%s" % category.lower()
                mailto_addresses = referee_listname + "@cern.ch"
    else:
        referee_address = ""
        ## Try to retrieve the referee's email from the referee's database:
        for user in \
            acc_get_role_users(acc_get_role_id("referee_%s_%s" \
                                               % (doctype, category))):
            referee_address += user[1] + ","
        ## And if there are general referees:
        for user in \
            acc_get_role_users(acc_get_role_id("referee_%s_*" % doctype)):
            referee_address += user[1] + ","
        referee_address = re.sub(",$", "", referee_address)
        # Creation of the mail for the referee
        mailto_addresses = ""
        if referee_address != "":
            mailto_addresses = referee_address + ","
        else:
            mailto_addresses = re.sub(",$", "", mailto_addresses)
    ##
    ## Send the email:
    mail_subj = "Request for approval of [%s]" % rn
    mail_body = CFG_MAIL_BODY % \
                { 'site-name'               : CFG_SITE_NAME,
                  'CFG_SITE_RECORD'          : CFG_SITE_RECORD,
                  'report-number-fieldname' : edsrn_file,
                  'report-number'           : rn,
                  'title'                   : rec_title,
                  'authors'                 : rec_authors,
                  'site-url'                : CFG_SITE_URL,
                  'record-id'               : sysno,
                  'approval-action'         : approve_act,
                  'doctype'                 : doctype,
                  'notes'                   : approval_notes,
                  'category'                : category,
                }
    send_email(CFG_SITE_SUPPORT_EMAIL,
               mailto_addresses,
               mail_subj,
               mail_body,
               copy_to_admin=CFG_WEBSUBMIT_COPY_MAILS_TO_ADMIN)
    ##
    return ""
Exemple #9
0
def Register_Referee_Decision(parameters, curdir, form, user_info=None):
    """
    A referee may either "accept" or "reject" a refereed document.
    The referee's decision is stored in a file in the submission's working
    directory and it is this function's job to read the contents of that
    file and update the status of the document's entry in the approvals
    table (sbmAPPROVAL) to be either "accepted" or "rejected" depending
    upon the referee's decision.

    @param decision_file: (string) - the name of the file in which the
        referee's decision is to be found.
        NOTE: A referee's decision _MUST_ be either "accept" or "reject".
              If not, an InvenioWebSubmitFunctionError will be raised.
              If a document's "approval status" is not "waiting" at the
              time of the referee's decision, the decision will not be
              taken into account and the submission will be halted.
              (This is because it's not appropriate to approve a document
              that has already been approved or rejected, has been
              withdrawn, etc.)

    @return: empty string.

    @Exceptions raised: InvenioWebSubmitFunctionError on unexpected error.
                        InvenioWebSubmitFunctionStop in the case where the
                        approval should be stopped for whatever reason.
                        (E.g. when it has already been approved.)
    """
    global rn
    doctype = form['doctype']
    ########
    ## Get the parameters from the list:
    ########
    ## Get the name of the "decision" file and read its value:
    ########
    decision = ""  ## variable to hold the referee's decision
    try:
        decision_file = parameters["decision_file"]
    except KeyError:
        ## No value given for the decision file:
        decision_file = None
    else:
        if decision_file is not None:
            decision_file = os.path.basename(decision_file).strip()
            if decision_file == "":
                decision_file = None
    if decision_file is None:
        ## Unable to obtain the name of the file in which the referee's
        ## decision is stored. Halt.
        err_msg = "Error in Register_Referee_Decision: Function was not " \
                  "configured with a valid value for decision_file - the " \
                  "file in which the referee's decision is stored. " \
                  "The referee's decision has not been processed for " \
                  "[%s]. Please inform the administrator." \
                  % rn
        raise InvenioWebSubmitFunctionError(err_msg)
    ## Read in the referee's decision:
    decision = ParamFromFile("%s/%s" % (curdir, decision_file)).lower()
    ##
    ########
    if decision not in ("approve", "reject"):
        ## Invalid value for the referee's decision.
        err_msg = "Error in Register_Referee_Decision: The value for the " \
                  "referee's decision (%s) was invalid. Please inform the " \
                  "administrator." % decision
        raise InvenioWebSubmitFunctionError(err_msg)
    ##
    ## Get the status of the approval request for this document from the DB:
    document_status = get_simple_approval_status(doctype, rn)
    if document_status is None:
        ## No information about this document in the approval database.
        ## Its approval has never been requested.
        msg = """
<br />
<div>
<span style="color: red;">Note:</span> No details about an approval request
 for the document [%s] have been found in the database.<br />
Before a decision can be made about it, a request for its approval must have
 been submitted.<br />
If you feel that there is a problem, please contact &lt;%s&gt;, quoting the
document's report number.
</div>""" % (cgi.escape(rn), cgi.escape(CFG_SITE_SUPPORT_EMAIL))
        raise InvenioWebSubmitFunctionStop(msg)
    elif document_status in ("approved", "rejected"):
        ## If a document was already approved or rejected, halt the approval
        ## process with a message for the referee:
        msg = """
<br />
<div>
<span style="color: red;">Note:</span> The document [%s] has
 already been %s.<br />
There is nothing more to be done in this case and your decision
 has <b>NOT</b> been taken into account.<br />
If you believe this to be an error, please contact &lt;%s&gt;, quoting the<br />
document's report-number [%s] and describing the problem.
</div>""" % (cgi.escape(rn), \
             cgi.escape(document_status), \
             cgi.escape(CFG_SITE_SUPPORT_EMAIL), \
             cgi.escape(rn))
        raise InvenioWebSubmitFunctionStop(msg)
    elif document_status == "withdrawn":
        ## Somebody had withdrawn the approval request for this document
        ## before the referee made this decision. Halt the approval process
        ## with a message for the referee:
        msg = """
<br />
<div>
<span style="color: red;">Note:</span> The request for the approval of the
 document [%s] had been withdrawn prior to the submission of your
 decision.<br />
Before a decision can be made regarding its status, a new request for its
 approval must be submitted by the author.<br />
Your decision has therefore <b>NOT</b> been taken into account.<br />
If you believe this to be an error, please contact &lt;%s&gt;, quoting the
 document's report-number [%s] and describing the problem.
</div>
""" % (cgi.escape(rn), \
       cgi.escape(CFG_SITE_SUPPORT_EMAIL), \
       cgi.escape(rn))
        raise InvenioWebSubmitFunctionStop(msg)
    elif document_status == "waiting":
        ## The document is awaiting approval. Register the referee's decision:
        if decision == "approve":
            ## Register the approval:
            update_approval_request_status(doctype, \
                                           rn, \
                                           note="",
                                           status="approved")
        else:
            ## Register the rejection:
            update_approval_request_status(doctype, \
                                           rn, \
                                           note="",
                                           status="rejected")
        ## Now retrieve the status of the document once more and check that
        ## it is either approved or rejected.  If not, the decision couldn't
        ## be registered and an error should be raised.
        status_after_update = get_simple_approval_status(doctype, rn)
        if status_after_update not in ("approved", "rejected"):
            msg = "Error in Register_Referee_Decision function: It was " \
                  "not possible to update the approvals database when " \
                  "trying to register the referee's descision of [%s] " \
                  "for the document [%s]. Please report this this " \
                  "problem to [%s], quoting the document's " \
                  "report-number [%s]." \
                  % (decision, rn, CFG_SITE_SUPPORT_EMAIL, rn)
            raise InvenioWebSubmitFunctionError(msg)
    else:
        ## The document had an unrecognised "status". Halt with an error.
        msg = "Error in Register_Referee_Decision function: The " \
              "document [%s] has an unknown approval status " \
              "[%s]. Unable to process the referee's decision. Please " \
              "report this problem to [%s], quoting the document's " \
              "report-number [%s] and describing the problem." \
              % (rn, document_status, CFG_SITE_SUPPORT_EMAIL, rn)
        raise InvenioWebSubmitFunctionError(msg)
    ## Finished.
    return ""
def Second_Report_Number_Generation(parameters, curdir, form, user_info=None):
    """
    This function's task is to generate a SECONDARY report number.
    Some document types require more than one report number.  The function
    "Report_Number_Generation" should be used to generate the PRIMARY
    report number (for various reasons, including the fact that that
    function populates the global "rn" with the document's main report
    number). This function then, should be used to generate the secondary
    report number.
    This function doesn't populate any global variable with the secondary
    report number, meaning that it should only be called if a secondary
    report number actually needs to be generated by the system (i.e. not
    if the user is to supply this secondary report number via the submission
    form.
    A use case for this function could be when a document is approved as
    an official note of some sort. Before approval for example, it could
    be classed as a Communication and have its own "Communication number".
    At approval time however, it could be given a new "official note"
    number, and this function could be used to generate the new number.
    So in short, the function behaves rather like Report_Number_Generation
    and was infact based upon that function. It:
          + Creates a "report number" according to some format and using some
            counter files.
          + Writes that new "report number" to a file in the submission's
            working directory.
            (** If the "second report number" file already exists in the
             submission's working directory, it merely exits silently.)

    Parameters:

    @param 2nd_counterpath: (string) - the path to the counter file that
        is used to create the report number.
        The counter path can make use of <PA></PA> in order to specify
        some value that should be included in the path:
          <PA>yy</PA> --> Include the year in the path
          <PA>categ</PA> --> Include the submission's category in the path.
          <PA>file[re]:name_of_file[regular expression to match]</PA> -->
              Include the first line of file (in curdir), matching [re]
          <PA>file*[re]:name_of_file [regular expression to match]</PA> -->
              Include all the lines of a file (in curdir), matching [re]
              separated by - (dash) char.

    @param 2nd_rn_file: (string) - the name of the file that is to be
        created containing the secondary report number. The file will be
        created in the submission's working directory.

    @param 2nd_rn_format: (string) - The format according to which the
        secondary report number will be created.

    @param 2nd_rncateg_file: (string) - the name of the file (in the
        submission's working directory) that contains the category of the
        document.
        The value in this file can be put into the report number by
        including <PA>categ</PA> anywhere that it is needed in the report-
        number format.

    @param 2nd_rn_yeargen: (string) - the instruction used for generating
        the year if one is to be used in the report number.
        The parameter should take either the value "AUTO" - in which case
        the year component of the report number will be a 4-digit
        representation for the current year - or - the name of a file in
        the submission's working directory that contains the year that should
        be included in the report number.
        Note, if the parameter contains the name of a file, it will be assumed
        that if the length of its contents is 10 chars, its value will be a
        date in the format "dd/mm/yyyy" and the last 4 characters will be
        taken as the year.  Otherwise if the length is not 10 characters, it
        will be assumed that the file simply contained a year and its
        contents will be taken as-is. If the file cannot be opened, an
        InvenioWebSubmitFunctionError exception will be raised. If no value
        is provided for this parameter, the year in the format YYYY will be
        used.
        The value that is finally created using this parameter for year will
        be used in the final report number anywhere that the format contains
        <PA>yy</PA>.
       Note:
       Tages that use <PA></PA> can take values as follows:
          <PA>yy</PA> --> Include the year (as determined by 2nd_rn_yeargen).
          <PA>categ</PA> --> Include the submission's category.
          <PA>file[re]:name_of_file[regular expression to match]</PA> -->
              Include the first line of file (in curdir), matching [re]
          <PA>file*[re]:name_of_file [regular expression to match]</PA> -->
              Include all the lines of a file (in curdir), matching [re]
              separated by - (dash) char.

    @param 2nd_nb_length: (string) the number of digits for the
        report number. Eg: '3' for XXX-YYYY-025 or '4' for
        XXX-YYYY-0025. If more are needed (all available digits have
        been used), the length is automatically extended. Choose 1 to
        never have leading zeros. Default length: 3.

    @return: (string) - empty string.

    @Exceptions raised: InvenioWebSubmitFunctionError - upon unexpected
        error.
    """

    ######################
    ## Internal function definition:
    ######################
    def get_pa_tag_content(pa_content):
        """Get content for <PA>XXX</PA>.
        @param pa_content: MatchObject for <PA>(.*)</PA>.
        return: if pa_content=yy => 4 digits year
                if pa_content=categ =>category
                if pa_content=file[re]:a_file => first line of file a_file matching re
                if pa_content=file*p[re]:a_file => all lines of file a_file, matching re,
                                              separated by - (dash) char.
        """
        pa_content = pa_content.groupdict()['content']
        sep = '-'
        out = ''
        if pa_content == 'yy':
            out = yy
        elif pa_content == 'categ':
            out = category
        elif pa_content.startswith('file'):
            filename = ""
            with_regexp = 0
            regexp = ""
            if "[" in pa_content:
                with_regexp = 1
                split_index_start = pa_content.find("[")
                split_index_stop = pa_content.rfind("]")
                regexp = pa_content[split_index_start + 1:split_index_stop]
                filename = pa_content[split_index_stop + 2:]  #]:
            else:
                filename = pa_content.split(":")[1]
            if os.path.exists(os.path.join(curdir, filename)):
                fp = open(os.path.join(curdir, filename), 'r')
                if pa_content[:5] == "file*":
                    out = sep.join(
                        map(lambda x: re.split(regexp, x.strip())[-1],
                            fp.readlines()))
                else:
                    out = re.split(regexp, fp.readline().strip())[-1]
                fp.close()
        return out

    ######################
    ## End of internal function definition:
    ######################
    document_type = form['doctype']
    access_number = form['access']

    ############
    ## Get parameter values and sanitize them:
    ############
    ############
    ## Report number length
    ############
    new_nb_length = 3
    if '2nd_nb_length' in parameters and \
           parameters['2nd_nb_length'].isdigit():
        new_nb_length = int(parameters['2nd_nb_length'])
    ############
    ## Category file name - when category is included in the new report number
    ############
    try:
        new_rn_categ_filename = parameters['2nd_rncateg_file']
    except KeyError:
        new_rn_categ_filename = ""
    else:
        if new_rn_categ_filename is None:
            new_rn_categ_filename = ""
    ## Get the "basename" for the report-number file:
    new_rn_categ_filename = os.path.basename(new_rn_categ_filename).strip()
    if new_rn_categ_filename != ""  and \
           os.path.exists("%s/%s" % (curdir, new_rn_categ_filename)):
        try:
            fh_category = open("%s/%s" % (curdir, new_rn_categ_filename), "r")
            category = fh_category.read()
            fh_category.close()
        except IOError:
            register_exception()
        else:
            ## No newlines in category:
            category = category.replace("\n", "").replace("\r", "")
    else:
        category = ""
    ############
    ## Get the details of whether to automatically generate the year, or
    ## whether to get it from a file (if the report number uses a year.
    ############
    try:
        new_rn_yeargen = parameters['2nd_rn_yeargen']
    except IOError:
        new_rn_yeargen = ""
    else:
        if new_rn_yeargen is None:
            new_rn_yeargen = ""
    if new_rn_yeargen == "AUTO":
        ## If the function is configured to automatically generate the year,
        ## it should take the format "YYYY" (e.g. 2008). It should also be the
        ## current year:
        yy = time.strftime("%Y")
    elif new_rn_yeargen != "":
        ## Apparently, the value to be used for year should be taken from a
        ## file.
        new_rn_yeargen = os.path.basename(new_rn_yeargen).strip()
        if new_rn_yeargen != "" and \
           os.path.exists("%s/%s" % (curdir, new_rn_yeargen)):
            try:
                fh_year = open("%s/%s" % (curdir, new_rn_yeargen), "r")
                yy = fh_year.read()
                fh_year.close()
            except IOError:
                err_msg = "Error in Second_Report_Number_Generation: It " \
                          "wasn't possible to open the file containing " \
                          "the year: [%s]. Please report this problem to " \
                          "[%s]." % (cgi.escape(new_rn_yeargen), \
                                     cgi.escape(CFG_SITE_SUPPORT_EMAIL))
                register_exception(prefix=err_msg)
                raise InvenioWebSubmitFunctionError(err_msg)
            else:
                ## It is assumed that the contents of the date file will be
                ## either the year (in the format YYYY) or the date (in the
                ## format DD/MM/YYYY). If it is 10 chars in length, we take
                ## the last 4, assuming that they are the year component of
                ## the date. If not, we take the whole string, assuming that
                ## it is just the year anyway.
                yy = yy.strip()
                if len(yy) == 10:
                    yy = yy[-4:]
        elif new_rn_yeargen != "":
            ## Although a "valid" filename for the 2nd_rn_yeargen parameter had
            ## been provided, the file didn't exist.
            err_msg = "Error in Second_Report_Number_Generation: It " \
                      "wasn't possible to open the file containing " \
                      "the year: [%s]. Please report this problem to " \
                      "[%s]." % (cgi.escape(new_rn_yeargen), \
                                 cgi.escape(CFG_SITE_SUPPORT_EMAIL))
            raise InvenioWebSubmitFunctionError(err_msg)
        else:
            ## The filename provided for the 2nd_rn_yeargen parameter was
            ## invalid.
            err_msg = "Error in Second_Report_Number_Generation: The " \
                      "function has been configured with an invalid " \
                      "filename for the year (2nd_rn_yeargen). Please " \
                      "report this problem to [%s], quoting the document " \
                      "type [%s]." \
                      % (cgi.escape(CFG_SITE_SUPPORT_EMAIL), \
                         cgi.escape(document_type))
            raise InvenioWebSubmitFunctionError(err_msg)
    else:
        ## No value for the year-generation parameter. Just use the current
        ## year.
        yy = time.strftime("%Y")
    ############
    ## Counter Path:
    ############
    try:
        new_rn_counter_path = parameters['2nd_counterpath']
    except KeyError:
        new_rn_counter_path = ""
    else:
        if new_rn_counter_path is None:
            new_rn_counter_path = ""
    counter_path = re.sub('<PA>(?P<content>[^<]*)</PA>', get_pa_tag_content,
                          new_rn_counter_path)
    counter_path = counter_path.replace(" ", "").replace("\n", "")
    ## Counter path isn't allowed to contain "../" (no moving below the
    ## counters directory) and must not be empty. If either of these cases
    ## is true, it is considered to be an error:
    if counter_path == "" or counter_path.find("../") != -1:
        ## Invalid counter path.
        err_msg = "Error in Second_Report_Number_Generation: The function " \
                  "has been configured with an invalid value for " \
                  "2nd_counterpath. Please report this problem to " \
                  "[%s]." % cgi.escape(CFG_SITE_SUPPORT_EMAIL)
        raise InvenioWebSubmitFunctionError(err_msg)
    ############
    ## New Report Number's File Name:
    ############
    try:
        new_rn_filename = parameters['2nd_rn_file']
    except KeyError:
        new_rn_filename = ""
    else:
        if new_rn_filename is None:
            new_rn_filename = ""
    ## Get the "basename" for the report-number file:
    new_rn_filename = os.path.basename(new_rn_filename).strip()
    if new_rn_filename == "":
        ## No file name provided for the new report-number. This is
        ## considered to be an error.
        err_msg = "Error in Second_Report_Number_Generation: The function " \
                  "has been configured with an invalid value for " \
                  "2nd_rn_file. Please report this problem to " \
                  "[%s]." % cgi.escape(CFG_SITE_SUPPORT_EMAIL)
        raise InvenioWebSubmitFunctionError(err_msg)
    ############
    ## Report Number Format:
    ############
    try:
        new_rn_format = parameters['2nd_rn_format']
    except KeyError:
        new_rn_format = ""
    else:
        if new_rn_format is None:
            new_rn_format = ""
    new_rn_format = re.sub('<PA>(?P<content>[^<]*)</PA>', get_pa_tag_content,
                           new_rn_format)
    ############
    ## End of treatment of parameters.
    ############
    ############
    ## Test to see whether the second report number file already exists:
    if not os.path.exists("%s/%s" % (curdir, new_rn_filename)):
        ## The new report number file doesn't exist. Create it.
        new_rn = create_reference(counter_path, new_rn_format, new_nb_length)
        new_rn = re.compile('\s').sub('', new_rn)
        ## Write it to file:
        # The file edsrn is created in the submission directory, and it stores the report number
        try:
            fh_new_rn_file = open("%s/%s" % (curdir, new_rn_filename), "w")
            fh_new_rn_file.write(new_rn)
            fh_new_rn_file.flush()
            fh_new_rn_file.close()
        except IOError:
            ## Unable to create the new report-number's file.
            err_msg = "Error in Second_Report_Number_Generation: It " \
                      "wasn't possible to write out the newly generated " \
                      "'second' report number (%s) to the file [%s]. " \
                      "Please report this problem to [%s], quoting the " \
                      "document type [%s], the submission access number " \
                      "[%s] and the new report number [%s]."
            register_exception(prefix=err_msg % (new_rn, \
                                                 new_rn_filename, \
                                                 CFG_SITE_SUPPORT_EMAIL, \
                                                 document_type, \
                                                 access_number, \
                                                 new_rn))
            raise InvenioWebSubmitFunctionError(err_msg % \
                                  (cgi.escape(new_rn), \
                                   cgi.escape(new_rn_filename), \
                                   cgi.escape(CFG_SITE_SUPPORT_EMAIL), \
                                   cgi.escape(document_type), \
                                   cgi.escape(access_number), \
                                   cgi.escape(new_rn)))
    return ""
def Allocate_ALEPH_SYS(parameters, curdir, form, user_info=None):
    """
       Get the next available ALEPH SYS from the counter file, and allocate it as the
       SYS for this record. Increment the counterby one.
       ALEPH SYS allocation works in "slots" of free numbers. For example,
       000425201 -> 000634452 for a given database may be available.
       This means that it is necessary to care about not over-stepping the maximum
       boundary. To this end, two counters (for each ALEPH Database) must be present:
          - last_SYS_<DATABASE>      (this contains the last SYS allocated for
            a database)
          - maximum_SYS_<DATABASE>   (this contains the MAXIMUM SYS allowed for a
            database)
       So, for example, for the CER database, there would be:
          - last_SYS_CER
          - maximum_SYS_CER
       When the maximum SYS has been reached, all further attempts to obtain ALEPH SYSs
       will fail, as this function will fail with an error.  To prevent this from coming
       as a surprise, however, when "last_SYS_<DATABASE>" gets somewhere near to the value
       stored in "maximum_SYS_<DATABASE>", a mail will be sent to the Admin with every
       SYS allocated, warning them that only N numbers remain free for the XY database.
       The number until MAX SYS which determines this period of warning emails is
       determined by a variable "warn_admin_at_N_sys_remaining".  It is set to 2000 by
       default, but can be changed.
       When the system allocates a new sys and there are 2000 or less free SYS remaining,
       the warning mails to ADMIN will be sent.

       @param alephdatabase: (string) the name of the ALEPH database for which a SYS is to be
        allocated.  E.g. "CER".  The he absence of this will cause the function to fail.
        Also, the absence of either of the 2 counter files "last_SYS_${database}" and
        "maximum_SYS_${database}" will cause the function to fail.
    """
    mailfrom_addr = '%s Submission Engine <%s>' % (CFG_SITE_NAME, CFG_SITE_SUPPORT_EMAIL)
    database = parameters['alephdatabase'].strip()
    counter_lastsys = "last_SYS_%s" % database
    counter_maxsys = "maximum_SYS_%s" % database

    ## ensure that "database" param is not empty, and exists in the list of legal DBs
    if database == "" or database not in CFG_LEGAL_ALEPH_DATABASES:
        ## error with supplied database
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, an invalid database name was"""\
              """ supplied: [%s]. It was therefore not possible to allocate the SYS.""" % database
        raise InvenioWebSubmitFunctionError(msg)

    ## before trying to make a lockfile, test if one exists and whether it is older than "CFG_MAX_AGE_LOCKFILE" seconds
    ## if so, raise an error and warn the admin:
    counter_lockfile = "last_SYS_%s.lock" % database
    try:
        lockfile_modtime = getmtime("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lockfile))
        time_now = mktime(localtime())
        time_since_last_lockfile_mod = time_now - lockfile_modtime
        if time_since_last_lockfile_mod > CFG_MAX_AGE_LOCKFILE:
            ## lockfile is old - warn admin and stop
            admin_msg = """ERROR: When trying to allocate an ALEPH SYS for a record in the [%s] DB, it was not possible """\
                        """to create a lockfile. An attempt was made at [%s], but a lockfile already existed with a """\
                        """last modification time of [%s]. It was therefore not possible to allocate the SYS.""" \
                        % (database, strftime("%d/%m/%Y %H:%M:%S", localtime(time_now)),
                           strftime("%d/%m/%Y %H:%M:%S", localtime(lockfile_modtime)))
            send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - OLD ALEPH SYS LOCKFILE ENCOUNTERED!", content=admin_msg)
            user_msg = """ERROR: When trying to allocate an ALEPH SYS for a record in the [%s] DB, it was not possible""" \
                       """ to create a lockfile. It was therefore not possible to allocate the SYS.""" \
                       % database
            raise InvenioWebSubmitFunctionError(user_msg)
    except OSError:
        ## no lockfile
        pass

    ## before any counter operations, create a lockfile:
    got_lock = _create_SYS_counter_lockfile(database)

    if got_lock == 0:
        ## unable to create lockfile!
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record in the [%s] DB, it was not possible"""\
              """ to create a lockfile within 60 seconds. It was therefore not possible to allocate the SYS.""" % database
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - CANNOT CREATE LOCKFILE!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)

    ## test that counter files exist for "database":
    rw_count_lastsys_ok = access("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys), R_OK|W_OK)
    rw_count_maxsys_ok = access("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_maxsys), R_OK|W_OK)

    if not rw_count_lastsys_ok or not rw_count_maxsys_ok:
        ## cannot access the ALEPH counter files - critical error
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, either [%s] or [%s] (or both) was not"""\
              """ accessible. It was therefore not possible to allocate the SYS.""" % (counter_lastsys, counter_maxsys)
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - CANNOT ACCESS ALEPH SYS COUNTER(S)!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)


    ## read last-sys and max-sys:
    try:
        fp = open("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys), "r")
        fileval_lastsys = fp.read()
        fp.close()
        fp = open("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_maxsys), "r")
        fileval_maxsys = fp.read()
        fp.close()
    except IOError:
        ## could not read one or both of the files
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, either [%s] or [%s] (or both) could not"""\
              """ be read. It was therefore not possible to allocate the SYS.""" % (counter_lastsys, counter_maxsys)
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - CANNOT ACCESS ALEPH SYS COUNTER(S)!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)


    ## for the values from both files, clean any whitespace from beginning or end of file text and cast the result to an integer:
    try:
        lastsys = int(fileval_lastsys.strip())
        maxsys = int(fileval_maxsys.strip())
    except ValueError:
        ## the value in one or both of the files did not cast to an int!
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, either [%s] or [%s] (or both) contained invalid"""\
              """ (non-integer) values. It was therefore not possible to allocate the SYS.""" % (counter_lastsys, counter_maxsys)
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - ALEPH SYS COUNTER(S) CONTAINS INVALID DATA!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)


    ## check that "fileval_lastsys" is less than "fileval_maxsys". If yes, proceed - else fail and mail ADMIN
    if not (lastsys < maxsys):
        ## MAX SYS EXCEEDED
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, the value of [%s -> %d] is not less than the """\
              """value of [%s -> %d]. It was therefore not possible to allocate the SYS. A new SYS range must be allocated!"""\
              % (counter_lastsys, lastsys, counter_maxsys, maxsys)
        ## mail admin:
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - MAXIMUM ALEPH SYS COUNTER VALUE EXCEEDED!", content=msg)
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        raise InvenioWebSubmitFunctionError(msg)


    if maxsys - lastsys < CFG_WARNING_MAX_SYS_APPROACHING:
        ## WARN admin that MAX ALEPH SYS for this DB is approaching:
        _warn_admin_counterlimit_approaching(db=database, lastsys=lastsys, maxsys=maxsys)


    ## increment the value of the last SYS
    lastsys += 1

    ## cast sys to a string and pad the value on the left with leading zeros to 9 characters:
    cursys = "%09d%s" % (lastsys, database[0:3].upper().strip())

    ## now write out the new value of lastsys to the relevant counter file:
    ## make temporary file then move it later
    tmpfname = "%s_%s_%s" % (counter_lastsys, strftime("%Y%m%d%H%M%S", localtime()), getpid())

    ## open temp counter file for writing:
    try:
        fp = open("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, tmpfname), "w")
        fp.write("%d" % (lastsys,))
        fp.flush()
        fp.close()
    except IOError:
        ## could not write to temp file
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, could not write out new value for last SYS used """\
              """to a temporary file [%s]. It was therefore not possible to allocate a SYS for the record ([%s] was not """\
              """incremented.)""" % ("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, tmpfname), counter_lastsys)
        ## remove the "lock file"
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - CANNOT CREATE TEMPORARY ALEPH SYS COUNTER FILE!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)

    ## copy old counter file to backup version:
    try:
        copyfile("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys), "%s/%s.bk" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys))
    except IOError:
        ## unable to make backup of counter file:
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, could not write out new value for last SYS used."""\
              """ Couldn't make a back-up copy of the SYS counter file [%s].""" % ("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys),)
        ## remove the "lock file"
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - CANNOT WRITE BACK-UP ALEPH SYS COUNTER!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)

    ## rename temp counter file to final counter file:
    try:
        rename("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, tmpfname), "%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys))
    except OSError:
        ## couldnt rename the tmp file to final file name
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, could not write out new value for last SYS used."""\
              """ Created the temporary last SYS counter file [%s], but couldn't then rename it to the final counter file [%s]."""\
              """ It was therefore not possible to allocate a SYS for the record ([%s] was not incremented.)"""\
              % ("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, tmpfname), "%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, counter_lastsys), counter_lastsys)
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        send_email(fromaddr=mailfrom_addr, toaddr=CFG_SITE_ADMIN_EMAIL, subject="WebSubmit ERROR - CANNOT WRITE ALEPH SYS COUNTER FILE!", content=msg)
        raise InvenioWebSubmitFunctionError(msg)


    ## now that counter has been successfully incremented, write cursys out to the file "SNa500":
    try:
        fp = open("%s/SNa500" % curdir, "w")
        fp.write("%s" % cursys)
        fp.flush()
        fp.close()
    except IOError:
        ## unable to write out the SYS!
        msg = """ERROR: When trying to allocate an ALEPH SYS for a record, could not write out new SYS to file [%s/SNa500]."""\
              """ It was therefore not possible to allocate the SYS ([%s] was not incremented.)"""\
              % (curdir, counter_lastsys)
        lockfile_removed = _unlink_SYS_counter_lockfile(database)
        if lockfile_removed == 0:
            ## couldn't remove lockfile - mail ADMIN
            _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        raise InvenioWebSubmitFunctionError(msg)

    ## finally, unlink the lock file:
    lockfile_removed = _unlink_SYS_counter_lockfile(database)
    if lockfile_removed == 0:
        ## couldn't remove lockfile - mail ADMIN
        msg = """ERROR: After allocating an ALEPH SYS for a record, it was not possible to remove the lock file [last_SYS_%s.lock] after the """\
              """SYS was allocated.""" % ("%s/%s" % (CFG_WEBSUBMIT_COUNTERSDIR, database),)
        _mail_admin_because_lockfile_not_removeable(lockfilename="last_SYS_%s" % database, extramsg="\n\n"+msg)
        raise InvenioWebSubmitFunctionError(msg)

    return ""
Exemple #12
0
def visit_for_stamping(visit_for_stamping_arguments, dirname, filenames):
    """Visitor function called by os.path.walk.
       This function takes a directory and a list of files in that directory
       and for each file, calls the websubmit_file_stamper on it.
       When a file is stamped, the original is moved away into a directory
       of unstamped files and the new, stamped version is moved into its
       place.
       @param visit_for_stamping_arguments: (dictionary) of arguments needed
        by this function. Must contain 'curdir', 'user_info' and
        'file_stamper_options' members.
       @param dirname: (string) - the path to the directory in which the
        files are to be stamped.
       @param filenames: (list) - the names of each file in dirname. An
        attempt will be made to stamp each of these files.
       @Exceptions Raised:
         + InvenioWebSubmitFunctionWarning;
         + InvenioWebSubmitFunctionError;
    """
    ## Get the dictionary of options to pass to the stamper:
    file_stamper_options = visit_for_stamping_arguments['file_stamper_options']

    ## Create a directory to store original files before stamping:
    dirname_files_pre_stamping = dirname.replace("/files/", \
                                                 "/files_before_stamping/", 1)
    if not os.path.exists(dirname_files_pre_stamping):
        try:
            os.makedirs(dirname_files_pre_stamping)
        except OSError as err:
            ## Unable to make a directory in which to store the unstamped
            ## files.
            ## Register the exception:
            exception_prefix = "Unable to stamp files in [%s]. Couldn't " \
                               "create directory in which to store the " \
                               "original, unstamped files." \
                               % dirname
            register_exception(prefix=exception_prefix)
            ## Since we can't make a directory for the unstamped files,
            ## we can't continue to stamp them.
            ## Skip the stamping of the contents of this directory by raising
            ## a warning:
            msg = "Warning: A problem occurred when stamping files in [%s]. " \
                  "Unable to create directory to store the original, " \
                  "unstamped files. Got this error: [%s]. This means the " \
                  "files in this directory were not stamped." \
                  % (dirname, str(err))
            raise InvenioWebSubmitFunctionWarning(msg)

    ## Loop through each file in the directory and attempt to stamp it:
    for file_to_stamp in filenames:
        ## Get the path to the file to be stamped and put it into the
        ## dictionary of options that will be passed to stamp_file:
        path_to_subject_file = "%s/%s" % (dirname, file_to_stamp)
        file_stamper_options['input-file'] = path_to_subject_file

        ## Just before attempting to stamp the file, log the dictionary of
        ## options (file_stamper_options) that will be passed to websubmit-
        ## file-stamper:
        try:
            fh_log = open("%s/websubmit_file_stamper-calls-options.log" \
                          % visit_for_stamping_arguments['curdir'], "a+")
            fh_log.write("%s\n" % file_stamper_options)
            fh_log.flush()
            fh_log.close()
        except IOError:
            ## Unable to log the file stamper options.
            exception_prefix = "Unable to write websubmit_file_stamper " \
                               "options to log file " \
                               "%s/websubmit_file_stamper-calls-options.log" \
                               % visit_for_stamping_arguments['curdir']
            register_exception(prefix=exception_prefix)

        try:
            ## Try to stamp the file:
            (stamped_file_path_only, stamped_file_name) = \
                    websubmit_file_stamper.stamp_file(file_stamper_options)
        except InvenioWebSubmitFileStamperError:
            ## It wasn't possible to stamp this file.
            ## Register the exception along with an informational message:
            exception_prefix = "A problem occurred when stamping [%s]. The " \
                               "stamping of this file was unsuccessful." \
                               % path_to_subject_file
            register_exception(prefix=exception_prefix)
            ## Skip this file, moving on to the next:
            continue
        else:
            ## Stamping was successful.
            path_to_stamped_file = "%s/%s" % (stamped_file_path_only, \
                                              stamped_file_name)
            ## Move the unstamped file from the "files" directory into the
            ## "files_before_stamping" directory:
            try:
                shutil.move(path_to_subject_file, "%s/%s" \
                            % (dirname_files_pre_stamping, file_to_stamp))
            except IOError:
                ## Couldn't move the original file away from the "files"
                ## directory. Log the problem and continue on to the next
                ## file:
                exception_prefix = "A problem occurred when stamping [%s]. " \
                                   "The file was sucessfully stamped, and " \
                                   "can be found here: [%s]. Unfortunately " \
                                   "though, it could not be copied back to " \
                                   "the current submission's working " \
                                   "directory because the unstamped version " \
                                   "could not be moved out of the way (tried " \
                                   "to move it from here [%s] to here: " \
                                   "[%s/%s]). The stamping of this file was " \
                                   "unsuccessful." \
                                   % (path_to_subject_file, \
                                      path_to_stamped_file, \
                                      path_to_subject_file, \
                                      dirname_files_pre_stamping, \
                                      file_to_stamp)
                register_exception(prefix=exception_prefix)
                continue
            else:
                ## The original file has been moved into the files before
                ## stamping directory. Now try to copy the stamped file into
                ## the files directory:
                try:
                    shutil.copy(path_to_stamped_file, "%s/%s" \
                                % (dirname, file_to_stamp))
                except IOError:
                    ## Even though the original, unstamped file was moved away
                    ## from the files directory, the stamped-version couldn't
                    ## be moved into its place. Register the exception:
                    exception_prefix = "A problem occurred when stamping " \
                                       "[%s]. The file was sucessfully " \
                                       "stamped, and can be found here: " \
                                       "[%s]. Unfortunately though, it " \
                                       "could not be copied back to the " \
                                       "current submission's working " \
                                       "directory." % (path_to_subject_file, \
                                                       path_to_stamped_file)
                    register_exception(prefix=exception_prefix)

                    ## Because it wasn't possible to move the stamped file
                    ## into the files directory, attempt to move the original,
                    ## unstamped file back into the files directory:
                    try:
                        shutil.move("%s/%s" % (dirname_files_pre_stamping, \
                                               file_to_stamp), \
                                    path_to_stamped_file)
                    except IOError as err:
                        ## It wasn't possible even to move the original file
                        ## back to the files directory. Register the
                        ## exception and stop the stamping process - it isn't
                        ## safe to continue:
                        exeption_prefix = "A problem occurred when stamping " \
                                          "[%s]. The file was sucessfully " \
                                           "stamped, and can be found here: " \
                                           "[%s]. Unfortunately though, it " \
                                           "could not be copied back to the " \
                                           "current submission's working " \
                                           "directory. Additionionally, the " \
                                           "original, unstamped file " \
                                           "could not be moved back to the " \
                                           "files directory, from the files-" \
                                           "before-stamping directory. It " \
                                           "can now be found here: [%s/%s]. " \
                                           "Stamping cannot continue and " \
                                           "manual intervention is necessary " \
                                           "because the file cannot be " \
                                           "attached to the record." \
                                           % (path_to_subject_file, \
                                              path_to_stamped_file, \
                                              dirname_files_pre_stamping, \
                                              file_to_stamp)
                        register_exception(prefix=exeption_prefix)
                        ## Raise an InvenioWebSubmitFunctionError, stopping
                        ## further stamping, etc:
                        raise InvenioWebSubmitFunctionError(exception_prefix)
Exemple #13
0
def Stamp_Uploaded_Files(parameters, curdir, form, user_info=None):
    """
    Stamp certain files that have been uploaded during a submission.

    @param parameters: (dictionary) - must contain:

         + latex_template: (string) - the name of the LaTeX template that
            should be used for the creation of the stamp.

         + latex_template_vars: (string) - a string-ified dictionary
            of variables to be replaced in the LaTeX template and the
            values (or names of files in curdir containing the values)
            with which to replace them. Use prefix 'FILE:' to specify
            that the stamped value must be read from a file in
            submission directory instead of being a fixed value to
            stamp.
            E.G.:
               { 'TITLE' : 'FILE:DEMOTHESIS_TITLE',
                 'DATE'  : 'FILE:DEMOTHESIS_DATE'
               }

         + files_to_be_stamped: (string) - The directories in which files
            should be stamped: This is a comma-separated list of directory
            names. E.g.:
               DEMOTHESIS_MAIN,DEMOTHESIS_ADDITIONAL

            (If you use Create_Upload_Files_Interface function, you
            should know that uploaded files goes under a subdirectory
            'updated/' of the /files/ folder in submission directory:
            in this case you have to specify this component in the
            parameter. For eg:
            updated/DEMOTHESIS_MAIN,updated/DEMOTHESIS_ADDITIONAL)

         + stamp: (string) - the type of stamp to be applied to the files.
            should be one of:
              + first (only the first page is stamped);
              + all (all pages are stamped);
              + coverpage (a separate cover-page is added to the file as a
                 first page);

         + layer: (string) - the position of the stamp. Should be one of:
              + background (invisible if original file has a white -
                            not transparent- background layer)
              + foreground (on top of the stamped file. If the stamp
                            does not have a transparent background,
                            will hide all of the document layers)

         + switch_file: (string) - when this value is set, specifies
            the name of a file that will swith on/off the
            stamping. The 'switch_file' must contain the names defined
            in 'files_to_be_stamped' (comma-separated values). Stamp
            will be applied only to files referenced in the
            switch_file. No stamp is applied if the switch_file is
            missing from the submission directory.
            However if the no switch_file is defined in this variable
            (parameter is left empty), stamps are applied according
            the variable 'files_to_be_stamped'.
            Useful for eg. if you want to let your users control the
            stamping with a checkbox on your submission page.

    If all goes according to plan, for each directory in which files are to
    be stamped, the original, unstamped files should be found in a
    directory 'files_before_stamping/DIRNAME', and the stamped versions
    should be found under 'files/DIRNAME'. E.g., for DEMOTHESIS_Main:
         - Unstamped: files_before_stamping/DEMOTHESIS_Main
         - Stamped:   files/DEMOTHESIS_Main
    """
    ## The file stamper needs to be called with a dictionary of options of
    ## the following format:
    ##  { 'latex-template'      : "", ## TEMPLATE_NAME
    ##    'latex-template-var'  : {}, ## TEMPLATE VARIABLES
    ##    'input-file'          : "", ## INPUT FILE
    ##    'output-file'         : "", ## OUTPUT FILE
    ##    'stamp'               : "", ## STAMP TYPE
    ##    'layer'               : "", ## LAYER TO STAMP
    ##    'verbosity'           : 0,  ## VERBOSITY (we don't care about it)
    ##  }
    file_stamper_options = {
        'latex-template': "",
        'latex-template-var': {},
        'input-file': "",
        'output-file': "",
        'stamp': "",
        'layer': "",
        'verbosity': 0,
    }
    ## A dictionary of arguments to be passed to visit_for_stamping:
    visit_for_stamping_arguments = { 'curdir' : curdir,
                                     'file_stamper_options' : \
                                                file_stamper_options,
                                     'user_info' : user_info
                                   }

    ## Start by getting the parameter-values from WebSubmit:
    ## The name of the LaTeX template to be used for stamp creation:
    latex_template = "%s" % ((type(parameters['latex_template']) is str \
                              and parameters['latex_template']) or "")
    ## A string containing the variables/values that should be substituted
    ## in the final (working) LaTeX template:
    latex_template_vars_string = "%s" % \
                       ((type(parameters['latex_template_vars']) is str \
                         and parameters['latex_template_vars']) or "")
    ## The type of stamp to be applied to the file(s):
    stamp = "%s" % ((type(parameters['stamp']) is str and \
                     parameters['stamp'].lower()) or "")
    ## The layer to use for stamping:
    try:
        layer = parameters['layer']
    except KeyError:
        layer = "background"
    if not layer in ('background', 'foreground'):
        layer = "background"
    ## The directories in which files should be stamped:
    ## This is a comma-separated list of directory names. E.g.:
    ## DEMOTHESIS_MAIN,DEMOTHESIS_ADDITIONAL
    stamp_content_of = "%s" % ((type(parameters['files_to_be_stamped']) \
                                is str and parameters['files_to_be_stamped']) \
                               or "")
    ## Now split the list (of directories in which to stamp files) on commas:
    if stamp_content_of.strip() != "":
        stamping_locations = stamp_content_of.split(",")
    else:
        stamping_locations = []

    ## Check if stamping is enabled
    switch_file = parameters.get('switch_file', '')
    if switch_file:
        # Good, a "switch file" was specified. Check if it exists, and
        # it its value is not empty.
        switch_file_content = ''
        try:
            fd = file(os.path.join(curdir, switch_file))
            switch_file_content = fd.read().split(',')
            fd.close()
        except:
            switch_file_content = ''
        if not switch_file_content:
            # File does not exist, or is emtpy. Silently abort
            # stamping.
            return ""
        else:
            stamping_locations = [location for location in stamping_locations \
                                  if location in switch_file_content]

    if len(stamping_locations) == 0:
        ## If there are no items to be stamped, don't continue:
        return ""

    ## Strip the LaTeX filename into the basename (All templates should be
    ## in the template repository):
    latex_template = os.path.basename(latex_template)

    ## Convert the string of latex template variables into a dictionary
    ## of search-term/replacement-term pairs:
    latex_template_vars = get_dictionary_from_string(
        latex_template_vars_string)
    ## For each of the latex variables, check in `CURDIR' for a file with that
    ## name. If found, use it's contents as the template-variable's value.
    ## If not, just use the raw value string already held by the template
    ## variable:
    latex_template_varnames = latex_template_vars.keys()
    for varname in latex_template_varnames:
        ## Get this variable's value:
        varvalue = latex_template_vars[varname].strip()
        if not ((varvalue.find("date(") == 0 and varvalue[-1] == ")") or \
                (varvalue.find("include(") == 0 and varvalue[-1] == ")")) \
                and varvalue != "":
            ## We don't want to interfere with date() or include() directives,
            ## so we only do this if the variable value didn't contain them:
            ##
            ## Is this variable value the name of a file in the current
            ## submission's working directory, from which a literal value for
            ## use in the template should be extracted? If yes, it will
            ## begin with "FILE:". If no, we leave the value exactly as it is.
            if varvalue.upper().find("FILE:") == 0:
                ## The value to be used is to be taken from a file. Clean the
                ## file name and if it's OK, extract that value from the file.
                ##
                seekvalue_fname = varvalue[5:].strip()
                seekvalue_fname = os.path.basename(seekvalue_fname).strip()
                if seekvalue_fname != "":
                    ## Attempt to extract the value from the file:
                    if os.access("%s/%s" % (curdir, seekvalue_fname), \
                                 os.R_OK|os.F_OK):
                        ## The file exists. Extract its value:
                        try:
                            repl_file_val = \
                              open("%s/%s" \
                                   % (curdir, seekvalue_fname), "r").readlines()
                        except IOError:
                            ## The file was unreadable.
                            err_msg = "Error in Stamp_Uploaded_Files: The " \
                                      "function attempted to read a LaTex " \
                                      "template variable value from the " \
                                      "following file in the current " \
                                      "submission's working directory: " \
                                      "[%s]. However, an unexpected error " \
                                      "was encountered when doing so. " \
                                      "Please inform the administrator." \
                                      % seekvalue_fname
                            register_exception(req=user_info['req'])
                            raise InvenioWebSubmitFunctionError(err_msg)
                        else:
                            final_varval = ""
                            for line in repl_file_val:
                                final_varval += line
                            final_varval = final_varval.rstrip()
                            ## Replace the variable value with that which has
                            ## been read from the file:
                            latex_template_vars[varname] = final_varval
                    else:
                        ## The file didn't actually exist in the current
                        ## submission's working directory. Use an empty
                        ## value:
                        latex_template_vars[varname] = ""
                else:
                    ## The filename was not valid.
                    err_msg = "Error in Stamp_Uploaded_Files: The function " \
                              "was configured to read a LaTeX template " \
                              "variable from a file with the following " \
                              "instruction: [%s --> %s]. The filename, " \
                              "however, was not considered valid. Please " \
                              "report this to the administrator." \
                              % (varname, varvalue)
                    raise InvenioWebSubmitFunctionError(err_msg)

    ## Put the 'fixed' values into the file_stamper_options dictionary:
    file_stamper_options['latex-template'] = latex_template
    file_stamper_options['latex-template-var'] = latex_template_vars
    file_stamper_options['stamp'] = stamp
    file_stamper_options['layer'] = layer

    for stampdir in stamping_locations:
        ## Create the full path to the stamp directory - it is considered
        ## to be under 'curdir' - the working directory for the current
        ## submission:
        path_to_stampdir = "%s/files/%s" % (curdir, stampdir.strip())
        ## Call os.path.walk, passing it the path to the directory to be
        ## walked, the visit_for_stamping function (which will call the
        ## file-stamper for each file within that directory), and the
        ## dictionary of options to be passed to the file-stamper:
        try:
            os.path.walk(path_to_stampdir, \
                         visit_for_stamping, \
                         visit_for_stamping_arguments)
        except InvenioWebSubmitFunctionWarning:
            ## Unable to stamp the files in stampdir. Register the exception
            ## and continue to try to stamp the files in the other stampdirs:
            ## FIXME - The original exception was registered in 'visit'.
            ## Perhaps we should just send the message contained in this
            ## warning to the admin?
            register_exception(req=user_info['req'])
            continue
        except InvenioWebSubmitFunctionError as err:
            ## Unexpected error in stamping. The admin should be contacted
            ## because it has resulted in an unstable situation with the
            ## files. They are no longer in a well-defined state - some may
            ## have been lost and manual intervention by the admin is needed.
            ## FIXME - should this be reported here, or since we propagate it
            ## up to websubmit_engine anyway, should we let it register it?
            register_exception(req=user_info['req'])
            raise err
    return ""
Exemple #14
0
def Stamp_Replace_Single_File_Approval(parameters, \
                                       curdir, \
                                       form, \
                                       user_info=None):
    """
    This function is intended to be called when a document has been
    approved and needs to be stamped.
    The function should be used when there is ONLY ONE file to be
    stamped after approval (for example, the "main file").
    The name of the file to be stamped should be known and should be stored
    in a file in the submission's working directory (without the extension).
    Generally, this will work our fine as the main file is named after the
    report number of the document, this will be stored in the report number
    file.

    @param parameters: (dictionary) - must contain:

         + latex_template: (string) - the name of the LaTeX template that
            should be used for the creation of the stamp.

         + latex_template_vars: (string) - a string-ified dictionary
            of variables to be replaced in the LaTeX template and the
            values (or names of files in curdir containing the values)
            with which to replace them. Use prefix 'FILE:' to specify
            that the stamped value must be read from a file in
            submission directory instead of being a fixed value to
            stamp.
            E.G.:
               { 'TITLE' : 'FILE:DEMOTHESIS_TITLE',
                 'DATE'  : 'FILE:DEMOTHESIS_DATE'
               }

         + file_to_be_stamped: (string) - this is the name of a file in the
            submission's working directory that contains the name of the
            bibdocfile that is to be stamped.

         + new_file_name: (string) - this is the name of a file in the
            submission's working directory that contains the name that is to
            be given to the file after it has been stamped. If empty, or if
            that file doesn't exist, the file will not be renamed after
            stamping.

         + switch_file: (string) - when this value is set, specifies
            the name of a file that will swith on/off the
            stamping. The stamp will be applied if the file exists in
            the submission directory and is not empty. If the file
            cannot be found or is empty, the stamp is not applied.
            Useful for eg. if you want to let your users control the
            stamping with a checkbox on your submission page.
            Leave this parameter empty to always stamp by default.

         + stamp: (string) - the type of stamp to be applied to the file.
            should be one of:
              + first (only the first page is stamped);
              + all (all pages are stamped);
              + coverpage (a separate cover-page is added to the file as a
                 first page);

         + layer: (string) - the position of the stamp. Should be one of:
              + background (invisible if original file has a white
                -not transparent- background layer)
              + foreground (on top of the stamped file.  If the stamp
                does not have a transparent background, will hide all
                of the document layers)
           The default value is 'background'.
    """
    ############
    ## Definition of important variables:
    ############
    ## The file stamper needs to be called with a dictionary of options of
    ## the following format:
    ##  { 'latex-template'      : "", ## TEMPLATE_NAME
    ##    'latex-template-var'  : {}, ## TEMPLATE VARIABLES
    ##    'input-file'          : "", ## INPUT FILE
    ##    'output-file'         : "", ## OUTPUT FILE
    ##    'stamp'               : "", ## STAMP TYPE
    ##    'layer'               : "", ## LAYER TO STAMP
    ##    'verbosity'           : 0,  ## VERBOSITY (we don't care about it)
    ##  }
    file_stamper_options = {
        'latex-template': "",
        'latex-template-var': {},
        'input-file': "",
        'output-file': "",
        'stamp': "",
        'layer': "",
        'verbosity': 0,
    }

    ## Check if stamping is enabled
    switch_file = parameters.get('switch_file', '')
    if switch_file:
        # Good, a "switch file" was specified. Check if it exists, and
        # it its value is not empty.
        if not _read_in_file(os.path.join(curdir, switch_file)):
            # File does not exist, or is emtpy. Silently abort
            # stamping.
            return ""

    ## Submission access number:
    access = _read_in_file("%s/access" % curdir)
    ## record ID for the current submission. It is found in the special file
    ## "SN" (sysno) in curdir:
    recid = _read_in_file("%s/SN" % curdir)
    try:
        recid = int(recid)
    except ValueError:
        ## No record ID. Cannot continue.
        err_msg = "Error in Stamp_Replace_Single_File_Approval: " \
                  "Cannot recover record ID from the submission's working " \
                  "directory. Stamping cannot be carried out. The " \
                  "submission ID is [%s]." % cgi.escape(access)
        register_exception(prefix=err_msg)
        raise InvenioWebSubmitFunctionError(err_msg)
    ############
    ## Resolution of function parameters:
    ############
    ## The name of the LaTeX template to be used for stamp creation:
    latex_template = "%s" % ((type(parameters['latex_template']) is str \
                              and parameters['latex_template']) or "")
    ## A string containing the variables/values that should be substituted
    ## in the final (working) LaTeX template:
    latex_template_vars_string = "%s" % \
                       ((type(parameters['latex_template_vars']) is str \
                         and parameters['latex_template_vars']) or "")
    ## The type of stamp to be applied to the file(s):
    stamp = "%s" % ((type(parameters['stamp']) is str and \
                     parameters['stamp'].lower()) or "")
    ## The layer to use for stamping:
    try:
        layer = parameters['layer']
    except KeyError:
        layer = "background"
    if not layer in ('background', 'foreground'):
        layer = "background"
    ## Get the name of the file to be stamped from the file indicated in
    ## the file_to_be_stamped parameter:
    try:
        file_to_stamp_file = parameters['file_to_be_stamped']
    except KeyError:
        file_to_stamp_file = ""
    else:
        if file_to_stamp_file is None:
            file_to_stamp_file = ""
    ## Get the "basename" for the file to be stamped (it's mandatory that it
    ## be in curdir):
    file_to_stamp_file = os.path.basename(file_to_stamp_file).strip()
    name_file_to_stamp = _read_in_file("%s/%s" % (curdir, file_to_stamp_file))
    name_file_to_stamp.replace("\n", "").replace("\r", "")
    ##
    ## Get the name to be given to the file after it has been stamped (if there
    ## is one.) Once more, it will be found in a file in curdir:
    try:
        new_file_name_file = parameters['new_file_name']
    except KeyError:
        new_file_name_file = ""
    else:
        if new_file_name_file is None:
            new_file_name_file = ""
    ## Get the "basename" for the file containing the new file name. (It's
    ## mandatory that it be in curdir):
    new_file_name_file = os.path.basename(new_file_name_file).strip()
    new_file_name = _read_in_file("%s/%s" % (curdir, new_file_name_file))

    ############
    ## Begin:
    ############
    ##
    ## If no name for the file to stamp, warning.
    if name_file_to_stamp == "":
        wrn_msg = "Warning in Stamp_Replace_Single_File_Approval: " \
                  "It was not possible to recover a valid name for the " \
                  "file to be stamped. Stamping could not, therefore, be " \
                  "carried out. The submission ID is [%s]." \
                  % access
        raise InvenioWebSubmitFunctionWarning(wrn_msg)
    ##
    ## The file to be stamped is a bibdoc. We will only stamp it (a) if it
    ## exists; and (b) if it is a PDF file. So, get the path (in the bibdocs
    ## tree) to the file to be stamped:
    ##
    ## First get the object representing the bibdocs belonging to this record:
    bibrecdocs = BibRecDocs(recid)
    try:
        bibdoc_file_to_stamp = bibrecdocs.get_bibdoc("%s" % name_file_to_stamp)
    except InvenioBibDocFileError:
        ## Couldn't get a bibdoc object for this filename. Probably the file
        ## that we wanted to stamp wasn't attached to this record.
        wrn_msg = "Warning in Stamp_Replace_Single_File_Approval: " \
                  "It was not possible to recover a bibdoc object for the " \
                  "filename [%s] when trying to stamp the main file. " \
                  "Stamping could not be carried out. The submission ID is " \
                  "[%s] and the record ID is [%s]." \
                  % (name_file_to_stamp, access, recid)
        register_exception(prefix=wrn_msg)
        raise InvenioWebSubmitFunctionWarning(wrn_msg)
    ## Get the BibDocFile object for the PDF version of the bibdoc to be
    ## stamped:
    try:
        bibdocfile_file_to_stamp = bibdoc_file_to_stamp.get_file("pdf")
    except InvenioBibDocFileError:
        ## This bibdoc doesn't have a physical file with the extension ".pdf"
        ## (take note of the lower-case extension - the bibdocfile library
        ## is case-sensitive with respect to filenames.  Log that there was
        ## no "pdf" and check for a file with extension "PDF":
        wrn_msg = "Warning in Stamp_Replace_Single_File_Approval: " \
                  "It wasn't possible to recover a PDF BibDocFile object " \
                  "for the file with the name [%s], using the extension " \
                  "[pdf] - note the lower case - the bibdocfile library " \
                  "relies upon the case of an extension. The submission ID " \
                  "is [%s] and the record ID is [%s]. Going to try " \
                  "looking for a file with a [PDF] extension before giving " \
                  "up . . . " \
                  % (name_file_to_stamp, access, recid)
        register_exception(prefix=wrn_msg)
        try:
            bibdocfile_file_to_stamp = bibdoc_file_to_stamp.get_file("PDF")
        except InvenioBibDocFileError:
            wrn_msg = "Warning in Stamp_Replace_Single_File_Approval: " \
                      "It wasn't possible to recover a PDF " \
                      "BibDocFile object for the file with the name [%s], " \
                      "using the extension [PDF] - note the upper case. " \
                      "Had previously tried searching for [pdf] - now " \
                      "giving up. Stamping could not be carried out. " \
                      "The submission ID is [%s] and the record ID is [%s]." \
                      % (name_file_to_stamp, access, recid)
            register_exception(prefix=wrn_msg)
            raise InvenioWebSubmitFunctionWarning(wrn_msg)
    ############
    ## Go ahead and prepare the details for the LaTeX stamp template and its
    ## variables:
    ############
    ## Strip the LaTeX filename into the basename (All templates should be
    ## in the template repository):
    latex_template = os.path.basename(latex_template)

    ## Convert the string of latex template variables into a dictionary
    ## of search-term/replacement-term pairs:
    latex_template_vars = get_dictionary_from_string(
        latex_template_vars_string)
    ## For each of the latex variables, check in `CURDIR' for a file with that
    ## name. If found, use it's contents as the template-variable's value.
    ## If not, just use the raw value string already held by the template
    ## variable:
    latex_template_varnames = latex_template_vars.keys()
    for varname in latex_template_varnames:
        ## Get this variable's value:
        varvalue = latex_template_vars[varname].strip()
        if not ((varvalue.find("date(") == 0 and varvalue[-1] == ")") or \
                (varvalue.find("include(") == 0 and varvalue[-1] == ")")) \
                and varvalue != "":
            ## We don't want to interfere with date() or include() directives,
            ## so we only do this if the variable value didn't contain them:
            ##
            ## Is this variable value the name of a file in the current
            ## submission's working directory, from which a literal value for
            ## use in the template should be extracted? If yes, it will
            ## begin with "FILE:". If no, we leave the value exactly as it is.
            if varvalue.upper().find("FILE:") == 0:
                ## The value to be used is to be taken from a file. Clean the
                ## file name and if it's OK, extract that value from the file.
                ##
                seekvalue_fname = varvalue[5:].strip()
                seekvalue_fname = os.path.basename(seekvalue_fname).strip()
                if seekvalue_fname != "":
                    ## Attempt to extract the value from the file:
                    if os.access("%s/%s" % (curdir, seekvalue_fname), \
                                 os.R_OK|os.F_OK):
                        ## The file exists. Extract its value:
                        try:
                            repl_file_val = \
                              open("%s/%s" \
                                   % (curdir, seekvalue_fname), "r").readlines()
                        except IOError:
                            ## The file was unreadable.
                            err_msg = "Error in Stamp_Replace_Single_File_" \
                                      "Approval: The function attempted to " \
                                      "read a LaTex template variable " \
                                      "value from the following file in the " \
                                      "current submission's working " \
                                      "directory: [%s]. However, an " \
                                      "unexpected error was encountered " \
                                      "when doing so. Please inform the " \
                                      "administrator." \
                                      % seekvalue_fname
                            register_exception(req=user_info['req'])
                            raise InvenioWebSubmitFunctionError(err_msg)
                        else:
                            final_varval = ""
                            for line in repl_file_val:
                                final_varval += line
                            final_varval = final_varval.rstrip()
                            ## Replace the variable value with that which has
                            ## been read from the file:
                            latex_template_vars[varname] = final_varval
                    else:
                        ## The file didn't actually exist in the current
                        ## submission's working directory. Use an empty
                        ## value:
                        latex_template_vars[varname] = ""
                else:
                    ## The filename was not valid.
                    err_msg = "Error in Stamp_Replace_Single_File_Approval: " \
                              "The function was configured to read a LaTeX " \
                              "template variable from a file with the " \
                              "following instruction: [%s --> %s]. The " \
                              "filename, however, was not considered valid. " \
                              "Please report this to the administrator." \
                              % (varname, varvalue)
                    raise InvenioWebSubmitFunctionError(err_msg)

    ## Put the 'fixed' values into the file_stamper_options dictionary:
    file_stamper_options['latex-template'] = latex_template
    file_stamper_options['latex-template-var'] = latex_template_vars
    file_stamper_options['stamp'] = stamp
    file_stamper_options['layer'] = layer

    ## Put the input file and output file into the file_stamper_options
    ## dictionary:
    file_stamper_options['input-file'] = bibdocfile_file_to_stamp.fullpath
    file_stamper_options[
        'output-file'] = bibdocfile_file_to_stamp.get_full_name()
    ##
    ## Before attempting to stamp the file, log the dictionary of arguments
    ## that will be passed to websubmit_file_stamper:
    try:
        fh_log = open("%s/websubmit_file_stamper-calls-options.log" \
                      % curdir, "a+")
        fh_log.write("%s\n" % file_stamper_options)
        fh_log.flush()
        fh_log.close()
    except IOError:
        ## Unable to log the file stamper options.
        exception_prefix = "Unable to write websubmit_file_stamper " \
                           "options to log file " \
                           "%s/websubmit_file_stamper-calls-options.log" \
                           % curdir
        register_exception(prefix=exception_prefix)

    try:
        ## Try to stamp the file:
        (stamped_file_path_only, stamped_file_name) = \
                websubmit_file_stamper.stamp_file(file_stamper_options)
    except InvenioWebSubmitFileStamperError:
        ## It wasn't possible to stamp this file.
        ## Register the exception along with an informational message:
        wrn_msg = "Warning in Stamp_Replace_Single_File_Approval: " \
                  "There was a problem stamping the file with the name [%s] " \
                  "and the fullpath [%s]. The file has not been stamped. " \
                  "The submission ID is [%s] and the record ID is [%s]." \
                  % (name_file_to_stamp, \
                     file_stamper_options['input-file'], \
                     access, \
                     recid)
        register_exception(prefix=wrn_msg)
        raise InvenioWebSubmitFunctionWarning(wrn_msg)
    else:
        ## Stamping was successful. The BibDocFile must now be revised with
        ## the latest (stamped) version of the file:
        file_comment = "Stamped by WebSubmit: %s" \
                       % time.strftime("%d/%m/%Y", time.localtime())
        try:
            dummy = \
                  bibrecdocs.add_new_version("%s/%s" \
                                             % (stamped_file_path_only, \
                                                stamped_file_name), \
                                                name_file_to_stamp, \
                                                comment=file_comment, \
                                                flags=('STAMPED', ))
        except InvenioBibDocFileError:
            ## Unable to revise the file with the newly stamped version.
            wrn_msg = "Warning in Stamp_Replace_Single_File_Approval: " \
                      "After having stamped the file with the name [%s] " \
                      "and the fullpath [%s], it wasn't possible to revise " \
                      "that file with the newly stamped version. Stamping " \
                      "was unsuccessful. The submission ID is [%s] and the " \
                      "record ID is [%s]." \
                      % (name_file_to_stamp, \
                         file_stamper_options['input-file'], \
                         access, \
                         recid)
            register_exception(prefix=wrn_msg)
            raise InvenioWebSubmitFunctionWarning(wrn_msg)
        else:
            ## File revised. If the file should be renamed after stamping,
            ## do so.
            if new_file_name != "":
                try:
                    bibrecdocs.change_name(newname=new_file_name,
                                           docid=bibdoc_file_to_stamp.id)
                except (IOError, InvenioBibDocFileError):
                    ## Unable to change the name
                    wrn_msg = "Warning in Stamp_Replace_Single_File_Approval" \
                              ": After having stamped and revised the file " \
                              "with the name [%s] and the fullpath [%s], it " \
                              "wasn't possible to rename it to [%s]. The " \
                              "submission ID is [%s] and the record ID is " \
                              "[%s]." \
                              % (name_file_to_stamp, \
                                 file_stamper_options['input-file'], \
                                 new_file_name, \
                                 access, \
                                 recid)
    ## Finished.
    return ""