Changeset 4696 for exist


Ignore:
Timestamp:
22/12/08 09:53:07 (11 years ago)
Author:
cbyrom
Message:

Adjust Atom and MolesEntity? data models to properly use namespaces when
dealing with xpath queries - rather than having these stripped out. This
avoids problems when namespaces are given arbitrary names and is a more
exact, hence robust, approach.
Create new test class to put the xmlhandler2 tests separately in.
Add delete function to granulite - to allow data granules, and their
connections to data entities, to be removed + add 'roll back' functionality
to cope with scenarios when granulite replace/delete fails to complete
properly. Add new methods to the existdbclient to allow the restore/delete/backup
functionality.
Extend test suite to exercise new functionality.

Location:
exist/trunk/python
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • exist/trunk/python/ndgUtils/lib/existdbclient.py

    r4679 r4696  
    55 @author: C Byrom - Tessella 08 
    66''' 
    7 import os, sys, logging, datetime, uuid 
     7import os, sys, logging, datetime, uuid, re 
    88from ndgUtils.models.Atom import Atom 
    99from ndgUtils.eXistConnector import eXistConnector as ec 
     
    6767         
    6868        self.atomSchema = None 
     69         
     70        # this keeps a record of the last file backed up - incase we need 
     71        # to do a rollback if there's an error encountered 
     72        self.backupName = None 
     73         
    6974        # NB, there are two routes through here: if a config file is specified 
    7075        # without a hostname, the host will be taken to be the first entry in 
     
    7479            if not self.eXistDBHostname: 
    7580                self.__loadDBDetails(configFile) 
    76              
    7781             
    7882        # Now set up the connection 
     
    319323 
    320324 
    321     def backupEXistFile(self, collection, fileName): 
     325    def __removeTimeStamp(self, fileName): 
     326        ''' 
     327        Remove a timestamp from a file name 
     328        ''' 
     329        match = re.search('(.*)(_\d{4}-\d{2}-\d{2}T\d{2}_\d{2}_\d{2})(.*)', fileName) 
     330        if match: 
     331            return match.group(1) + match.group(3) 
     332 
     333        return fileName 
     334 
     335    def backupEXistFile(self, collection, fileName, runAsynch = True): 
    322336        ''' 
    323337        Backup a file that exists in the eXist DB 
     
    328342        @param collection: path of the collection to store the file in 
    329343        @param fileName: name of file to add in eXist 
     344        @param runAsynch: if True, do the backup asynchronously in a separate thread 
    330345        @return: path to new backup file 
    331346        ''' 
     
    351366        docPath = collection + fileName 
    352367         
    353         # run the back up in a separate thread 
    354         thread = backingUpThread(self, doc, collection, fileName) 
    355         thread.start() 
    356  
     368        if runAsynch: 
     369            # run the back up in a separate thread 
     370            thread = backingUpThread(self, doc, collection, fileName) 
     371            thread.start() 
     372        else: 
     373            self.createEXistFile(doc, collection, fileName) 
     374             
    357375        return docPath 
    358376 
     
    505523 
    506524             
    507     def createAtomInExist(self, atom, replaceAtom = True): 
     525    def createAtomInExist(self, atom, replaceAtom = True, runAsynch = True): 
    508526        ''' 
    509527        Create an atom in the eXist DB - using the atom contents to work out 
     
    511529        @param atom: atom object to create in the DB 
    512530        @keyword replaceAtom: if False and the atom is already available in eXist 
     531        @param runAsynch: if True, if a backup of an existing file, do this 
     532        asynchronously in a separate thread 
    513533        then raise a ValueError. 
     534         
    514535        ''' 
    515536        logging.info("Creating atom in eXist") 
     
    549570                raise DuplicateError('An atom with the specified ID (%s) already exists in eXist' \ 
    550571                                     %atom.datasetID) 
    551             self.backupEXistFile(eXistCollection, atom.atomName) 
     572            # store name of backup - to allow restore, if subsequent ops fail 
     573            self.backupName = self.backupEXistFile(eXistCollection, atom.atomName, \ 
     574                                                   runAsynch = runAsynch) 
    552575             
    553576            # also change updated date to current time 
     
    557580        logging.info("Atom created in eXist") 
    558581        return atom 
     582     
     583     
     584    def restoreBackup(self, docPath): 
     585        ''' 
     586        Restore the backed up file - effectively recreating in the non-backup collection 
     587        @param docPath: path to file to backup 
     588        ''' 
     589        logging.info("Restoring file, '%s' in eXist" %docPath) 
     590        doc = self.getEXistFile(docPath) 
     591         
     592        if not doc: 
     593            errorMessage = "Could not retrieve file contents (%s) to backup - exiting." %docPath 
     594            logging.error(errorMessage) 
     595            raise SystemError(errorMessage) 
     596         
     597        bits = docPath.split('/') 
     598        fileName = bits[-1] 
     599        collection = '/'.join(bits[0:-1]) 
     600        # remove timestamp from filename 
     601        fileName = self.__removeTimeStamp(fileName) 
     602         
     603        # Now adjust the collection to map to the backup dir 
     604        collection = collection.replace(ec.BACKUP_COLLECTION_PATH, ec.BASE_COLLECTION_PATH) 
     605        collection = collection.replace(ec.NDG_A_COLLECTION_PATH_BACKUP, ec.NDG_A_COLLECTION_PATH) 
     606         
     607        self.createEXistFile(doc, collection, fileName) 
     608        logging.info("File restored") 
     609         
  • exist/trunk/python/ndgUtils/lib/granulite.py

    r4679 r4696  
    7272                  eXistClient = None, csmlOrCdmlFile = None, \ 
    7373                  aggregateCoverage = False, useCSMLID = False,  
    74                   timeAxis = 'time', datasetID = None, replaceAtom = False): 
     74                  timeAxis = 'time', datasetID = None, replaceAtom = False,  
     75                  deleteMode = False): 
    7576        ''' 
    7677        Constructor for granulite object - NB, the csml/cdml keywords allow use 
     
    9495        the atom to be created by the granulite, automatically overwrite the older 
    9596        atom  
     97        @keyword deleteMode: if True the granule atom and any references to it are 
     98        deleted from eXist - when processGranulite are ran 
    9699        ''' 
    97100        logging.info("Creating granulite data model") 
     
    125128             
    126129        self._replaceAtom = replaceAtom 
     130        self._deleteMode = deleteMode 
    127131        self.useCSMLID = useCSMLID 
    128132        self._cdmlTimeAxis = timeAxis 
     
    367371            dataEntityProviderID = data[1] 
    368372             
    369             try: 
    370                 self.__updateDataEntity(dataEntityID) 
    371             except: 
    372                 logging.error("Exception thrown - detail: ") 
    373                 logging.error(sys.exc_info()) 
    374                 logging.info("Continue processing other data entities") 
     373            self.__updateDataEntity(dataEntityID) 
    375374 
    376375        logging.info("Granule data added to data entities") 
    377  
    378  
    379     def __updateDataEntity(self, dataEntityID): 
     376      
     377     
     378    def __removeGranuleFromDataEntityRecords(self): 
     379        ''' 
     380        Remove references to the granule in any data entity records 
     381        ''' 
     382        logging.info("Removing granule info from data entities") 
     383 
     384        logging.info("Retrieving data entities with references to granule") 
     385        self._atom.lookupAssociatedData(VTD.DE_TERM, self._eXist.xmldb, \ 
     386                                    lookupIndirectReferences = True) 
     387         
     388        # now set up the granule links to the data entities specified 
     389        for de in self._atom.dataEntities: 
     390            self.__updateDataEntity(de.rel, removeLink = True) 
     391        logging.info("Granule data removed from data entities") 
     392 
     393 
     394    def __updateDataEntity(self, dataEntityID, removeLink = False): 
    380395        ''' 
    381396        Retrieve the specified data entity and add a link to the current 
    382397        data granule, if required, then update the atom in eXist 
    383398        @param dataEntityID: ID of the data entity to augment with granule link 
    384         ''' 
    385         logging.debug("Retrieving data entity atom - to attach granule to") 
     399        @keyword removeLink: If True, remove the link to the current data granule 
     400        - otherwise add it 
     401        ''' 
     402        logging.debug("Retrieving data entity atom - to update associated granule info") 
    386403        doc = self._eXist.getAtom('dataent_' + dataEntityID) 
    387         logging.debug("DE retrieved - now adding link to granule") 
     404        logging.debug("DE retrieved") 
    388405        de = Atom.Atom(xmlString=str(doc)) 
    389406        noLinks = len(de.relatedLinks) 
    390         de.addRelatedLinks(self._atom.atomBrowseURL + " | " + \ 
    391                            self._atom.title + " | " + \ 
    392                            self._atom.VTD.getTermCurrentVocabURL(VTD.GRANULE_TERM)) 
    393          
     407         
     408        linkData = "%s | %s | %s " %(self._atom.atomBrowseURL, 
     409                                     self._atom.title, 
     410                                     self._atom.VTD.getTermCurrentVocabURL(VTD.GRANULE_TERM)) 
     411        if removeLink: 
     412            logging.debug("- now removing link to granule") 
     413            link = Atom.Link() 
     414            link.fromString(linkData) 
     415            de.removeRelatedLinks(link) 
     416            logging.debug("Link removed") 
     417        else: 
     418            logging.debug("- now adding link to granule") 
     419            de.addRelatedLinks(linkData) 
     420            logging.debug("Link added") 
     421             
    394422        # only save if need be 
    395423        if len(de.relatedLinks) == noLinks: 
    396             logging.info("- data entity already contains link to this granule - skpping") 
     424            logging.info("- data entity unchanged - skipping") 
    397425            return 
    398426         
     
    552580        self.__applyGranuliteDetails(inputs) 
    553581         
    554         # add the granule to eXist - if this exists already a DuplicationError 
    555         # will be thrown if backups are not allowed 
    556         doReplace = replaceAtom or self._replaceAtom 
    557         logging.info("Creating granule atom, '%s', in eXist DB" %self._atom.atomName) 
    558         self._eXist.createAtomInExist(self._atom, replaceAtom = doReplace) 
    559  
    560         # now add the granule data to the data entity in eXist 
    561         self.__addGranuleToDataEntityRecords() 
    562          
     582        if self._deleteMode: 
     583            logging.info("In delete mode - deleting granule atom and any references") 
     584            self.__deleteGranuleAndDEReferences() 
     585             
     586        else: 
     587            # add the granule to eXist - if this exists already a DuplicationError 
     588            # will be thrown if backups are not allowed 
     589            doReplace = replaceAtom or self._replaceAtom 
     590            logging.info("Creating granule atom, '%s', in eXist DB" %self._atom.atomName) 
     591            self._eXist.createAtomInExist(self._atom, replaceAtom = doReplace) 
     592             
     593            # if the atom already exists, keep track of the backed up file - incase it 
     594            # needs to be restored 
     595            self.backupName = self._eXist.backupName 
     596         
     597            # now add the granule data to the data entity in eXist 
     598            # NB, if problems occur here, rollback changes 
     599            try: 
     600                # if the atom existed before, clear out the links to it before we start 
     601                if self.backupName: 
     602                    self.__removeGranuleFromDataEntityRecords() 
     603                     
     604                self.__addGranuleToDataEntityRecords() 
     605            except: 
     606                logging.error("Exception thrown whilst updating data entities - detail: ") 
     607                logging.error(sys.exc_info()) 
     608                logging.error("Will now roll back changes to granule atom to leave system in original state") 
     609                self.__deleteGranuleAndDEReferences() 
     610 
     611                # if the atom existed before, restore it and recreate the old DE links 
     612                if self.backupName: 
     613                    self._eXist.restoreBackup(self._eXist.backupName) 
     614                    self.__restoreGranuleToDataEntityRecords() 
     615                 
    563616        logging.info("granulite processing complete") 
    564617        return self._atom 
    565618 
     619 
     620    def __deleteGranuleAndDEReferences(self): 
     621        ''' 
     622        Delete the granule atom and any references in data entities to it 
     623        ''' 
     624        self.__deleteGranule() 
     625        self.__removeGranuleFromDataEntityRecords() 
     626 
     627 
     628    def __deleteGranule(self): 
     629        ''' 
     630        Delete granule data - remove granule atom from eXist and all references to  
     631        granule in DEs 
     632        ''' 
     633        logging.info("Deleting granule...") 
     634        logging.info("Remove granule atom from eXist") 
     635        self._eXist.deleteAtomInExist(self._atom) 
     636        logging.info("Granule deleted") 
     637 
     638 
     639    def __restoreGranuleToDataEntityRecords(self): 
     640        ''' 
     641        If a granulite ingest has failed whilst replacing an existing granule,  
     642        restore the original DE links to this granule 
     643        ''' 
     644        logging.info("Restoring original links with data entities") 
     645        # clear out any DEs from the current granulite input 
     646        self._dataEntityIDs = [] 
     647         
     648        # now add back in the original DE links 
     649        for de in self._atom.dataEntities: 
     650            deLink = "%s | %s | %s " %(de.href, de.title, de.rel) 
     651            self._dataEntityIDs.append(deLink) 
     652         
     653        self.__addGranuleToDataEntityRecords() 
     654        logging.info("Finished restoring links") 
     655         
    566656 
    567657if __name__=="__main__": 
     
    573663    loggingLevel = logging.WARNING 
    574664    isReplace = False 
     665    isDelete = False 
    575666    for o, a in opts: 
    576667        if o == "-v": 
     
    589680    logging.basicConfig(level = loggingLevel, 
    590681                        format='%(asctime)s %(filename)s:%(lineno)d %(levelname)s %(message)s') 
    591     g = granulite(args[0], replaceAtom = isReplace) 
     682    g = granulite(args[0], replaceAtom = isReplace, deleteMode = isDelete) 
    592683     
    593684    try: 
  • exist/trunk/python/ndgUtils/models/Atom.py

    r4679 r4696  
    1414        from ndgUtils.elementtree import cElementTree as ET 
    1515import sys, logging, re, datetime 
     16from ndgUtils import ndgObject 
    1617from ndgUtils.eXistConnector import eXistConnector 
    1718from ndgUtils.ETxmlView import et2text 
     
    7071         
    7172    def fromETElement(self, personTag): 
    72         self.name = personTag.findtext('name') or "" 
    73         self.role = personTag.findtext('role') or "" 
    74         self.uri = personTag.findtext(self.uriTagName) or "" 
     73        self.name = personTag.findtext('{%s}name' %ndgObject.ATOM_NS) or "" 
     74        self.role = personTag.findtext('{%s}role' %ndgObject.ATOM_NS) or "" 
     75        self.uri = personTag.findtext('{%s}%s' %(ndgObject.ATOM_NS, self.uriTagName)) or "" 
    7576        logging.debug("Added name: '%s', role: '%s', %s: '%s'" \ 
    7677                      %(self.name, self.role, self.uriTagName, self.uri)) 
     
    561562        logging.info("Creating formatted XML version of Atom") 
    562563        root = ET.Element("entry") 
    563         root.attrib["xmlns"] = "http://www.w3.org/2005/Atom" 
    564         root.attrib["xmlns:moles"] = "http://ndg.nerc.ac.uk/schema/moles2beta" 
    565         root.attrib["xmlns:georss"] = "http://www.georss.org/georss/10" 
    566         root.attrib["xmlns:gml"] = "http://www.opengis.net/gml" 
     564        root.attrib["xmlns"] = ndgObject.ATOM_NS 
     565        root.attrib["xmlns:moles"] = ndgObject.MOLES_NS 
     566        root.attrib["xmlns:georss"] = ndgObject.GEOSS_NS 
     567        root.attrib["xmlns:gml"] = ndgObject.GML_NS 
    567568        id = ET.SubElement(root, "id") 
    568569        id.text = self.atomID 
     
    598599            content.attrib["type"] = "xhtml" 
    599600            div = ET.SubElement(content, 'div') 
    600             div.attrib["xmlns"] = "http://www.w3.org/1999/xhtml" 
     601            div.attrib["xmlns"] = ndgObject.XHTML_NS 
    601602            div.text = self.Content 
    602603         
     
    678679        ''' 
    679680        logging.info("Ingesting data from XML string") 
    680          
    681         # firstly, remove any namespaces used - to avoid problems with elementtree 
    682         logging.debug("Stripping moles namespace from string to allow easy handling with elementtree") 
    683         xmlString = xmlString.replace('moles:', '') 
    684         xmlString = xmlString.replace('georss:', '') 
    685         xmlString = xmlString.replace('gml:', '') 
    686         xmlString = xmlString.replace('xmlns="http://www.w3.org/2005/Atom"', '') 
    687         xmlString = xmlString.replace('default:', '') 
    688         xmlString = xmlString.replace('xs:', '') 
    689  
    690         # now create elementtree with the XML string 
    691681        logging.debug("Create elementtree instance with XML string") 
    692682        tree = ET.fromstring(xmlString) 
    693          
    694         title = tree.findtext('title') 
     683        title = tree.findtext('{%s}title' %ndgObject.ATOM_NS) 
    695684        if title: 
    696685            logging.debug("Adding title data") 
    697686            self.title = title 
    698687 
    699         summary = tree.findtext('summary') 
     688        summary = tree.findtext('{%s}summary' %ndgObject.ATOM_NS) 
    700689        if summary: 
    701690            self.Summary = summary#.decode('unicode_escape') 
    702691 
    703         authorElement = tree.find('author') 
     692        authorElement = tree.find('{%s}author' %ndgObject.ATOM_NS) 
    704693        if authorElement: 
    705694            logging.debug("Adding author data") 
     
    708697            self.author = author 
    709698 
    710         contributorElements = tree.findall('contributor') 
     699        contributorElements = tree.findall('{%s}contributor' %ndgObject.ATOM_NS) 
    711700        for contributorElement in contributorElements: 
    712701            logging.debug("Adding contributor data") 
     
    715704            self.contributors.append(contributor) 
    716705 
    717         molesElement = tree.find('entity') 
     706        molesElement = tree.find('{%s}entity' %ndgObject.MOLES_NS) 
    718707        if molesElement: 
    719708            self.ME.fromET(molesElement) 
    720709                 
    721         self.atomID = tree.findtext('id') 
    722  
    723         self._parseCategoryData(tree.findall('category')) 
    724  
    725         self._parseLinksData(tree.findall('link')) 
    726              
    727         contentTag = tree.find('content') 
     710        self.atomID = tree.findtext('{%s}id' %ndgObject.ATOM_NS) 
     711 
     712        self._parseCategoryData(tree.findall('{%s}category' %ndgObject.ATOM_NS)) 
     713 
     714        self._parseLinksData(tree.findall('{%s}link' %ndgObject.ATOM_NS)) 
     715             
     716        contentTag = tree.find('{%s}content' %ndgObject.ATOM_NS) 
    728717        if contentTag != None: 
    729718            logging.debug("Found content tag - checking for CSML/CDML file data") 
     
    743732                self.Content = div.text 
    744733         
    745         range = tree.findtext('temporalRange') 
     734        range = tree.findtext('{%s}temporalRange' %ndgObject.MOLES_NS) 
    746735        if range: 
    747736            logging.debug("Adding temporal range data") 
     
    751740                self.t2 = timeData[1] 
    752741         
    753         # NB, this parser won't mind if we're dealing with Envelope or EnvelopeWithTimePeriod 
    754         minBBox = tree.findall('.//lowerCorner') 
    755         if minBBox: 
    756             logging.debug("Adding min spatial range data") 
    757             minBBox = minBBox[0] 
    758             spatialData = minBBox.text.split() 
    759             self.minX = spatialData[0] 
    760             if len(spatialData) > 1: 
    761                 self.minY = spatialData[1] 
    762          
    763         maxBBox = tree.findall('.//upperCorner') 
    764         if maxBBox: 
    765             maxBBox = maxBBox[0] 
    766             logging.debug("Adding max spatial range data") 
    767             spatialData = maxBBox.text.split() 
    768             self.maxX = spatialData[0] 
    769             if len(spatialData) > 1: 
    770                 self.maxY = spatialData[1] 
     742        where = tree.find('{%s}where' %ndgObject.GEOSS_NS) 
     743        if where: 
     744            # NB, this parser won't mind if we're dealing with Envelope or EnvelopeWithTimePeriod 
     745            minBBox = where.findall('.//{%s}lowerCorner' %ndgObject.GML_NS) 
     746            if minBBox: 
     747                logging.debug("Adding min spatial range data") 
     748                minBBox = minBBox[0] 
     749                spatialData = minBBox.text.split() 
     750                self.minX = spatialData[0] 
     751                if len(spatialData) > 1: 
     752                    self.minY = spatialData[1] 
     753             
     754            maxBBox = where.findall('.//{%s}upperCorner' %ndgObject.GML_NS) 
     755            if maxBBox: 
     756                maxBBox = maxBBox[0] 
     757                logging.debug("Adding max spatial range data") 
     758                spatialData = maxBBox.text.split() 
     759                self.maxX = spatialData[0] 
     760                if len(spatialData) > 1: 
     761                    self.maxY = spatialData[1] 
    771762                 
    772763        publishedDate = tree.findtext('published') 
     
    12461237        logging.info("Retrieving info from %s references" %type) 
    12471238        if type == VTD.DEPLOYMENT_TERM: 
     1239            logging.info("Extracting links data to deployment entitites") 
    12481240            self.deployments = [] 
    12491241            for link in links: 
     
    12641256            # for DE data, just store the title + link in a Link object 
    12651257            self.dataEntities = [] 
     1258            logging.info("Extracting links data to data entitites") 
    12661259            for data in links: 
    12671260                atom = Atom(xmlString=str(data)) 
     
    12691262                link.title = atom.title 
    12701263                link.href = atom.atomBrowseURL 
     1264                link.rel = atom.datasetID 
    12711265                 
    12721266                # NB, different deployments may be used by the same DE - so  
  • exist/trunk/python/ndgUtils/models/MolesEntity.py

    r4490 r4696  
    1515import logging, datetime 
    1616from ndgUtils.lib.utilities import escapeSpecialCharacters 
    17          
     17from ndgUtils import ndgObject 
    1818 
    1919class MolesEntity(object): 
     
    6969        logging.info("Creating formatted XML version of MolesEntity") 
    7070        molesEntity = ET.Element("moles:entity") 
    71         molesEntity.attrib["xmlns:moles"] = "http://ndg.nerc.ac.uk/schema/moles2beta" 
     71        molesEntity.attrib["xmlns:moles"] = ndgObject.MOLES_NS 
    7272         
    7373        molesISO = ET.SubElement(molesEntity, "moles:molesISO") 
     
    111111        ''' 
    112112        logging.info("Ingesting data from ElementTree object") 
    113          
    114         authorElements = tree.findall('molesISO/responsibleParties/responsibleParty') 
     113        authorElements = tree.findall('{%s}molesISO/{%s}responsibleParties/{%s}responsibleParty' \ 
     114                                      %(ndgObject.MOLES_NS, ndgObject.MOLES_NS, ndgObject.MOLES_NS)) 
    115115        from ndgUtils.models.Atom import Person 
    116116        for authorElement in authorElements: 
     
    120120            self.responsibleParties.append(author) 
    121121                 
    122         self.abbreviation = tree.findtext('molesISO/abbreviation') 
     122        self.abbreviation = tree.findtext('{%s}molesISO/{%s}abbreviation' \ 
     123                                          %(ndgObject.MOLES_NS, ndgObject.MOLES_NS)) 
    123124 
    124         self.providerID = tree.findtext('molesISO/providerID') 
     125        self.providerID = tree.findtext('{%s}molesISO/{%s}providerID' \ 
     126                                        %(ndgObject.MOLES_NS, ndgObject.MOLES_NS)) 
    125127 
    126         self.datasetLanguage = tree.findtext('molesISO/datasetLanguage') 
    127         self.metadataLanguage = tree.findtext('molesISO/metadataLanguage') 
     128        self.datasetLanguage = tree.findtext('{%s}molesISO/{%s}datasetLanguage' \ 
     129                                             %(ndgObject.MOLES_NS, ndgObject.MOLES_NS)) 
     130        self.metadataLanguage = tree.findtext('{%s}molesISO/{%s}metadataLanguage' \ 
     131                                              %(ndgObject.MOLES_NS, ndgObject.MOLES_NS)) 
    128132 
    129         createdDate = tree.findtext('molesISO/created') 
     133        createdDate = tree.findtext('{%s}molesISO/{%s}created' \ 
     134                                    %(ndgObject.MOLES_NS, ndgObject.MOLES_NS)) 
    130135        if createdDate: 
    131136            logging.debug("Adding created date") 
     
    142147        logging.info("Ingesting data from XML string") 
    143148         
    144         # firstly, remove any namespaces used - to avoid problems with elementtree 
    145         logging.debug("Stripping moles namespace from string to allow easy handling with elementtree") 
    146         xmlString = xmlString.replace('moles:', '') 
    147  
    148149        # now create elementtree with the XML string 
    149150        logging.debug("Create elementtree instance with XML string") 
  • exist/trunk/python/ndgUtils/ndgObject.py

    r4564 r4696  
    2727    GML_NS = 'http://www.opengis.net/gml' 
    2828    CSML_NS = 'http://ndg.nerc.ac.uk/csml' 
     29    XHTML_NS = "http://www.w3.org/1999/xhtml" 
    2930    CDML_DTD = 'http://www-pcmdi.llnl.gov/software/cdms/cdml.dtd' 
    3031         
  • exist/trunk/python/ndgUtils/xmlHandler2.py

    r4196 r4696  
    225225                return rr 
    226226        else: return rr[0] 
    227            
    228            
    229 if __name__=="__main__": 
    230      
    231     import unittest 
    232      
    233     class TestCase(unittest.TestCase):   
    234          
    235         def setup(self): 
    236             self.ss='''<?xml version="1.0" encoding="UTF-8"?> 
    237                 <Dataset xmlns:swe="http://www.opengis.net/swe" xmlns:gml="http://www.opengis.net/gml" 
    238                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:moles="http://ndg.nerc.ac.uk/moles" 
    239                  xmlns:om="http://www.opengis.net/om" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://ndg.nerc.ac.uk/csml"  
    240                  id="FGPfF9i0"><CSMLFeatureCollection gml:id="AfEj15o6"/><om:blah>blahvalue</om:blah><foo>foovalue</foo></Dataset>''' 
    241                   
    242         def testns(self): 
    243             ''' Make sure we extract the namespaces correctly ''' 
    244             self.setup() 
    245             x=xmlHandler(self.ss,string=1) 
    246             self.assertEqual(x.realns,{'http://www.opengis.net/om':'om', 'http://www.opengis.net/gml':'gml',  
    247             'http://ndg.nerc.ac.uk/csml':'default', 'http://www.opengis.net/swe':'swe',  
    248             'http://www.w3.org/1999/xlink':'xlink',  
    249             'http://www.w3.org/2001/XMLSchema-instance':'xsi', 'http://ndg.nerc.ac.uk/moles':'moles'}) 
    250              
    251         def teststr(self): 
    252             ''' Make sure we can get a string version after loading ''' 
    253             self.setup() 
    254             x=xmlHandler(self.ss,string=1) 
    255             self.assertEqual('<?xml version="1.0" encoding="utf-8"><Dataset id="FGPfF9i0" xmlns="http://ndg.nerc.ac.uk/csml" xmlns:om="http://www.opengis.net/om" xmlns:gml="http://www.opengis.net/gml" xmlns:swe="http://www.opengis.net/swe" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:moles="http://ndg.nerc.ac.uk/moles"><CSMLFeatureCollection gml:id="AfEj15o6"  /><om:blah >blahvalue</om:blah><foo>foovalue</foo></Dataset>',str(x)) 
    256              
    257         def testorphans(self): 
    258             ''' Make sure we can handle orphan characters properly ''' 
    259             s='<data> 1<2</data>' 
    260             x=xmlHandler(s,string=1) 
    261             self.assertEqual('<data> 1&lt;2</data>',str(x)) 
    262  
    263         def testAmpsersand1(self): 
    264             ''' Can we load unescaped ampersands?''' 
    265             s='<data> a & b </data>' 
    266             x=xmlHandler(s,string=1) 
    267             self.assertEqual('<data> a &amp; b </data>',str(x)) 
    268              
    269         def testAmpersand2(self): 
    270             ''' Do we output proper things? ''' 
    271             s='<data> 2 &amp; 3 &lt; 8 </data>' 
    272             x=xmlHandler(s,string=1) 
    273             self.assertEqual('<data> 2 &amp; 3 &lt; 8 </data>',str(x)) 
    274              
    275         def testPrettyPrint(self): 
    276             ''' Test a simple pretty print ''' 
    277             s='<?xml version="1.0" encoding="utf-8"?><data><element>stuff</element></data>' 
    278             x=xmlHandler(s,string=1) 
    279             h=x.tohtml() # only testing the mechanics, not the result 
    280              
    281              
    282         #turn off the test 
    283         def AtestRealDIF(self): 
    284             ''' Test a real DIF from the ndgRetrieve stable ''' 
    285             f='ndgRetrieve.badc.nerc.ac.uk__DIF__dataent_11738019833217179.debug.xml' 
    286             x=xmlHandler(f) 
    287             y=str(x)  # only testing the mechanics, not the result 
    288              
    289              
    290         def testDIF(self): 
    291             s='''<DIF xmlns="http://gcmd.gsfc.nasa.gov/Aboutus/xml/dif/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Entry_ID>badc.nerc.ac.uk__DIF__dataent_11738019833217179</Entry_ID></DIF>''' 
    292             x=xmlHandler(s,string=1) 
    293             print x.realns 
    294             print str(x) 
    295             h=x.tohtml() 
    296             print h 
    297              
    298         def testExist(self): 
    299             s='''<exist:result xmlns:exist="http://exist.sourceforge.net/NS/exist" hits="4" start="1" count="4"> 
    300 <document>/db/ndg_B_metadata/badc.nerc.ac.uk/activity_activity_COAPEC.xml</document><document>/db/ndg_B_metadata/badc.nerc.ac.uk/activity_activity_coapec_1k.xml</document><document>/db/ndg_B_metadata/badc.nerc.ac.uk/activity_activity_cpdn.xml</document><document>/db/ndg_B_metadata/badc.nerc.ac.uk/dataent_COAPEC.xml</document> 
    301 </exist:result>''' 
    302             x=xmlHandler(s,string=1) 
    303             d=x.getText('Document',multiple=1) 
    304              
    305              
    306              
    307     unittest.main() 
    308              
    309              
     227                 
  • exist/trunk/python/ndgutilstests/lib/testexistdbclient.py

    r4663 r4696  
    2222    VALID_FILE_PATH = VALID_COLLECTION_PATH + VALID_FILE 
    2323    INVALID_PATH = 'blah' 
    24     DUMMY_COLLECTION = VALID_COLLECTION_PATH + 'blah' 
     24    DUMMY_COLLECTION = VALID_COLLECTION_PATH + INVALID_PATH 
    2525     
    2626    def setUp(self): 
     
    5050        self.assertNotEqual(file, self.VALID_FILE) 
    5151         
     52 
     53    def testRemoveTimeStamp(self): 
     54        file = self.utils.dbc._eXistDBClient__addTimeStamp(self.VALID_FILE) 
     55        file = self.utils.dbc._eXistDBClient__removeTimeStamp(file) 
     56        self.assertEqual(self.VALID_FILE, file) 
     57         
    5258    def testValidCheckAtomSchemaCompliance(self): 
    53         createdAtom = self.utils.createAtomInEXist(tc.xmlString) 
    5459        atom = self.utils.createAtomInEXist(tc.xmlString) 
    5560        atomPath = atom.getDefaultCollectionPath() + atom.atomName 
    5661        self.assertEqual([], self.utils.dbc.checkAtomSchemaCompliance(atomPath)) 
    57         self.utils.dbc.deleteAtomInExists(atom) 
     62        self.utils.dbc.deleteAtomInExist(atom) 
     63         
     64    def testInvalidCheckAtomSchemaCompliance(self): 
     65        atom = self.utils.createAtomInEXist(tc.invalidXmlString) 
     66        atomPath = atom.getDefaultCollectionPath() + atom.atomName 
     67        errors = self.utils.dbc.checkAtomSchemaCompliance(atomPath) 
     68        self.assertEqual(1, len(errors)) 
     69        self.assertTrue(errors[0].startswith(" Invalid content was found starting with element 'contributor'")) 
     70        self.utils.dbc.deleteAtomInExist(atom) 
     71         
     72    def testInvalidEmailCheckAtomSchemaCompliance(self): 
     73        atom = self.utils.createAtomInEXist(tc.invalidEmailXmlString) 
     74        atomPath = atom.getDefaultCollectionPath() + atom.atomName 
     75        errors = self.utils.dbc.checkAtomSchemaCompliance(atomPath) 
     76        self.assertEqual(2, len(errors)) 
     77        self.assertTrue(errors[1].find("of element 'email' is not valid") > -1) 
     78        self.utils.dbc.deleteAtomInExist(atom) 
    5879 
    5980    def testValidCheckAtomSchemaComplianceWithKeyword(self): 
     
    189210    def testBackupEXistFileMissing(self): 
    190211        self.assertRaises(SystemError, self.utils.dbc.backupEXistFile, self.VALID_COLLECTION_PATH, self.VALID_FILE) 
    191  
    192212             
    193213    def testGetAtomFileCollectionPath(self): 
     
    213233        self.assertNotEquals({}, ids) 
    214234 
    215      
     235    def testRestoreBackup(self): 
     236        atom = self.utils.createAtomInEXist(tc.xmlString) 
     237        atom = self.utils.createAtomInEXist(tc.xmlString) 
     238        backupPath = self.utils.dbc.backupName 
     239        self.utils.dbc.deleteAtomInExist(atom) 
     240        atomPath = atom.getDefaultCollectionPath() + atom.atomName 
     241        self.assertTrue(self.utils.dbc.isNewEXistFile(atomPath)) 
     242        self.utils.dbc.restoreBackup(backupPath) 
     243        self.assertFalse(self.utils.dbc.isNewEXistFile(atomPath)) 
     244        self.utils.dbc.deleteAtomInExist(atom) 
     245 
     246    def testInvalidRestoreBackup(self): 
     247        self.assertRaises(SystemError, self.utils.dbc.restoreBackup, self.DUMMY_COLLECTION) 
     248         
    216249    def tearDown(self): 
    217250        if self.tidyUp: 
  • exist/trunk/python/ndgutilstests/lib/testgranulite.py

    r4663 r4696  
    1717import ndgUtils.lib.existdbclient as dbc 
    1818from ndgUtils.models.vocabtermdata import VocabTermData as VTD 
     19import ndgUtils.lib.utilities as utilities 
    1920 
    2021class testGranulite(unittest.TestCase): 
     
    2526        ''' 
    2627        self.utils = tu(tc.DBCONFIG_FILE) 
    27         self.g = granulite(tc.TEST_GRANULITE, eXistClient = tu.dbc) 
     28        self.g = granulite(tc.TEST_GRANULITE, eXistClient = self.utils.dbc) 
    2829 
     30    def createGranuliteDEs(self): 
     31        ''' 
     32        Create the DEs referenced in the granulite file 
     33        ''' 
     34        inputs = self.g._granulite__getGranuliteDetails() 
     35        self.DEs = [] 
     36        for de in inputs['data_entity_id']: 
     37            data = utilities.getTripleData(de) 
     38            id = data[0] 
     39            atom = self.utils.createAtom(tc.xmlString) 
     40            atom.atomTypeID = VTD.DE_TERM 
     41            atom.setDatasetID('dataent_' + id) 
     42            atom = self.utils.dbc.createAtomInExist(atom) 
     43            self.DEs.append(atom) 
     44 
     45    def deleteGranuliteDEs(self): 
     46        ''' 
     47        Delete the DEs referenced in the granulite file 
     48        ''' 
     49        for de in self.DEs: 
     50            self.utils.dbc.deleteAtomInExist(de) 
     51             
     52         
     53         
    2954    def testProcessGranulite(self): 
    3055        try: 
     56            # firstly, create the DEs referenced in the granulite 
     57            self.createGranuliteDEs() 
    3158            atom = self.g.processGranulite() 
    3259            self.assertNotEqual(None, atom) 
     
    3562            self.assertEqual('-3.33984375', atom.maxX) 
    3663            self.assertEqual('-17.578125', atom.maxY) 
     64 
     65        except Exception, e: 
     66            import pdb 
     67            pdb.set_trace() 
     68            self.fail("An exception should not have been thrown") 
     69        finally: 
    3770            self.utils.dbc.deleteAtomInExist(atom) 
    38         except Exception, e: 
    39             self.fail("An exception should not have been thrown") 
     71            self.deleteGranuliteDEs() 
    4072         
    4173    def testProcessCSMLOrCDMLFile(self): 
  • exist/trunk/python/ndgutilstests/testconstants.py

    r4663 r4696  
    8484maxX = '360.0' 
    8585maxY = '5505.29980469' 
     86ABBREVIATION = 'BLAH' 
     87VALID_EMAIL = 'dt@bob.org' 
     88INVALID_EMAIL = 'dt.bob.org' 
     89INVALID_FILE = 'blah.txt' 
    8690 
    87 xmlString = '<entry xmlns:georss="http://www.georss.org/georss/10" xmlns:gml="http://www.opengis.net/gml" ' + \ 
     91xmlString = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss/10" xmlns:gml="http://www.opengis.net/gml" ' + \ 
    8892    'xmlns:moles="http://ndg.nerc.ac.uk/schema/moles2beta">' + \ 
    8993    '<id>' + id + '</id>' + \ 
     
    9397        '<link href="http://badc.nerc.ac.uk/alternate" rel="http://vocab.ndg.nerc.ac.uk/term/C110/6/GD" title="alternate page"/>' + \ 
    9498        '<link href="' + logo + '" title="' + VTD.TERM_DATA[VTD.LOGO_TERM].title + '" rel="http://vocab.ndg.nerc.ac.uk/term/C110/6/GD/Logo"/>' + \ 
    95         '<author><name>Tiddeman, David</name><uri>www.cb.org</uri></author>' + \ 
    96         '<contributor><name>Brian Bandy</name><uri>www.cb.org</uri></contributor>' + \ 
     99        '<author><name>Tiddeman, David</name><email>' + VALID_EMAIL + '</email></author>' + \ 
     100        '<contributor><name>Brian Bandy</name><email>bb@bb.org</email></contributor>' + \ 
    97101        '<moles:entity type="granule"><moles:molesISO><moles:responsibleParties>' + \ 
    98102            '<moles:responsibleParty><name>calum byrom</name><uri>www.cb.org</uri><role>Metadata maintainer</role></moles:responsibleParty>' + \ 
    99103            '<moles:responsibleParty><name>dom lowe</name><uri>www.badc.rl.ac.uk</uri><role>Metadata maintainer</role></moles:responsibleParty>' + \ 
    100104            '</moles:responsibleParties><moles:datasetLanguage>English</moles:datasetLanguage>' + \ 
     105            '<moles:datasetLanguage>English</moles:datasetLanguage>' + \ 
     106            '<moles:metadataLanguage>English</moles:metadataLanguage>' + \ 
     107            '<moles:abbreviation>' + ABBREVIATION + '</moles:abbreviation>' + \ 
    101108            '<moles:providerID>badc.nerc.ac.uk</moles:providerID>' + \ 
    102109            '<moles:metadataLanguage>English</moles:metadataLanguage>' + \ 
     
    114121    '</entry>' 
    115122 
     123invalidEmailXmlString = xmlString.replace(VALID_EMAIL, INVALID_EMAIL) 
     124 
    116125INVALID_TITLE = '\xa2' 
    117 invalidXmlString = '<entry xmlns:georss="http://www.georss.org/georss/10" xmlns:gml="http://www.opengis.net/gml" ' + \ 
     126invalidXmlString = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss/10" xmlns:gml="http://www.opengis.net/gml" ' + \ 
    118127    'xmlns:moles="http://ndg.nerc.ac.uk/schema/moles2beta">' + \ 
    119128    '<id>' + id + '</id>' + \ 
Note: See TracChangeset for help on using the changeset viewer.