Project

General

Profile

1
#!/usr/bin/env python
2
# A DiGIR client
3

    
4
import os
5
import os.path
6
import sys
7
import urllib
8
import urllib2
9
import xml.dom.minidom as minidom
10

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

    
13
import dates
14
import opts
15
import streams
16
import util
17
import xml_dom
18
import xpath
19

    
20
# Config
21
timeout = 20 # sec
22
chunk_size = 10000 # records
23
default_schema = 'http://digir.net/schema/conceptual/darwin/full/2003/1.0/darwin2full.xsd'
24

    
25
request_xml_template = '''\
26
<?xml version="1.0" encoding="UTF-8"?>
27
<request
28
    xmlns="http://digir.net/schema/protocol/2003/1.0"
29
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
30
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
31
    xmlns:digir="http://digir.net/schema/protocol/2003/1.0"
32
    xmlns:darwin="http://digir.net/schema/conceptual/darwin/2003/1.0"
33
    xmlns:dwc="http://digir.net/schema/conceptual/darwin/2003/1.0"
34
    xsi:schemaLocation="http://digir.net/schema/protocol/2003/1.0 
35
      http://digir.sourceforge.net/schema/protocol/2003/1.0/digir.xsd 
36
      http://digir.net/schema/conceptual/darwin/2003/1.0 
37
      http://digir.sourceforge.net/schema/conceptual/darwin/2003/1.0/darwin2.xsd">
38
    <header>
39
        <version>1.0</version>
40
        <sendTime>[time]</sendTime>
41
        <source>[source]</source>
42
        <destination resource="[resource]">[url]</destination>
43
        <type>search</type>
44
    </header>
45
    <search>
46
        <filter>
47
            <equals>
48
                <darwin:Kingdom>plantae</darwin:Kingdom>
49
            </equals>
50
        </filter>
51
        <records limit="[count]" start="[start]">
52
            <structure schemaLocation="[schema]"/>
53
        </records>
54
        <count>true</count>
55
    </search>
56
</request>
57
'''
58

    
59
diags_start = '<diagnostics>'
60
diags_end = '</diagnostics>'
61

    
62
class InputError(Exception): pass
63

    
64
def main():
65
    # Usage
66
    env_names = []
67
    def usage_err():
68
        raise SystemExit('Usage: '+opts.env_usage(env_names, True)+' '
69
            +sys.argv[0]+' 2>>log')
70
    
71
    # Get config from env vars
72
    url = opts.get_env_var('url', None, env_names)
73
    resource = opts.get_env_var('resource', None, env_names)
74
    schema = opts.get_env_var('schema', default_schema, env_names)
75
    start = util.cast(int, opts.get_env_var('start', 0, env_names))
76
    count = util.cast(int, opts.get_env_var('n', None, env_names))
77
    debug = opts.env_flag('debug', False, env_names)
78
    if url == None or resource == None: usage_err()
79
    
80
    # Logging
81
    def clear_line(): sys.stderr.write('\n')
82
    log_indent = 0
83
    def log(msg, line_ending='\n'):
84
        sys.stderr.write(('    '*log_indent)+msg+line_ending)
85
    def debug_log(str_, label=None):
86
        if debug:
87
            if label != None: sys.stderr.write(label+':\n')
88
            sys.stderr.write(str_+'\n')
89
    
90
    # Request XML
91
    self_dir = os.path.dirname(__file__)
92
    source = os.popen(self_dir+"/local_ip").read().strip()
93
    this_request_xml_template = (request_xml_template
94
        .replace('[source]', source)
95
        .replace('[url]', url)
96
        .replace('[resource]', resource)
97
        .replace('[schema]', schema)
98
        .replace('[count]', str(chunk_size))
99
        )
100
    
101
    # Stats
102
    total = 0
103
    def print_status(line_ending='\n'):
104
        log('Processed '+str(total)+' record(s)', line_ending)
105
    match_ct = None
106
    
107
    # Retrieve data
108
    while count == None or total < count:
109
        # Request XML
110
        time = dates.strftime('%Y-%m-%d %H:%M:%S %Z', dates.now())
111
        request_xml = (this_request_xml_template
112
            .replace('[start]', str(start))
113
            .replace('[time]', time)
114
            )
115
        debug_log(request_xml, 'request')
116
        
117
        # Send request
118
        this_url = url+'?'+urllib.urlencode({'request': request_xml})
119
        stream = streams.CaptureStream(streams.TimeoutInputStream(
120
            urllib2.urlopen(this_url), timeout), diags_start, diags_end)
121
        
122
        # Retrieve response
123
        streams.copy(stream, sys.stdout)
124
        stream.close()
125
        
126
        # Parse diagnostics
127
        diags_str = stream.match
128
        debug_log(diags_str, 'diagnostics')
129
        diags = xml_dom.parse_str(diags_str)
130
        def get_diag(name):
131
            return xpath.get_value(diags, 'diagnostic[@code='+name+']')
132
        
133
        # Process match count
134
        this_match_ct = util.cast(int, get_diag('MATCH_COUNT'))
135
        if this_match_ct != match_ct: # first or updated match count
136
            match_ct = this_match_ct
137
            log('Found '+str(match_ct)+' record(s)')
138
        
139
        # Process record count
140
        this_ct = util.cast(int, get_diag('RECORD_COUNT'))
141
        if this_ct == None: raise InputError('Missing RECORD_COUNT diagnostic')
142
        total += this_ct
143
        start += this_ct # advance start to fetch next set
144
        print_status('\r') # CR at end so next print overwrites msg
145
        if this_ct == 0 or get_diag('END_OF_RECORDS') == 'true': break
146
    
147
    print_status()
148

    
149
main()
(7-7/40)