Project

General

Profile

1
#!/usr/bin/env python
2
# Maps one datasource to another, using a map spreadsheet if needed
3
# For outputting an XML file to a PostgreSQL database, use the general format of
4
# http://vegbank.org/vegdocs/xml/vegbank_example_ver1.0.2.xml
5

    
6
import os
7
import os.path
8
import sys
9
import xml.dom.minidom
10

    
11
sys.path.append(os.path.dirname(__file__)+"/lib")
12

    
13
def env_flag(name): return name in os.environ and os.environ[name] != ''
14

    
15
def main():
16
    # Get db config from env vars
17
    db_config_names = ['host', 'user', 'password', 'database']
18
    env_names = []
19
    def get_db_config(prefix):
20
        has_all = True
21
        db_config = {}
22
        for name in db_config_names:
23
            env_name = prefix+'_'+name
24
            env_names.append(env_name)
25
            if env_name in os.environ: db_config[name] = os.environ[env_name]
26
            else: has_all = False
27
        if has_all: return db_config
28
        else: return None
29
    from_db_config = get_db_config('from')
30
    to_db_config = get_db_config('to')
31
    in_is_db = from_db_config != None
32
    out_is_db = to_db_config != None
33
    uses_map = in_is_db or not out_is_db
34
    
35
    # Parse args
36
    prog_name = sys.argv[0]
37
    try: prog_name, map_path = sys.argv
38
    except ValueError:
39
        if uses_map: raise SystemExit('Usage: env'+''.join(map(lambda name:
40
            ' ['+name+'=...]', env_names))+' [commit=1] '+prog_name
41
            +' [map_path] [<input] [>output]')
42
    commit = env_flag('commit')
43
    
44
    csv_config = dict(delimiter=',', quotechar='"')
45
    
46
    # Load map header
47
    in_is_xml = True
48
    if uses_map:
49
        import copy
50
        import csv
51
        
52
        import xpath
53
        
54
        map_stream = open(map_path, 'rb')
55
        map_reader = csv.reader(map_stream, **csv_config)
56
        src, dest = map_reader.next()[:2]
57
        src, sep, src_base = src.partition('/')
58
        in_is_xml = sep != ''
59
    
60
    # Input datasource to XML tree, mapping if needed
61
    if in_is_xml: doc = xml.dom.minidom.parse(sys.stdin)
62
    if uses_map:
63
        import xml_xpath
64
        
65
        map_ = {}
66
        has_types = False # whether outer elements are type containiners
67
        for row in map_reader:
68
            in_, out = row[:2]
69
            if out != '':
70
                if out.startswith('/*s/'): has_types = True # *s for type elem
71
                out = xpath.parse(out)
72
                if in_is_xml: pass # TODO: process the mapping
73
                elif in_is_db: pass # TODO: process the mapping
74
                else: map_[in_] = out
75
        map_stream.close()
76
        
77
        out_doc = xml.dom.minidom.getDOMImplementation().createDocument(None,
78
            dest, None)
79
        if in_is_xml: raise Exception('XML-XML mapping not supported yet')
80
        else: # input is CSV
81
            reader = csv.reader(sys.stdin, **csv_config)
82
            fieldnames = reader.next()
83
            row_idx = 0
84
            for row in reader:
85
                row_id = str(row_idx)
86
                for idx, name in enumerate(fieldnames):
87
                    value = row[idx]
88
                    if value != '' and name in map_:
89
                        path = copy.deepcopy(map_[name]) # don't modify value!
90
                        xpath.set_id(path, row_id, has_types)
91
                        xpath.set_value(path, value)
92
                        xml_xpath.get(out_doc, path, True)
93
                row_idx += 1
94
        doc = out_doc
95
    
96
    # Output XML tree
97
    if to_db_config != None: # output is database
98
        import psycopg2
99
        from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE
100
        
101
        import db_xml
102
        
103
        db = psycopg2.connect(**to_db_config)
104
        db.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)
105
        try:
106
            row_ct_ref = [0]
107
            db_xml.xml2db(db, doc.documentElement, row_ct_ref)
108
            print 'Inserted '+str(row_ct_ref[0])+' rows'
109
            if commit: db.commit()
110
        finally:
111
            db.rollback()
112
            db.close()
113
    else: doc.writexml(sys.stdout, addindent='    ', newl='\n') # output is XML
114

    
115
main()
(2-2/3)