-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcsv-to-rest.py
More file actions
346 lines (303 loc) · 11.2 KB
/
csv-to-rest.py
File metadata and controls
346 lines (303 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
from bottle import route, run, template, response, static_file, debug, redirect
import csv
import json
import os.path
from os import listdir
from os.path import isfile, join
import argparse
import glob
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
# Constants
LINE_RETURN_COUNT_MAX = 100 # How many records to return in a dump
CSVPATH_DEFAULT = './data/'
# CSVFILENAME_DEFAULT = 'IME-US-list.csv'
PORT_DEFAULT = 8983
# Globals
csvpath = './data/'
csvreader = None
csvfields = []
csvcontents = []
csvdict = {}
_filelist = []
# Fetch arguments
parser = argparse.ArgumentParser(description='Expose CSV via REST')
parser.add_argument('-d', '--datapath', type=str, nargs='?', default=CSVPATH_DEFAULT)
parser.add_argument('-f', '--filename', type=str, nargs='?', default=None)
parser.add_argument('-t', '--template', type=str, nargs='?', default=None)
parser.add_argument('-p', '--port', type=int, nargs='?', default=PORT_DEFAULT)
parser.add_argument('-v', '--verbose', help='Verbose output', action='store_true')
parser.add_argument('-q', '--quiet', help='No output', action='store_true')
parser.add_argument('-z', '--dev-mode', help='Dev mode - debug mode & reloading', action='store_true')
parser.add_argument('-C', '--critmaj', help='Only include Critical and Major Severity defects', action='store_true')
args = parser.parse_args()
csvpath = args.datapath
csvfilename = args.filename
fname_glob = args.template
port = args.port
verbose = args.verbose
quiet = args.quiet
devmode = args.dev_mode
critmaj = args.critmaj
# Don't allow -q and -v
if verbose and quiet:
logging.fatal("Cannot set both 'verbose' and 'quiet' parameters")
exit(101)
# Routes
@route('/')
def home():
return 'home page'
@route('/_admin')
def admin():
return '<h1>admin page</h1><h2>Filename</h2>' + csvfilename + '<h2># of records</h2>' + str(len(_filelist) - 1) + "<h2>First file</h2><p>" + getFirstFilename() + " - <a href='/_admin/redirect/" + getFirstFilename() + "'>select</a></p><h2>Last file</h2>" + getLastFilename() + " - <a href='/_admin/redirect/" + getLastFilename() + "'>select</a></p><h2>Template</h2><p>" + (fname_glob if fname_glob else "<em>none set</em>") + "</p><h2>Fields</h2><UL><LI>" + '</LI><LI>'.join(csvfields) + "</LI></UL>\n" + "<h2>Available data files</h2>" + listDataFiles()
@route('/_admin/show/fields')
def adminShowfields():
return "<UL><LI>" + '</LI><LI>'.join(csvcontents[0]) + "</LI></UL>"
@route('/_admin/get/fields')
def adminGetFields():
response.add_header('Content-type', 'application/json')
return json.dumps(csvcontents[0])
@route('/_admin/get/filenames')
def adminGetFilenames():
global filelist
filelist = getDataFiles()
return buildResponseObjectSuccess(filelist)
@route('/_admin/get/selectedFile')
def adminGetSelectedFile():
global csvfilename
return buildResponseObjectSuccess(csvfilename)
@route('/_admin/get/template')
def adminGetTemplate():
global fname_glob
return fname_glob
@route('/_admin/set/template/<new_template>')
def adminSetTemplate(new_template):
global fname_glob
fname_glob = new_template
getDataFiles()
redirect('/_admin')
@route('/_admin/redirect/<new_filename>')
def adminRedirect(new_filename):
if (os.path.isfile(os.sep.join([csvpath, new_filename]))):
logging.info("adminRedirect: reading in new file: " + new_filename)
read_file(new_filename)
return buildResponseObjectSuccessOk()
else:
return buildResponseObjectError(["File not found"])
@route('/_admin/redirect-latest')
def adminRedirectLatest():
read_file(getLastFilename())
return buildResponseObjectSuccessOk()
@route('/_admin/redirect-first')
def adminRedirectFirst():
read_file(getFirstFilename())
return buildResponseObjectSuccessOk()
@route('/get/<id_value>')
def getIdValue(id_value):
result = {}
row = csvdict[id_value]
fieldCtr = 0
for f in row:
result[csvfields[fieldCtr]] = f
fieldCtr += 1
return buildResponseObjectSuccess(result)
@route('/get/<field>/<value>')
def getFieldValue(field, value):
result_rows = []
# Get the # of the field you're searching for
fieldnum = csvfields.index(field)
for r in csvcontents:
if r[fieldnum] == value: # Match, so save the row
hit = {}
fieldCtr = 0
# Add field names
for f in r:
hit[csvfields[fieldCtr]] = f
fieldCtr += 1
result_rows.append(hit)
return buildResponseObjectSuccess(result_rows)
@route('/get/<field1>/<value1>/<field2>/<value2>')
def getFieldValueDouble(field1, value1, field2, value2):
# Handle empty field spec
if value2 == '""':
value2 = ""
result_rows = []
# Get the # of the field you're searching for
fieldnum1 = csvfields.index(field1)
fieldnum2 = csvfields.index(field2)
for r in csvcontents:
if (r[fieldnum1] == value1) and (r[fieldnum2] == value2): # Match, so save the row
hit = {}
fieldCtr = 0
# Add field names
for f in r:
if (fieldCtr < len(csvfields)):
hit[csvfields[fieldCtr]] = f
fieldCtr += 1
result_rows.append(hit)
return buildResponseObjectSuccess(result_rows)
@route('/get/<field1>/<value1>/<field2>/<value2>/<field3>/<value3>')
def getFieldValueTriple(field1, value1, field2, value2, field3, value3):
result_rows = []
# Get the # of the field you're searching for
fieldnum1 = csvfields.index(field1)
fieldnum2 = csvfields.index(field2)
fieldnum3 = csvfields.index(field3)
for r in csvcontents:
if (r[fieldnum1] == value1) and (r[fieldnum2] == value2) and (r[fieldnum3] == value3): # Match, so save the row
hit = {}
fieldCtr = 0
# Add field names
for f in r:
hit[csvfields[fieldCtr]] = f
fieldCtr += 1
result_rows.append(hit)
return buildResponseObjectSuccess(result_rows)
@route('/count/<field>/<value>')
def countFieldValue(field, value):
# Get the # of the field you're searching for
fieldnum = csvfields.index(field)
counter = 0
for r in csvcontents:
if r[fieldnum] == value: # Match, so increment the counter
counter += 1
return buildResponseObjectSuccessCount(counter)
@route('/count/<field1>/<value1>/<field2>/<value2>')
def countFieldValueTwo(field1, value1, field2, value2):
# Get the # of the field you're searching for
fieldnum1 = csvfields.index(field1)
fieldnum2 = csvfields.index(field2)
counter = 0
for r in csvcontents:
if (r[fieldnum1] == value1) and (r[fieldnum2] == value2): # Match, so increment the counter
counter += 1
return buildResponseObjectSuccessCount(counter)
@route('/count/<field1>/<value1>/<field2>/<value2>/<field3>/<value3>')
def countFieldValueThree(field1, value1, field2, value2, field3, value3):
# Get the # of the field you're searching for
fieldnum1 = csvfields.index(field1)
fieldnum2 = csvfields.index(field2)
fieldnum3 = csvfields.index(field3)
counter = 0
for r in csvcontents:
if (r[fieldnum1] == value1) and (r[fieldnum2] == value2) and (r[fieldnum3] == value3): # Match, so increment the counter
counter += 1
return buildResponseObjectSuccessCount(counter)
@route('/list/<field>')
def listValuesByField(field):
values = {}
fieldnum = csvfields.index(field)
for r in csvcontents:
if not r[fieldnum] in values:
values[r[fieldnum]] = 1
else:
values[r[fieldnum]] += 1
return buildResponseObjectSuccess(values)
@route('/list/<field>/<filter>/<value>')
def listValuesByFieldFiltered(field, filter, value):
values = {}
fieldnum = csvfields.index(field)
filternum = csvfields.index(filter)
for r in csvcontents:
if (r[filternum] == value):
if not r[fieldnum] in values:
values[r[fieldnum]] = 1
else:
values[r[fieldnum]] += 1
return buildResponseObjectSuccess(values)
def read_file(fname):
global csvfields, csvfilename, csvcontents, csvdict
logging.warn("read_file(" + fname + ") starting (" + csvfilename + ")...")
csvfields = []
csvfilename = fname
csvcontents = []
csvdict = {}
with open(os.sep.join([csvpath, csvfilename]), 'r') as csvfile:
csvreader = csv.reader(csvfile, delimiter=',', quotechar='"')
csvfields = next(csvreader) # Read in the field names
# logging.debug("csvfields: %s" % (csvfields))
if (critmaj): # Only capture the severityPos if critmaj is set
severityPos = csvfields.index("Severity") # Store the severity position for later
else:
severityPos = None
for row in csvreader:
if (critmaj and (row[severityPos] == "Minor Problem" or row[severityPos] == "Cosmetic")):
# logging.debug("Skipping Minor/Cosmetic problem")
pass
else:
csvcontents.append(row)
csvdict[row[0]] = row
# logging.debug("...setting csvdict[" + str(row[0]) + "]")
# csvfields = csvcontents[0]
# logging.debug("...csvfields len = " + str(len(csvfields)))
def buildBasicResponseObject(status, remainder = {}):
response = {}
response['meta'] = {}
response['meta']['status'] = status
response['meta']['path'] = csvpath
response['meta']['filename'] = csvfilename
response['meta'].update(remainder)
return(response)
def buildResponseObjectSuccessCount(counter):
result = buildBasicResponseObject('success')
result['data'] = { 'count': counter }
return result
def buildResponseObjectSuccessOk():
result = buildBasicResponseObject('success')
return result
def buildResponseObjectSuccess(data):
result = buildBasicResponseObject('success', { 'hit_count': len(data) })
result['data'] = data
return result
def buildResponseObjectError(errors):
result = buildBasicResponseObject('error', { 'error_count': len(errors) })
result['errors'] = errors
return result
def getDataFiles():
global csvpath, _filelist, fname_glob
if (fname_glob): # Glob/template specified
logging.info("fname_glob set (%s)..." % (csvpath + os.path.sep + fname_glob))
onlyfiles = [os.path.basename(f) for f in glob.glob(csvpath + os.path.sep + fname_glob)]
else: # No globbing/template
logging.info("fname_glob not set...")
onlyfiles = [f for f in listdir(csvpath) if (isfile(join(csvpath, f)) and f.endswith(".csv"))]
_filelist = sorted(onlyfiles, reverse=True)
return(_filelist)
def getLastFilename():
return(getDataFiles()[0])
def getFirstFilename():
return(getDataFiles()[-1])
def listDataFiles():
result = "<UL>"
filelist = getDataFiles()
for f in filelist:
result += "<LI><A HREF='/_admin/redirect/" + f + "'>" + f + "</A></LI>"
result += "</UL>"
return result
if __name__ == "__main__":
# Verify that the path exists
if (not os.path.exists(csvpath)):
logging.fatal("Path (%s) was not found... " % (csvpath))
exit(101)
else:
_filelist = getDataFiles()
if None == csvfilename:
csvfilename = _filelist[0]
# Verify the file exists
if (not os.path.exists(csvpath + os.path.sep + csvfilename)):
logging.fatal("Can't find file (path: %s; name: %s). Exiting" % (csvpath, csvfilename))
exit(102)
else:
logging.debug("Parsing %s" % (os.sep.join([csvpath, csvfilename])))
if devmode:
debug(True)
# logging.basicConfig(level=logging.DEBUG)
logging.debug("In Developer Mode... debug(True)", )
if verbose or not quiet:
logging.info("Data path: %s" % (csvpath))
logging.info("Quiet? %s; Verbose? %s" % (quiet, verbose))
read_file(csvfilename)
if devmode:
run(host='0.0.0.0', port=port, reloader=True)
else:
run(host='0.0.0.0', port=port)