source: mauRepo/MolesManager/trunk/cedaMoles/libs/migration/processor/commons.py @ 8522

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/mauRepo/MolesManager/trunk/cedaMoles/libs/migration/processor/commons.py@8522
Revision 8522, 39.2 KB checked in by mnagni, 8 years ago (diff)

Incomplete - # 22528: Migration of FAtCat Open Search link for HPFeld
 http://team.ceda.ac.uk/trac/ceda/ticket/22528
Incomplete - # 22534: Add versiojn number to the gui page
 http://team.ceda.ac.uk/trac/ceda/ticket/22534

Line 
1'''
2BSD Licence
3Copyright (c) 2012, Science & Technology Facilities Council (STFC)
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without modification,
7are permitted provided that the following conditions are met:
8
9    * Redistributions of source code must retain the above copyright notice,
10        this list of conditions and the following disclaimer.
11    * Redistributions in binary form must reproduce the above copyright notice,
12        this list of conditions and the following disclaimer in the documentation
13        and/or other materials provided with the distribution.
14    * Neither the name of the Science & Technology Facilities Council (STFC)
15        nor the names of its contributors may be used to endorse or promote
16        products derived from this software without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29Created on 15 Nov 2011
30
31@author: mnagni
32'''
33from ea_model.moles3_4.utilities.mo_responsiblepartyinfo import MO_ResponsiblePartyInfo
34from httplib import HTTPConnection
35from xml.etree.ElementTree import XML, tostring
36import time, datetime
37from cedaMoles.libs.migration.exception.exceptions import NoDataLineage
38from hashlib import md5
39from xml.sax.saxutils import unescape, escape
40import html5lib
41from html5lib import treebuilders
42from ea_model.ceda_metadatamodel.ceda_project.ceda_project import CEDA_Project
43from ea_model.ceda_metadatamodel.ceda_utilities.ceda_review import CEDA_Review   
44from ea_model.ceda_metadatamodel.ceda_acquisition.ceda_instrument import CEDA_Instrument
45from ea_model.ceda_metadatamodel.ceda_computation.ceda_processing import CEDA_Processing
46from ea_model.ceda_metadatamodel.ceda_result.ceda_result import CEDA_Result
47from ea_model.moles3_4.utilities.mo_individual import MO_Individual
48from ea_model.moles3_4.utilities.mo_organisation import MO_Organisation
49from ea_model.iso_19108_2006_temporal_schema.temporal_objects.tm_instant import TM_Instant
50from ea_model.iso_19108_2006_temporal_schema.temporal_objects.tm_period import TM_Period
51from ea_model.moles3_4.result.mo_onlineresource import MO_OnlineResource
52from ea_model.ceda_metadatamodel.ceda_acquisition.ceda_acquisition import CEDA_Acquisition
53
54from ea_model.iso_19103_2005_schema_language.basic_types.\
55    primitive.date_and_time.datetime import DateTime
56from ea_model.iso_19103_2005_schema_language.basic_types.\
57    primitive.date_and_time.date import Date
58from ea_model.iso_19108_2006_temporal_schema.temporal_reference_system.\
59    tm_position import TM_Position
60   
61from ea_model.iso_19115_2006_metadata_corrigendum.\
62    reference_system_information.md_identifier import MD_Identifier
63from ea_model.iso_19115_2006_metadata_corrigendum.\
64    identification_information.md_keywords import MD_Keywords   
65from ea_model.iso_19115_2006_metadata_corrigendum.\
66    citation_and_responsible_party_information.ci_address import CI_Address
67from ea_model.iso_19115_2006_metadata_corrigendum.\
68    citation_and_responsible_party_information.ci_onlineresource import CI_OnlineResource
69from ea_model.iso_19115_2006_metadata_corrigendum.\
70    citation_and_responsible_party_information.ci_telephone import CI_Telephone
71from ea_model.iso_19115_2006_metadata_corrigendum.\
72    citation_and_responsible_party_information.ci_contact import CI_Contact
73from ea_model.iso_19115_2006_metadata_corrigendum.\
74    citation_and_responsible_party_information.ci_date import CI_Date
75from ea_model.iso_19115_2006_metadata_corrigendum.\
76    citation_and_responsible_party_information.ci_citation import CI_Citation
77from ea_model.iso_19115_2006_metadata_corrigendum.\
78    constraint_information.md_constraints import MD_Constraints
79from ea_model.iso_19115_2006_metadata_corrigendum.\
80    constraint_information.md_legalconstraints import MD_LegalConstraints
81from ea_model.iso_19115_2006_metadata_corrigendum.\
82    citation_and_responsible_party_information.ci_responsibleparty \
83        import CI_ResponsibleParty
84from ea_model.iso_19115_2006_metadata_corrigendum.\
85    metadata_entity_set_information.md_metadata import MD_Metadata
86from ea_model.iso_19115_2006_metadata_corrigendum.\
87    data_quality_information.dq_conformanceresult import DQ_ConformanceResult
88from ea_model.iso_19115_2006_metadata_corrigendum.\
89    extent_information.ex_geographicboundingbox import EX_GeographicBoundingBox
90from ea_model.iso_19115_2006_metadata_corrigendum.\
91    data_quality_information.dq_element import DQ_Element
92   
93from ea_model.ceda_metadatamodel.ceda_observationprocess.\
94    ceda_compositeprocess import CEDA_CompositeProcess
95
96base = '/exist/rest/atoms'
97
98DS_pUBLISHED = 'published'
99DS_WORKING = 'working'
100DS_PUBLISHED = 'Published'
101DOC_STATUS = (DS_pUBLISHED, DS_WORKING, DS_PUBLISHED)
102
103DT_DEPLOYMENTS = 'deployments'
104DT_DATA_ENTITIES = 'data_entities'
105DT_DEPLOYMENT_DATA = 'deployment_data'
106DT_DATA_GRANULES = 'data_granules'
107DOC_TYPES = (DT_DEPLOYMENTS, DT_DATA_ENTITIES, DT_DEPLOYMENT_DATA, DT_DATA_GRANULES)
108
109DO_BADC = 'badc.nerc.ac.uk'
110DO_NEODC = 'neodc.nerc.ac.uk'
111DO_UKSSDC = 'ukssdc.ac.uk'
112
113CEDA = 'Centre for Environmental Data Archive'
114DOC_OWNERS = (DO_BADC, DO_NEODC, DO_UKSSDC)
115
116ATOM_NS = "{http://www.w3.org/2005/Atom}"
117EXIST_NS = "{http://exist.sourceforge.net/NS/exist}"
118MOLES_NS = "{http://ndg.nerc.ac.uk/schema/moles2beta}"
119#htmlNS = "{http://www.w3.org/1999/xhtml}"
120GEORSS_NS = "{http://www.georss.org/georss/10}"
121GML_NS = "{http://www.opengis.net/gml}"
122DATE_FORMAT = '%Y-%m-%d'
123DATETIME_FORMAT = DATE_FORMAT + 'T%H:%M:%SZ'
124HOST = 'bora.badc.rl.ac.uk'
125PORT = '8080'
126
127LINK_MARKERS = ['Deployment', 'DOWNLOAD', 'DOCUMENTATION', 'ACCESS', 'LOGO', 'ACTIVITY', \
128               'DPT', 'OBS', 'TOOLS']
129
130#MD_Identifier codes
131MD_CODE_MOLES2_CITATION = 'ceda_moles2_citation'
132
133HTML_PARSER = html5lib.HTMLParser(tree=treebuilders.getTreeBuilder("etree"), \
134                                 namespaceHTMLElements=False)
135
136def calculate_hash(text):
137    """
138        Returns an md5 hexadecimal representation of the given text
139        @param text: a string or an array of strings to encode
140        @return: the hexadecimal md5 value of the given text
141    """
142    encoder = md5()
143    messages = []
144    if not isinstance(text, list):
145        messages.append(text)
146    else:
147        messages = text
148       
149    for item in messages:
150        encoder.update(item)
151
152    return encoder.hexdigest()
153
154def build_exist_doc_path(doc_status, doc_type, doc_owner, doc_name):
155    '''
156        @param doc_status: one value from commons.docStatus
157        @param doc_type: one value from commons.docTypes
158        @param doc_owner: one value from commons.DOC_OWNERS
159        @param doc_name: one value from commons.DOC_OWNERS       
160    '''       
161    return '%s/%s' % (_build_exist_owner_path(doc_status, doc_type, doc_owner), doc_name)
162
163def _build_exist_owner_path(doc_status, doc_type, doc_owner):
164    '''
165        @param doc_status: one value from commons.docStatus
166        @param doc_type: one value from commons.docCollections
167        @param doc_owner: one value from commons.DOC_OWNERS
168    '''       
169    return '%s/%s' % (build_exist_type_path(doc_status, doc_type), doc_owner)
170
171def build_exist_type_path(doc_status, doc_type):
172    '''
173        @param doc_status: one value from commons.docStatus
174        @param doc_type: one value from commons.docCollections
175    '''       
176    return '%s/%s' % ('/exist/rest/atoms/%s' % (doc_status), doc_type)
177
178def _get_atom_document_by_mo(migration_object):
179    if migration_object is None:
180        raise Exception("migrationObject is None")
181    mo_type_dict = {'DeploymentsMigration': DT_DEPLOYMENTS, \
182                   'DataEntityMigration': DT_DATA_ENTITIES, \
183                   'DeploymentDataMigration': DT_DEPLOYMENT_DATA}     
184    return get_atom_document_as_elementtree(migration_object.doc_status, \
185                                    mo_type_dict[type(migration_object).__name__], \
186                                    migration_object.doc_owner, migration_object.doc_name)
187
188def get_atom_document_hash_by_mo(migration_object):
189    if migration_object is None:
190        raise Exception("migrationObject is None")   
191    mo_type_dict = {'DeploymentsMigration': DT_DEPLOYMENTS, \
192                   'DataEntityMigration': DT_DATA_ENTITIES, \
193                   'DeploymentDataMigration': DT_DEPLOYMENT_DATA}     
194    text = _get_atom_document_as_text(migration_object.doc_status, \
195                                  mo_type_dict[type(migration_object).__name__],\
196                                   migration_object.doc_owner, migration_object.doc_name)
197    return calculate_hash(text)
198
199def has_mo_been_processed(migration_object):
200    '''
201        Checks if a migration object has been already processed.
202        @param migrationObject: an instance of DataEntityMigration or DeploymentsMigration
203        @return True if has been processed, otherwise False
204    '''
205    if migration_object is None:
206        return False
207   
208    if not hasattr(migration_object, 'doc_hash'):
209        return False
210    else:
211        if migration_object.doc_hash is None:
212            return False
213       
214    return True
215
216def has_mo_same_hash(migration_object):
217    '''
218        Checks if a migration object has changed.
219        @param migrationObject: an instance of DataEntityMigration or
220            DeploymentsMigration or DeploymentDataMigration
221        @return True if the hash of the actual document is the
222            same of the migrationObject, otherwise False
223    '''
224    if not has_mo_been_processed(migration_object):
225        return False
226    return get_atom_document_hash_by_mo(migration_object) == migration_object.doc_hash
227
228def _get_atom_document_as_text(doc_status, doc_type, doc_owner, doc_name):
229    source = build_exist_doc_path(doc_status, doc_type, doc_owner, doc_name)
230    return _get_document(source)
231
232def get_atom_document_as_elementtree(doc_status, doc_type, doc_owner, doc_name):
233    source = build_exist_doc_path(doc_status, doc_type, doc_owner, doc_name)
234    return _get_xml_document(source)
235
236def _get_xml_document(source):
237    return XML(_get_document(source))
238
239def stringToTimestamp(timestring):
240    '''
241        Return a timestamp such as is returned by time.time().
242        @param timestring: a time string formatted as '%Y-%m-%dT%H:%M:%SZ'
243    '''   
244    return datetime.datetime.fromtimestamp(\
245                                time.mktime(time.strptime(timestring, DATETIME_FORMAT)))
246
247def isoDateStringToTimeDate(datestring):
248    '''
249        Return a datatime.datatime instance.
250        @param datestring: a date string formatted as '%Y-%m-%d'
251    '''
252    return datetime.datetime.strptime(datestring, DATE_FORMAT)
253
254def isoDateTimeStringToTimeDate(timestring):
255    '''
256        Return a datatime.datatime instance.
257        @param timestring: a time string formatted as '%Y-%m-%dT%H:%M:%SZ'
258    '''
259    try:
260        return datetime.datetime.strptime(timestring, DATETIME_FORMAT)
261    except:
262        pass
263
264def _get_document(source):
265    conn = HTTPConnection(host = HOST, port = PORT)
266    conn.connect()
267    conn.request('GET', source)
268    res = conn.getresponse()
269    xml_doc = res.read()
270    conn.close()
271    return xml_doc
272
273def _return_not_none_text(element):
274    if element is None:
275        return None
276    return element.text
277
278def find_moles_creation_date(resource_xml):
279    creation_date = resource_xml.find('%sentity/%smolesISO/%screated' \
280                                    % (MOLES_NS, MOLES_NS, MOLES_NS))
281    return _return_not_none_text(creation_date)
282
283def _find_moles_published_date(resource_xml):
284    creation_date = resource_xml.find('%sentity/%smolesISO/%spublished' \
285                                    % (MOLES_NS, MOLES_NS, MOLES_NS))
286    return _return_not_none_text(creation_date)
287
288def find_moles_lineage(data_entity_migration):
289    resource_xml = _get_atom_document_by_mo(data_entity_migration)
290    lineage = resource_xml.find('%sentity/%smolesISO/%slineage' \
291                               % (MOLES_NS, MOLES_NS, MOLES_NS))
292    if lineage is None:
293        raise NoDataLineage(data_entity_migration)
294    return lineage.text
295
296def extract_moles_provider_id(migration_object):
297    resource_xml = _get_atom_document_by_mo(migration_object)
298    provider_id = resource_xml.find('%sentity/%smolesISO/%sproviderID' \
299                                   % (MOLES_NS, MOLES_NS, MOLES_NS))
300    return _return_not_none_text(provider_id)
301
302def extract_moles_temporal_range(migration_object):
303    resource_xml = _get_atom_document_by_mo(migration_object)
304    temporal_range = resource_xml.find('%stemporalRange' % (MOLES_NS))
305    return _return_not_none_text(temporal_range)
306
307def extract_moles_creation_date(migration_object):
308    resource_xml = _get_atom_document_by_mo(migration_object)
309    return find_moles_creation_date(resource_xml)
310
311def extract_moles_published_date(migration_object):
312    resource_xml = _get_atom_document_by_mo(migration_object)
313    return _find_moles_published_date(resource_xml)
314
315def extract_quality(data_entity_migration):
316    resource_xml = _get_atom_document_by_mo(data_entity_migration)
317    quality = resource_xml.find('%sentity/%smolesISO/%squality' \
318                               % (MOLES_NS, MOLES_NS, MOLES_NS))
319    return _return_not_none_text(quality)
320
321def extract_update_frequency(data_entity_migration):
322    resource_xml = _get_atom_document_by_mo(data_entity_migration)
323    update_frequency = resource_xml.find('%sentity/%smolesISO/%supdateFrequency' \
324                                        % (MOLES_NS, MOLES_NS, MOLES_NS))
325    return _return_not_none_text(update_frequency)
326
327def extract_content(data_entity_migration):
328    """
329        Returns a dictionary containing the div composing the
330        <content> element in a dataentity document.
331    """
332    resource_xml = _get_atom_document_by_mo(data_entity_migration)
333    content = resource_xml.find('%scontent' % (ATOM_NS))
334    text = _return_not_none_text(content)
335    contentDict = {}
336    if text:
337        doc = HTML_PARSER.parse(unescape(text))
338        for el in doc.findall('body//div'):   
339            prop = el.get('property')
340            if prop:
341                if prop.startswith('cedacat'):
342                    contentDict[prop.split(':')[1]] = escape(tostring(el))
343    return contentDict
344
345def find_authors_in_resource(resource_migration):
346    '''
347        Returns a dictionary with the following keys:
348        'authors': a list of string representing the authors
349        'contributors': a list of string representing the contributors
350    '''
351    ret = {}   
352    resource_xml = _get_atom_document_by_mo(resource_migration)
353    ret['authors'] = _find_author_in_resource(resource_xml)
354    ret['contributors'] = _find_contributor_in_resource(resource_xml)
355    return ret
356
357def _find_author_in_resource(resource_xml): 
358    author = resource_xml.find('%sauthor/%sname' % (ATOM_NS, ATOM_NS))
359    return _return_not_none_text(author)
360
361def _find_contributor_in_resource(resource_xml): 
362    contributors = resource_xml.find('%scontributor/%sname' % (ATOM_NS, ATOM_NS))
363    return _return_not_none_text(contributors)
364
365def find_published_date(resource_migration):
366    resource_xml = _get_atom_document_by_mo(resource_migration)
367    publishedDate = resource_xml.find('%spublished' % (ATOM_NS))
368    return _return_not_none_text(publishedDate)
369
370def find_updated_date(resource_migration):
371    resource_xml = _get_atom_document_by_mo(resource_migration)
372    publishedDate = resource_xml.find('%supdated' % (ATOM_NS))
373    return _return_not_none_text(publishedDate)
374
375def find_summary(resource_migration):
376    """
377        Returns the <entry><summary> tag of an atom document
378        @param resourceMigration: a MigrationObject instance
379        @return: the <summary> value or None if empty
380    """
381    resource_xml = _get_atom_document_by_mo(resource_migration)
382    summary = resource_xml.find('%ssummary' % (ATOM_NS))
383    return _return_not_none_text(summary)
384
385def find_id(data_ent_xml):
386    ent_id = data_ent_xml.find('%sid' % (ATOM_NS))
387    return _return_not_none_text(ent_id)
388
389def _update_links_dict(links_dict, link, link_marker):
390    if not links_dict.has_key(link_marker):
391        links_dict[link_marker] = []
392    rel = link.get('rel')
393    if rel and rel.endswith('/' + link_marker):
394        links_dict[link_marker].append({'href': link.get('href'), \
395                                      'title': link.get('title')}) 
396
397def _extract_links(data_ent_xml, markers):
398    links_dict = {}
399    links = data_ent_xml.findall('%slink' % (ATOM_NS))
400    for link in links:
401        for marker in markers:
402            _update_links_dict(links_dict, link, marker)       
403    return links_dict
404
405def find_links_in_migration_document(data_entity_migration, key = None):
406    data_ent_XML = _get_atom_document_by_mo(data_entity_migration)
407    links_dict = _extract_links(data_ent_XML, LINK_MARKERS)
408    if key is None:
409        return links_dict
410   
411    if links_dict.has_key(key):
412        return links_dict[key]   
413    return {}       
414   
415
416def find_download_links_in_migration_document(migration_object):
417    """
418        Return a list of dictionaries describing a <link rel="...DOWNLOAD..."> tag type
419        Each dictionary has two keys: 'href' and 'title'
420        @param migrationObject: the migration instance to retrieve and parse
421        @return: a list of dictionaries
422    """
423    return find_links_in_migration_document(migration_object, 'DOWNLOAD')
424
425def find_tools_links_in_migration_document(migration_object):
426    """
427        Return a list of dictionaries describing a <link rel="...TOOLS..."> tag type
428        Each dictionary has two keys: 'href' and 'title'
429        @param migrationObject: the migration instance to retrieve and parse
430        @return: a list of dictionaries
431    """
432    return find_links_in_migration_document(migration_object, 'TOOLS')
433
434def find_access_links_in_migration_document(migration_object):
435    """
436        Return a list of dictionaries describing a <link rel="...ACCESS..."> tag type
437        Each dictionary has two keys: 'href' and 'title'
438        @param migrationObject: the migration instance to retrieve and parse
439        @return: a list of dictionaries
440    """
441    return find_links_in_migration_document(migration_object, 'ACCESS')
442
443
444def find_documentation_in_migration_document(migration_object):
445    """
446        Return a list of dictionaries describing a <link rel="...DOCUMENTATION...">
447        tag type
448        Each dictionary has two keys: 'href' and 'title'
449        @param migrationObject: the migration instance to retrieve and parse
450        @return: a list of dictionaries
451    """
452    return find_links_in_migration_document(migration_object, 'DOCUMENTATION')
453
454def find_doi_in_migration_document(migration_object):
455    """
456        Return a dictionary describing a <link rel="...DOCUMENTATION..."> tag type
457        The dictionary has two keys: 'href' and 'title'
458        @param migrationObject: the migration instance to retrieve and parse
459        @return: a dictionary relative to the DOI, None otherwise
460    """
461    for link in find_documentation_in_migration_document(migration_object):
462        if link['href'].startswith('http://dx.doi.org/doi:'):
463            return link
464    return None
465
466def find_deployments_in_de(data_entity_migration):
467    links_dict = find_links_in_migration_document(data_entity_migration)
468    links = _extract_links_by_marker(links_dict, 'Deployment')
469    return [depName + '.atom' for depName in links]
470
471def find_subtype_in_dpt(resourceMigration):
472    resource_xml = _get_atom_document_by_mo(resourceMigration)
473    categories = resource_xml.findall('%scategory' % (ATOM_NS))
474    for category in categories:
475        if category.get("term") == "ATOM_SUBTYPE":
476            return category.get("label")   
477       
478def extract_title(deploymentMigration):
479    resource_xml = _get_atom_document_by_mo(deploymentMigration)
480    title = resource_xml.find('%stitle' % (ATOM_NS))
481    return _return_not_none_text(title)
482
483def extract_summary(migrationObject):
484    resource_xml = _get_atom_document_by_mo(migrationObject)
485    summary = resource_xml.find('%ssummary' % (ATOM_NS))
486    return _return_not_none_text(summary)
487
488def extract_ge_in_migration_doc(migration_object):
489    """
490        Extracts if existing the georss:where/gel:Enveloper/upper-lowerCorner elements.
491        @param migrationObject: a migration object to retrieve to parse for data
492        @return: None if no data are found, otherwise a dictionary with keys: 'east',
493        'north', 'west', 'south' where
494        the values are float
495    """
496    resource_xml = _get_atom_document_by_mo(migration_object)
497    upper_corner = resource_xml.find('%swhere/%sEnvelope/%supperCorner' \
498                                   % (GEORSS_NS, GML_NS, GML_NS))
499    lower_corner = resource_xml.find('%swhere/%sEnvelope/%slowerCorner' \
500                                   % (GEORSS_NS, GML_NS, GML_NS))
501    ret = None
502    if upper_corner != None and lower_corner != None:
503        upper_corner_data = upper_corner.text.split()
504        lower_corner_data = lower_corner.text.split()
505        ret = {'east': float(upper_corner_data[0]), 'north': float(upper_corner_data[1]), \
506               'west': float(lower_corner_data[0]), 'south': float(lower_corner_data[1])}
507    return ret
508
509def find_links_in_deployment(migration_object):
510    """
511        Returns a dictionary of links owned by the given dataEntity document
512        @param deploymentMigration: a MigrationObject instance
513        @return: a dictionary of links.
514    """
515    links = {}
516    links_dict = find_links_in_migration_document(migration_object)
517    for marker in LINK_MARKERS:   
518        links[marker] = _extract_links_by_marker(links_dict, marker)
519    return links
520
521def _extract_links_by_marker(links_dict, marker):
522    dpt = []
523    if links_dict.has_key(marker):
524        for link in links_dict[marker]:
525            try:
526                linkLongName = link['href'].split('/')[-1]
527                if '__ATOM__' in linkLongName:
528                    linkName = linkLongName.rsplit('__ATOM__')[1]
529                else:
530                    linkName = linkLongName
531                dpt.append(linkName)
532            except Exception as ex:
533                print "WARN - Cannot extractLinksByMarker %s" % (link)
534    return dpt
535
536def get_owner_refs(doc_status, doc_type, doc_owner):
537    '''
538        Returns a list of Elements representing the inner resource reference items
539        @param resourceRefs: the name of the eXist collection name below the
540        'deployments' one
541    '''     
542    xml_depl = _get_xml_document(_build_exist_owner_path(doc_status, doc_type, doc_owner))
543    return xml_depl.findall('%scollection/%sresource' % (EXIST_NS, EXIST_NS))
544
545def get_collection_refs(published_refs):
546    '''
547        Returns a list of Elements representing the inner deployment reference items
548        @param basePublished: the name of the eXist collection name below the
549        'published' one
550    ''' 
551    xml_publ = _get_xml_document(published_refs)
552    return xml_publ.findall('%scollection/%scollection' % (EXIST_NS, EXIST_NS))
553
554def create_md_keywords(keywords, k_type=None, thesaurusName=None):
555    '''
556        Creates a new MD_Keywords instance.
557        @param keywords: a String array       
558    '''   
559    md_keywords = MD_Keywords()
560    md_keywords.keyword.extend(keywords)
561    if k_type:
562        md_keywords.type = k_type
563    if thesaurusName:
564        md_keywords.thesaurusName = thesaurusName       
565    return md_keywords
566   
567
568def create_datetime(datetime):
569    '''
570        Creates a new DateTime instance.
571        @param datetime: a datetime.datetime instance       
572    '''
573    if datetime is None:
574        return
575    dateTime = DateTime()
576    dateTime.century = (datetime.year / 100) + 1
577    dateTime.year = datetime.year
578    dateTime.month = datetime.month       
579    dateTime.day = datetime.day
580    dateTime.hour = datetime.hour
581    dateTime.minute = datetime.minute
582    dateTime.second = datetime.second
583    dateTime.timeZone = datetime.tzinfo       
584    return dateTime       
585       
586def create_date(date):
587    '''
588        Creates a new Date instance.
589        @param date: a datetime.datetime instance
590    '''
591    idate = Date()
592    idate.century = (date.year / 100) + 1
593    idate.year = date.year
594    idate.month = date.month       
595    idate.day = date.day       
596    return idate       
597       
598def create_tm_position(anyOther = None, \
599                      date8601 = None, \
600                      dateTime8601 = None, \
601                      time8601 = None):
602    '''
603        Creates a new TM_Position instance
604        @param anyOther: a TM_TemporalPosition instance
605        @param date8601: a Date instance
606        @param dateTime8601:a DateTime instance
607        @param time8601: a Time instance   
608    '''
609    tm_position = TM_Position()
610    if anyOther:
611        tm_position.anyOther = anyOther
612    if date8601:
613        tm_position.date8601 = date8601 
614    if dateTime8601:
615        tm_position.dateTime8601 = dateTime8601
616    if time8601:
617        tm_position.time8601 = time8601
618    return tm_position           
619
620def create_tm_instant(position):
621    '''
622        Creates a new TM_Position instance
623        @param position: a TM_Position instance
624    '''
625    tm_instant = TM_Instant()
626    tm_instant.position = position
627    return tm_instant
628
629def create_tm_period(begin, end):
630    '''
631        Creates a new TM_Position instance
632        @param position: a TM_Position instance
633    '''
634    tm_period = TM_Period()
635    tm_period.begin = begin
636    tm_period.end = end   
637    return tm_period
638
639def create_ci_address(deliveryPoint = None, electronicMailAddress = None, \
640                     city = None, country = None, postalCode = None):
641    '''
642        Creates a new CI_Address instance
643        @param deliveryPoint: an array of Strings
644        @param electronicMailAddress: an array of Strings
645        @param city: a string
646        @param country: a string               
647        @param postalCode: a string       
648    '''
649    ci_address = CI_Address()
650    if deliveryPoint:
651        ci_address.deliveryPoint = deliveryPoint
652    if electronicMailAddress:
653        ci_address.electronicMailAddress = electronicMailAddress       
654    if postalCode:
655        ci_address.postalCode = postalCode       
656    if city:
657        ci_address.city = city       
658    if country:
659        ci_address.country = country       
660    if postalCode:
661        ci_address.postalCode = postalCode       
662    return ci_address
663
664def create_ci_onlineresource(linkage, name=None):
665    '''
666        Creates a new CI_OnlineResource instance
667        @param linkage: a string (the URL class is associated with a String)
668        @param name: a String
669    '''
670    ci_online_resource = CI_OnlineResource()
671    ci_online_resource.linkage = linkage
672    if name:
673        ci_online_resource.name = name
674    return ci_online_resource
675
676def create_ci_telephone(voice=None, facsimile=None):
677    '''
678        Creates a new CI_Telephone instance
679        @param voice: an array of strings       
680        @param facsimile: an array of strings
681    '''
682    ci_telephone = CI_Telephone()
683    if voice:
684        ci_telephone.voice = voice
685    if facsimile:
686        ci_telephone.facsimile = voice       
687    return ci_telephone
688
689def create_ci_contact(phone, address = None, onlineResource = None):
690    '''
691        Creates a new CI_Contact instance
692        @param phone: a CI_Telephone instance       
693        @param address: a CI_Address instance
694        @param onlineResource: a CI_OnlineResource instance
695    '''
696    ci_contact = CI_Contact()
697    ci_contact.phone = phone
698    if address:
699        ci_contact.address = address
700    if onlineResource:
701        ci_contact.onlineResource = onlineResource       
702    return ci_contact
703
704def create_mo_individual(name = None, contactInfo = None):
705    '''
706        Creates a new MO_Individual instance
707        @param name: a String
708        @param contactInfo: an array of CI_Contact       
709    '''   
710    ci_party = MO_Individual()
711    if name:
712        ci_party.name = name
713    if contactInfo:
714        ci_party.contactInfo = contactInfo       
715    return ci_party
716
717def create_mo_organization(name = None, contactInfo = None):
718    '''
719        Creates a new MO_Organization instance.
720        @param name: a String
721        @param contactInfo: an array of CI_Contact       
722    '''       
723    ci_party = MO_Organisation()
724    if name:
725        ci_party.name = name
726    if contactInfo:
727        ci_party.contactInfo.extend(contactInfo)         
728    return ci_party
729
730def create_mo_responsible_party_info(role, i_party):
731    """
732        @param role: a CI_RoleCode/MO_RoleValue assigned to this ResponsibleParty
733        @param party: a list of MO_Organization/CI_Individual instances
734    """
735    mo_responsableInfo = MO_ResponsiblePartyInfo()
736    mo_responsableInfo.role = role
737    mo_responsableInfo.party.extend(i_party)
738    return mo_responsableInfo
739
740
741def create_ci_date(dateType, date = None):
742    """
743        Creates a new CI_Date
744        @param dateType: a CI_DateTypeCode value
745        @param date: a DateTime instance
746    """
747    ci_date = CI_Date()
748    ci_date.dateType = dateType
749    if date:
750        ci_date.date = date
751    return ci_date
752
753def create_ci_citation(title, date = None, icitedResponsibleParty = None):
754    """
755        Creates a new CI_Citation
756        @param title: the CI_Citation title
757        @param date: an array of CI_Date instances
758        @param icitedResponsibleParty: a list of CI_ResponsibleParty instances
759    """   
760    ci_citation = CI_Citation()
761    ci_citation.title = title
762    if date is not None:
763        if isinstance(date, list):
764            ci_citation.date.extend(date)
765        else:
766            ci_citation.date.append(date)
767    if icitedResponsibleParty:
768        ci_citation.citedResponsibleParty.extend(icitedResponsibleParty)
769    return ci_citation
770
771def create_md_constraints(useLimitation = None):
772    """
773        Creates a new MD_Constrains
774        @param useLimitation: a string array
775    """ 
776    md_constraints = MD_Constraints()
777    if useLimitation and type(useLimitation) == list:
778        md_constraints.useLimitation = useLimitation
779    return md_constraints
780
781def create_md_legal_constraints(useLimitation = None, accessConstrains = None):
782    """
783        Creates a new MD_LegalConstrains
784        @param useLimitation: a string array
785        @param accessConstrains: an MD_RestrictionCode array
786    """ 
787    md_legalconstraints = MD_LegalConstraints()
788    if useLimitation and type(useLimitation) == list:
789        md_legalconstraints.useLimitation = useLimitation
790    if accessConstrains and type(accessConstrains) == list:
791        md_legalconstraints.accessConstrains = accessConstrains       
792    return md_legalconstraints
793
794def create_md_identifier(code, authority = None):
795    """
796        Creates a new MD_Identifier
797        @param code: a String
798        @param authority: a CI_Citation instance
799    """
800    md_identifier = MD_Identifier()
801    if code == None:
802        raise Exception('NoNullableElement')
803    md_identifier.code = code
804    if authority:
805        md_identifier.authority = authority
806    return md_identifier
807
808def create_ci_responsible_party(role, organizationName = None, individualName = None):
809    """
810        Creates a new CI_ResponsibeParty
811        @param role: a CI_RoleCode
812    """
813    ci_responsible_party = CI_ResponsibleParty()
814    ci_responsible_party.role = role
815    if organizationName:
816        ci_responsible_party.organisationName = organizationName
817    if individualName:
818        ci_responsible_party.individualName = individualName   
819    return ci_responsible_party
820
821def create_md_metadata(date_stamp, contact, language = None):
822    """
823        Creates a new MD_Metadata
824        @param date_stamp: a Date instance
825        @param contacts: a CI_ResponsibleParty array instances
826        @param language: a string
827    """   
828    md_metadata = MD_Metadata()
829    md_metadata.dateStamp = date_stamp
830    for item in contact:
831        md_metadata.contact.append(item)
832    if language:
833        md_metadata.language = language   
834    return md_metadata
835
836def create_mo_online_resource(linkage, instance = None, name = None, function = None, \
837                            description = None, applicationProfile = None):
838    """
839        Creates a new CEDA_Result
840        @param linkage: the MO_OnlineResource.linkage.url.??? field
841        @param name: the MO_OnlineResource.linkage.name field
842        @param function: the MO_OnlineResource.function field
843        @param description: the MO_OnlineResource.description field
844        @param applicationProfile: the MO_OnlineResource.applicationProfile field
845    """
846    if instance is None:
847        on_line_resource = MO_OnlineResource()
848         
849    on_line_resource.linkage = linkage
850    if name and on_line_resource.name != name:         
851        on_line_resource.name = name
852    if function and on_line_resource.function != function:
853        on_line_resource.function = function
854    if description and on_line_resource.description != description:
855        on_line_resource.description = description
856    if applicationProfile and on_line_resource.applicationProfile != applicationProfile:
857        on_line_resource.applicationProfile = applicationProfile
858    return on_line_resource       
859
860def create_ceda_result(curation_category, internal_path, source = None):
861    """
862        Creates a new CEDA_Result
863        @param curation_category: a CEDA_CurationValue instance
864        @param internal_path: a String
865        @param source: an array of MO_OnlineResource instances
866    """
867    ceda_result = CEDA_Result()
868    ceda_result.curationCategory = curation_category
869    ceda_result.internalPath = internal_path           
870    if source: 
871        ceda_result.source = source
872    return ceda_result
873
874
875
876def create_dq_conformance_result(explanation, pass_, specification):
877    """
878        Creates a DQ_ConformanceResult instance
879        @param explanation: a String
880        @param pass_: a boolean value
881        @param specification: a CI_Citation instance 
882    """
883    dq_conformanceResult = DQ_ConformanceResult()
884    dq_conformanceResult.explanation = explanation
885    dq_conformanceResult.pass_ = pass_
886    dq_conformanceResult.specification = specification
887    return dq_conformanceResult
888
889def create_dq_element(result):
890    """
891        Creates a DQ_Element instance
892        @param result: a DQ_Result array (mix 1, max 2 items)
893    """
894    dq_element = DQ_Element()
895    if result and (type(result) == list) and (len(result) >= 1 and len(result) <= 2):
896        dq_element.result = result
897    return dq_element
898
899def create_ex_geographic_boundingbox(east, north, west, south):
900    """
901        Creates an EX_GeographicBoundingBox instance
902        @param east: the eastBoundLongitude attribute as float
903        @param north: the northBoundLongitude attribute as float
904        @param west: the westBoundLongitude attribute as float
905        @param south: the southBoundLongitude attribute as float               
906    """
907    ex_geographic_bb = EX_GeographicBoundingBox()
908    ex_geographic_bb.eastBoundLongitude = east
909    ex_geographic_bb.northBoundLatitude = north
910    ex_geographic_bb.westBoundLongitude = west
911    ex_geographic_bb.southBoundLatitude = south
912    return ex_geographic_bb
913   
914def create_ceda_processing():
915    ceda_processing = CEDA_Processing()
916    return ceda_processing
917
918
919def create_ceda_instrument():
920    ceda_instrument = CEDA_Instrument()
921    return ceda_instrument
922
923def create_ceda_composite_process():
924    ceda_cp = CEDA_CompositeProcess()
925    return ceda_cp
926
927def create_ceda_acquisition():
928    ceda_acquisition = CEDA_Acquisition()
929    return ceda_acquisition
930
931def create_ceda_review(reviewer, reviewFrequency, reviewStatus):
932    """
933        Returns a new CEDA_Review
934        @param reviewer: an MO_ResponsibilityPartyInfo
935        @param reviewFrequency: a CEDA_ReviewFrequencyValue
936        @param reviewStatus: a CEDA_ReviewStatusValue
937    """
938    ceda_review = CEDA_Review()
939    ceda_review.reviewer = reviewer
940    ceda_review.reviewFrequency = reviewFrequency
941    ceda_review.reviewStatus = reviewStatus           
942    return ceda_review
943
944def create_ceda_project(abstract = None, publication_state = None, \
945                       documentation = None, project_resource=None):
946    ceda_project = CEDA_Project()
947    if abstract:
948        ceda_project.abstract = abstract
949    if publication_state:
950        ceda_project.publicationState = publication_state       
951    if documentation and type(documentation) == list:
952        ceda_project.documentation = documentation
953    if project_resource and type(project_resource) == list:
954        ceda_project.projectResource = project_resource   
955    return ceda_project
956
957def from_date_string_to_pt(doc_phenomenon_time):
958    """
959        Transforms a date string like '2002-07-22' (startDate) in a TM_Instant instance or   
960        '2002-07-22/2011-08-06' (start/endDate) in a TM_Period instance
961        @param doc_phenomenon_time: a date string in the expected format
962    """
963    if doc_phenomenon_time:
964        pt = None
965        if '/' in doc_phenomenon_time:
966            period = doc_phenomenon_time.split('/')
967            begin_date = create_date(isoDateStringToTimeDate(period[0]))
968            begin_position = create_tm_position(date8601 = begin_date)
969            begin_tm_instant = create_tm_instant(begin_position)
970           
971            end_date = create_date(isoDateStringToTimeDate(period[1]))
972            end_position = create_tm_position(date8601 = end_date)
973            end_tm_instant = create_tm_instant(end_position)
974           
975            pt = create_tm_period(begin_tm_instant, end_tm_instant)
976        else:
977            tm_position = create_tm_position(date8601 = \
978                                            create_date(isoDateStringToTimeDate(doc_phenomenon_time)))
979            pt = create_tm_instant(tm_position)
980        return pt
981
982def from_pt_to_string(phenomenonTime):
983    """
984        Transforms a TM_Instant instance in a date string like '2002-07-22' (startDate) or   
985        a TM_Period instance in a string like '2002-07-22/2011-08-06' (start/endDate)
986        @param phenomenonTime: a aTM_Instace or a TM_Period instance
987        @return a pair startDate, endDate. If endDate does not exists return startDate,
988        None
989    """   
990    if phenomenonTime is None:
991        return None
992    startDate =None
993    endDate = None
994    if isinstance(phenomenonTime, TM_Instant):
995        startDate = _tm_InstantToString(phenomenonTime)
996    elif isinstance(phenomenonTime, TM_Period):
997        startDate = _tm_InstantToString(phenomenonTime.begin)       
998        endDate = _tm_InstantToString(phenomenonTime.end)
999    return startDate, endDate
1000
1001def compare_phenomenon_times(p1, p2):
1002    s1 = from_pt_to_string(p1)
1003    s2 = from_pt_to_string(p2)
1004    return s1[0] == s2[0] and s1[1] == s2[1] 
1005
1006def _tm_InstantToString(tm_instant):
1007    idate = tm_instant.position.date8601
1008    return str(datetime.date(int(idate.year), int(idate.month), int(idate.day)))
1009
1010def _from_geographic_bb_to_string(gbb):
1011    if gbb is None:
1012        return None
1013    return '{0} {1},{2} {3}'.format(gbb.eastBoundLongitude, gbb.northBoundLatitude, \
1014                                    gbb.westBoundLongitude, gbb.southBoundLatitude)
1015   
1016def compareGeographicBoundingBoxes(gb1, gb2):
1017    return _from_geographic_bb_to_string(gb1) == _from_geographic_bb_to_string(gb2)
Note: See TracBrowser for help on using the repository browser.