All Downloads are FREE. Search and download functionalities are using the official Maven repository.

portal.default.redbox.scripts.actions.andsDoi.py Maven / Gradle / Ivy

There is a newer version: 1.10.2
Show newest version
from com.googlecode.fascinator.api.storage import StorageException
from com.googlecode.fascinator.common import BasicHttpClient, JsonSimple

from java.lang import Exception

from org.apache.commons.httpclient.methods import GetMethod
from org.apache.commons.httpclient.methods import PostMethod

class AndsDoiData:
    def __init__(self):
        # Some templates for making our XML
        self.xml_xmlWrapper = "\n%s"
        self.xml_id = "%s\n"
        self.xml_title = "%s\n"
        self.xml_publisher = "%s\n"
        self.xml_pubYear = "%s\n"
        self.xml_creator = "%s\n"
        self.xml_creatorWrapper = "\n%s\n"

    def __activate__(self, context):
        self.velocityContext = context

        self.log = context["log"]
        self.storage = self.vc("Services").getStorage()
        self.responseJson = JsonSimple()
        self.writer = self.vc("response").getPrintWriter("application/json; charset=UTF-8")

        if self.doiSecurityCheck():
            self.process()
        else:
            self.throwError("Access Denied")

    def doiConfig(self, key):
        config = self.vc("systemConfig")
        return config.getString(None, ["andsDoi", key]);

    def doiSecurityCheck(self):
        config = self.vc("systemConfig")
        allowedUsers = config.getStringList(["andsDoi", "security", "users"])
        allowedRoles = config.getStringList(["andsDoi", "security", "roles"])

        userName = self.vc("page").authentication.get_username()
        userRoles = self.vc("page").authentication.get_roles_list()
        if userName in allowedUsers:
            return True
        for role in userRoles:
            if role in allowedRoles:
                return True
        return False

    def getApiUrl(self, page):
        baseUrl = self.doiConfig("apiBaseUrl")
        apiKey = self.doiConfig("apiKey")

        # Create = https://services.ands.org.au/doi/1.1/mint.json?app_id=$app_id&url=$url
        if page == "create":
            #return baseUrl + "mint.json?app_id=" + apiKey + "&url="
            return baseUrl + "mint.json/?app_id=" + apiKey + "&url="

        # Update = https://services.ands.org.au/doi/1.1/update.json?app_id=$app_id&doi=$DOI_id[&url=$url]
        if page == "update":
            return baseUrl + "update.json/?app_id=" + apiKey + "&doi="

        # Activate = https://services.ands.org.au/doi/1.1/activate.json?app_id=$app_id&doi=$DOI_id
        if page == "activate":
            return baseUrl + "activate.json/?app_id=" + apiKey + "&doi="

        # Deactivate = https://services.ands.org.au/doi/1.1/deactivate.json?app_id=$app_id&doi=$DOI_id
        if page == "deactivate":
            return baseUrl + "deactivate.json/?app_id=" + apiKey + "&doi="

        # Get = https://services.ands.org.au/doi/1.1/xml.json?doi=$DOI_id
        if page == "get":
            return baseUrl + "xml.json/?doi="

    # Get from velocity context
    def vc(self, index):
        if self.velocityContext[index] is not None:
            return self.velocityContext[index]
        else:
            log.error("ERROR: Requested context entry '" + index + "' doesn't exist")
            return None

    def activateDoi(self):
        self.throwError("Not implemented yet")

    def buildXml(self, json, doi):
        xmlString = "";

        ## Check metadata validity along the way
        if doi is None or doi == "":
            ## During DOI creation a URL is mandatory,
            ##   but not part of the XML, so...
            ## 1) Skip this is we have a DOI
            ## 2) Make sure it exists otherwise
            ## 3) But don't add into the XML
            url = json.getString(None, ["url"])
            if url is None or url == "":
                return None
            # We have to fake a DOI to get through the validator
            xmlString += self.xml_id % ("10.0/0")
        else:
            ## Once we have a DOI it needs to go in the XML.
            ## URL is optional in this case, and still not in XML
            xmlString += self.xml_id % (doi)

        creators = json.getStringList(["creators"])
        if creators is None or creators.isEmpty():
            return None
        else:
            creatorString = ""
            for creator in creators:
                creatorString += self.xml_creator % (creator)
            xmlString += self.xml_creatorWrapper % (creatorString)

        title = json.getString(None, ["title"])
        if title is None or title == "":
            return None
        else:
            xmlString += self.xml_title % (title)

        publisher = json.getString(None, ["publisher"])
        if publisher is None or publisher == "":
            return None
        else:
            xmlString += self.xml_publisher % (publisher)

        pubYear = json.getString(None, ["pubYear"])
        if pubYear is None or pubYear == "":
            return None
        else:
            xmlString += self.xml_pubYear % (pubYear)

        return self.xml_xmlWrapper % (xmlString)

    def urlGet(self, url):
        try:
            client = BasicHttpClient(url)
            get = GetMethod(url)
            code = client.executeMethod(get)
            body = get.getResponseBodyAsString().strip()
            return (str(code), body)
        except Exception, e:
            self.log.error("Error during HTTP request: ", e)
            return (None, None)

    def urlPost(self, url, postBody):
        try:
            client = BasicHttpClient(url)
            post = PostMethod(url)
            #######
            # Which will work?
            #post.setRequestBody(postBody)
            post.addParameter("xml", postBody) 
            #######
            code = client.executeMethod(post)
            if str(code) == "302":
                locationHeader = post.getResponseHeader("location");
                if locationHeader is not None:
                    redirectLocation = locationHeader.getValue();
                    self.log.info("302 Redirection was requested: '{}'", redirectLocation)
                    ##return self.urlPost(redirectLocation, postBody)
            body = post.getResponseBodyAsString().strip()
            return (str(code), body)
        except Exception, e:
            self.log.error("Error during HTTP request: ", e)
            return (None, None)

    def createDoi(self):
        jsonString = self.vc("formData").get("json")
        json = self.parseJson(jsonString)
        if json is None:
            return

        oid = json.getString(None, ["oid"])
        oldDoi = self.getDoiFromStorage(oid)
        if oldDoi is not None:
            if oldDoi is False:
                # An error, and it has already been thrown
                return
            else:
                self.throwError("There's already a DOI for this object! '"+oldDoi+"'")
                return

        xmlString = self.buildXml(json, None)
        if xmlString is None:
            self.throwError("Error during XML creation")
            return
        else:
            self.log.debug("XML:\n{}", xmlString)

        andsUrl = self.getApiUrl("create")
        ourUrl = json.getString(None, ["url"])
        if (andsUrl is not None and ourUrl is not None):
            andsUrl += ourUrl
            #andsUrl += "http://www.example.org"
        self.log.debug("About to create DOI via URL: '{}'", andsUrl)
        (code, body) = self.urlPost(andsUrl, xmlString)
        self.log.debug("Response Code: '{}'", code)
        self.log.debug("Response Body: '{}'", body)
        if code != "200":
            self.throwError("Invalid response from ANDS server, code "+code+ ": "+body)
            return

        # Grab the DOI from their response string
        # Modified to match ANDS service point version 2.2
        andsJsonResp = self.parseJson(body)
        if andsJsonResp is None:
            self.throwError("We received a 200 response from ANDS, but the body format was not as expected. Response body: '"+body.replace("\n", "\\n")+"'")
            return
        responseCode = andsJsonResp.getString(None, ["response", "responsecode"])
        if responseCode != "MT001":
            self.throwError(responseCode + "-" + andsJsonResp.getString(None, ["response", "verbosemessage"]))
            return
        doi = andsJsonResp.getString(None, ["response", "doi"]) 
        stored = self.storeDoi(doi, oid)
        if not stored:
            # We've already thrown the error
            return

        self.responseJson.getJsonObject().put("doi", doi)
        respStr = self.responseJson.toString(True)
        self.log.debug("Sending response to client:")
        self.log.debug(respStr)
        self.writer.println(respStr)
        self.writer.close()

    def getDoiFromStorage(self, oid):
        propName = self.doiConfig("doiProperty");
        if propName is None:
            self.throwError("Error accessing storing, no configured storage property name for DOIs!")
            return False

        try:
            object = self.storage.getObject(oid);
            metadata = object.getMetadata()
            return metadata.getProperty(propName)
        except StorageException, e:
            self.throwError("Error accessing DOI. See system logs!")
            self.log.error("Error stacktrace: ", e)
            return False

    def storeDoi(self, doi, oid):
        propName = self.doiConfig("doiProperty");
        if propName is None:
            self.throwError("Error storing DOI, no configured storage property name!")
            return False

        try:
            object = self.storage.getObject(oid);
            metadata = object.getMetadata()
            metadata.setProperty(propName, doi)
            object.close();
            self.log.debug("DOI '{}' stored for OID '{}'", doi, oid)
            return True
        except StorageException, e:
            self.throwError("Error storing DOI. See system logs!")
            self.log.error("Error stacktrace: ", e)
            return False

    def deactivateDoi(self):
        self.throwError("Not implemented yet")

    def getXml(self):
        self.throwError("Not implemented yet")

    def parseJson(self, jsonString):
        ## Parse JSON Metadata
        try:
            return JsonSimple(jsonString)
        except Exception, e:
            self.log.error("ERROR invalid JSON: ", e)
            self.log.error("Invalid JSON was:\n{}", jsonString)
            self.throwError("Parsing JSON failed : " + e.getMessage())
            return None

    def process(self):
        valid = self.vc("page").csrfSecurePage()
        if not valid:
            self.throwError("Invalid request")
            return

        action = self.vc("formData").get("verb")

        switch = {
            "createDoi"      : self.createDoi,
            "updateDoi"      : self.updateDoi,
            "activateDoi"    : self.activateDoi,
            "deactivateDoi"  : self.deactivateDoi,
            "getXml"         : self.getXml
        }
        switch.get(action, self.unknownAction)()

    def throwError(self, message):
        self.log.error("andsDoi.py : " + message);
        self.vc("response").setStatus(500)
        self.responseJson.getJsonObject().put("error", "Error: " + message)
        self.writer.println(self.responseJson.toString(True))
        self.writer.close()

    def updateDoi(self):
        self.throwError("Not implemented yet")

    def unknownAction(self):
        verb = self.vc("formData").get("verb") or "null"
        self.throwError("Unknown action requested - '" + verb + "'")




© 2015 - 2024 Weber Informatics LLC | Privacy Policy