citysdk 0.1.2.5 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +33 -0
- data/Gemfile.lock +27 -21
- data/README.md +55 -33
- data/bin/csdk +1220 -0
- data/citysdk.gemspec +6 -5
- data/examples/simple_api.rb +68 -0
- data/lib/citysdk/api.rb +105 -105
- data/lib/citysdk/file_reader.rb +195 -120
- data/lib/citysdk/importer.rb +36 -113
- data/lib/citysdk/util.rb +26 -4
- data/lib/citysdk.rb +1 -1
- data/spec/api_spec.rb +41 -34
- data/spec/import_spec.rb +40 -75
- metadata +40 -24
- data/b.sh +0 -4
data/bin/csdk
ADDED
@@ -0,0 +1,1220 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require "yaml"
|
7
|
+
|
8
|
+
# require 'citysdk'
|
9
|
+
require_relative '../lib/citysdk.rb'
|
10
|
+
include CitySDK
|
11
|
+
require "curses"
|
12
|
+
include Curses
|
13
|
+
|
14
|
+
|
15
|
+
$categories = %w{ none geography natural cultural civic tourism mobility administrative environment health education security commercial }
|
16
|
+
$outheight = 0
|
17
|
+
$outwidth = 0
|
18
|
+
|
19
|
+
############# convenience ####################################################
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
class Array
|
24
|
+
def sharedStart
|
25
|
+
a = self.sort
|
26
|
+
w1 = a[0]
|
27
|
+
w2 = a[-1]
|
28
|
+
l = w1.length
|
29
|
+
i = 0
|
30
|
+
while(i < l and w1[i] == w2[i])
|
31
|
+
i += 1
|
32
|
+
end
|
33
|
+
w1[0...i]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
############# menus ##########################################################
|
38
|
+
|
39
|
+
class MenuItem
|
40
|
+
attr_accessor :t, :c, :k
|
41
|
+
def initialize(key,text,command,*args)
|
42
|
+
@k = key
|
43
|
+
@t = text
|
44
|
+
@c = command
|
45
|
+
@a = args.flatten
|
46
|
+
end
|
47
|
+
def call
|
48
|
+
(@a.length > 0) ? @c.call(@a) : @c.call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
$currentMenu = nil
|
53
|
+
|
54
|
+
class Menu
|
55
|
+
def initialize(items)
|
56
|
+
@items = items
|
57
|
+
end
|
58
|
+
|
59
|
+
def replace(n,i)
|
60
|
+
@items[n]=i
|
61
|
+
end
|
62
|
+
|
63
|
+
def run(outwinproc = nil)
|
64
|
+
$currentMenu = self
|
65
|
+
$vpos = 3
|
66
|
+
banner
|
67
|
+
@items.each do |i|
|
68
|
+
setpos($vpos,1)
|
69
|
+
addstr(" #{i.k}: #{i.t}")
|
70
|
+
$vpos+=1
|
71
|
+
end
|
72
|
+
$vpos+=1
|
73
|
+
setpos($vpos,1)
|
74
|
+
addstr("please select")
|
75
|
+
setpos($vpos+1,0)
|
76
|
+
addstr("-" * Curses.cols)
|
77
|
+
|
78
|
+
$outwin.close if $outwin
|
79
|
+
$outheight = Curses.lines - $vpos - 3
|
80
|
+
$outwidth = Curses.cols
|
81
|
+
$outwin=Curses::Window.new( $outheight, $outwidth, $vpos+2, 1 )
|
82
|
+
$outwin.refresh
|
83
|
+
outwinproc.call if outwinproc
|
84
|
+
|
85
|
+
curs_set(0)
|
86
|
+
while true
|
87
|
+
noecho
|
88
|
+
c = getch
|
89
|
+
@items.each do |i|
|
90
|
+
if i.k == c
|
91
|
+
sherror('')
|
92
|
+
$outwin.clear
|
93
|
+
i.call
|
94
|
+
setpos($vpos,1)
|
95
|
+
clrtoeol
|
96
|
+
addstr("please select")
|
97
|
+
refresh
|
98
|
+
break
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
############# layers #########################################################
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
def layerSummary
|
111
|
+
layer = selectLayer("please select layer",false)
|
112
|
+
return unless layer
|
113
|
+
$outwin.clear
|
114
|
+
outAddstr 1,sprintf("%15s: #{layer[:name]}", 'name')
|
115
|
+
json = $api.get("/layers/#{layer[:name]}/objects?per_page=1&count")
|
116
|
+
outAddstr 2, sprintf("%15s: #{$api.last_result[:headers]['x-result-count']}", 'data frames')
|
117
|
+
outAddstr 3, sprintf("%15s: #{layer[:title]}", 'title')
|
118
|
+
outAddstr 4, sprintf("%15s: #{layer[:category]}/#{layer[:subcategory]}", 'category')
|
119
|
+
outAddstr 5, sprintf("%15s: #{layer[:description]}", 'description')
|
120
|
+
outAddstr 6, sprintf("%15s: #{layer[:licence]}", 'licence')
|
121
|
+
outAddstr 7, sprintf("%15s: #{layer[:rdf_type]}", 'type')
|
122
|
+
outAddstr 8, sprintf("%15s: #{layer[:fields].blank? ? 'none defined' : layer[:fields].map {|f| f[:name]}.join(', ')}", 'fields')
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def createLayer
|
127
|
+
return unless $user
|
128
|
+
|
129
|
+
userdomains = $user[:domains].split(",")
|
130
|
+
userdomains << 'test'
|
131
|
+
userdomains = userdomains.map{|d|d.strip}.uniq
|
132
|
+
layer = {}
|
133
|
+
|
134
|
+
$outwin.clear
|
135
|
+
$outwin.setpos(1,0)
|
136
|
+
$outwin.addstr "create new layer:"
|
137
|
+
$outwin.refresh
|
138
|
+
|
139
|
+
return cancelled unless get_input("name",:name,layer,3)
|
140
|
+
while true do
|
141
|
+
l = $layers.select { |i| (i[:name] == layer[:name]) }
|
142
|
+
break if l.blank?
|
143
|
+
$outwin.setpos(1,0)
|
144
|
+
$outwin.addstr "name needs to bve unique:"
|
145
|
+
$outwin.refresh
|
146
|
+
layer[:name] = ''
|
147
|
+
return cancelled unless get_input("name",:name,layer,3)
|
148
|
+
end
|
149
|
+
if !$user[:admin]
|
150
|
+
while true do
|
151
|
+
a = layer[:name].split(".")
|
152
|
+
break if a.length > 1 and userdomains.include?(a[0])
|
153
|
+
$outwin.setpos(1,0)
|
154
|
+
$outwin.addstr "name needs to start with one of #{userdomains}:"
|
155
|
+
$outwin.refresh
|
156
|
+
layer[:name] = ''
|
157
|
+
return cancelled unless get_input("name",:name,layer,3)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
return cancelled unless get_input("title",:title,layer,4)
|
162
|
+
return cancelled unless get_input("description",:description,layer,5)
|
163
|
+
return cancelled unless get_input("licence",:licence,layer,6)
|
164
|
+
return cancelled unless get_input("rdf_type",:rdf_type,layer,7)
|
165
|
+
ret = self.get_string_array("please enter data sources, empty line ends input.",8)
|
166
|
+
layer[:data_sources] = ret.blank? ? [""] : ret
|
167
|
+
|
168
|
+
c = get_input_from_list("category:", $user[:admin] ? $categories: $categories[1..-1], 8)
|
169
|
+
layer[:category] = c if c.length
|
170
|
+
return cancelled unless get_input("subcategory",:subcategory,layer,9)
|
171
|
+
|
172
|
+
if $user[:admin]
|
173
|
+
c = get_input_from_list("owner", $users.map {|u| u[:name]} , 10)
|
174
|
+
else
|
175
|
+
c = $user[:name]
|
176
|
+
end
|
177
|
+
layer[:owner] = c
|
178
|
+
|
179
|
+
if $user[:admin]
|
180
|
+
return cancelled unless get_input("authoritative",:authoritative,layer,11)
|
181
|
+
layer[:authoritative] = isTrue?(layer[:authoritative])
|
182
|
+
end
|
183
|
+
|
184
|
+
return cancelled unless get_input("webservice_url",:webservice_url,layer,11)
|
185
|
+
return cancelled unless get_input("update_rate",:update_rate,layer,12)
|
186
|
+
|
187
|
+
# '@context', 'fields'
|
188
|
+
|
189
|
+
$outwin.setpos(10,0)
|
190
|
+
$outwin.addstr JSON.pretty_generate(layer) + "\n\n"
|
191
|
+
$outwin.refresh
|
192
|
+
|
193
|
+
if yesNo?("is this correct? ")
|
194
|
+
apiCall('updating layer') do
|
195
|
+
json = ''
|
196
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
197
|
+
begin
|
198
|
+
json = $api.post "/layers", layer
|
199
|
+
getLayersList
|
200
|
+
$layer = $layers.select { |i| (i[:name] == layer[:name]) }[0]
|
201
|
+
rescue => ex
|
202
|
+
json = ex.message
|
203
|
+
end
|
204
|
+
$api.release
|
205
|
+
outMessage("Layer created: '#{$layer[:name]}'..")
|
206
|
+
return $layer
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
nil
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
def selectLayer(prompt,owner=true)
|
215
|
+
while true
|
216
|
+
n = get_input_from_list(prompt, $layers.map {|u| u[:name]}, 1)
|
217
|
+
return nil if n.blank?
|
218
|
+
l = $layers.select { |i| (i[:name] == n) }
|
219
|
+
if l.blank?
|
220
|
+
outAddstr(3,"Non-existant layer: #{n}")
|
221
|
+
elsif owner and !owned?(l[0])
|
222
|
+
outAddstr(3,"Layer not owned by #{$user[:fullname]}; #{n}")
|
223
|
+
else
|
224
|
+
return l[0]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def layerAddFields
|
231
|
+
return unless $layer and owned?($layer)
|
232
|
+
$outwin.clear; v = 2
|
233
|
+
outAddstr 1, "current fields:"
|
234
|
+
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
def clearLayer
|
239
|
+
layer = selectLayer("please select layer to empty",true)
|
240
|
+
return unless layer
|
241
|
+
$outwin.clear
|
242
|
+
if yesNo?("clear all objects from #{layer[:name]}; sure?", 1)
|
243
|
+
apiCall("deleting from layer #{layer[:name]}", "all #{layer[:name]} objects deleted") do
|
244
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
245
|
+
$api.delete("/layers/#{layer[:name]}/objects")
|
246
|
+
$api.release
|
247
|
+
''
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def selectNewOrExistingLayer
|
254
|
+
$outwin.clear
|
255
|
+
$outwin.setpos(1,0)
|
256
|
+
$outwin.addstr "Set layer: make new or use existing layer? (n/e)"
|
257
|
+
$outwin.refresh
|
258
|
+
a = charIn(['n','N','e','E'])
|
259
|
+
return cancelled if a == 27
|
260
|
+
if a.downcase == 'n'
|
261
|
+
return createLayer
|
262
|
+
end
|
263
|
+
return selectLayer("please select layer",true)
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
|
268
|
+
def doImport
|
269
|
+
|
270
|
+
return unless $user
|
271
|
+
|
272
|
+
if $file_hash[:layername].blank?
|
273
|
+
$file_hash[:layername] = $layer = selectNewOrExistingLayer
|
274
|
+
return unless $layer
|
275
|
+
end
|
276
|
+
|
277
|
+
unless ($user[:admin] or $layer[:owner][:name] == $user[:name])
|
278
|
+
outMessage("User has no access to layer #{$layer[:name]}..")
|
279
|
+
return
|
280
|
+
end
|
281
|
+
|
282
|
+
$file_hash[:batch_size] == 250 if $file_hash[:batch_size].blank?
|
283
|
+
get_input("Please select batch size for upload:", :batch_size, $file_hash, 1)
|
284
|
+
|
285
|
+
if $file_hash[:hasgeometry].nil? and $file_hash[:postcode] and $file_hash[:housenumber]
|
286
|
+
outAddstr 1,"No geometry detected, but possibly address and postcode. "
|
287
|
+
outAddstr 2,"We can possibly link to postcode/housenumber combination."
|
288
|
+
addreslayer = get_input_from_list("Please select address layer (blank for cancel)", $layers.map {|u| u[:name]}, 3)
|
289
|
+
return cancelled if addreslayer.blank?
|
290
|
+
alayer = $layers.select {|u| addreslayer = u[:name]}
|
291
|
+
if alayer.blank?
|
292
|
+
outMessage("No such layer: #{addreslayer}..")
|
293
|
+
return
|
294
|
+
end
|
295
|
+
field = get_input_from_list("Please pc/hn field (postcode_huisnummer)", $layers.map {|u| u[:name]}, 2)
|
296
|
+
end
|
297
|
+
|
298
|
+
if $file_hash[:hasgeometry]
|
299
|
+
n = $file_reader.content.length
|
300
|
+
i = 0
|
301
|
+
begin
|
302
|
+
$file_hash[:layername] = $layer[:name]
|
303
|
+
imp = Importer.new($file_hash, $file_reader)
|
304
|
+
imp.api.batch_size = $file_hash[:batch_size].to_i
|
305
|
+
n = imp.doImport do |d|
|
306
|
+
i += 1
|
307
|
+
outAddstr 1,"importing: #{Integer(100 * i / n)}%" if(i % 100)
|
308
|
+
end
|
309
|
+
outMessage("Import of #{$file_hash[:file_path]} succesful; processed #{n[:created]} items.")
|
310
|
+
rescue Exception => e
|
311
|
+
$file_hash[:layername] = nil
|
312
|
+
sherror(e.message)
|
313
|
+
return
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
if $layer[:fields].blank?
|
318
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
319
|
+
$file_hash[:fields].each do |f|
|
320
|
+
k = $file_hash[:alternate_fields][f]
|
321
|
+
field = {name: k.to_s}
|
322
|
+
$api.post("/layers/#{$layer[:name]}/fields", field)
|
323
|
+
$layer[:fields] << field
|
324
|
+
end
|
325
|
+
$api.release
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
def owned?(layer)
|
332
|
+
return true if $user[:admin]
|
333
|
+
return ($user[:name] == layer[:owner][:name]) if layer.class == Hash
|
334
|
+
$layers.each do |l|
|
335
|
+
return true if l[:name]==layer and ($user[:name] == l[:owner][:name])
|
336
|
+
end
|
337
|
+
false
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
|
342
|
+
def editLayer
|
343
|
+
|
344
|
+
l = selectLayer("layer to edit")
|
345
|
+
return if l.blank?
|
346
|
+
layer = l.deep_copy
|
347
|
+
|
348
|
+
$outwin.clear
|
349
|
+
$outwin.setpos(1,0)
|
350
|
+
$outwin.addstr "edit layer #{layer[:name]}:"
|
351
|
+
$outwin.refresh
|
352
|
+
|
353
|
+
return cancelled unless get_input("title",:title,layer,3)
|
354
|
+
return cancelled unless get_input("description",:description,layer,4)
|
355
|
+
return cancelled unless get_input("licence",:licence,layer,5)
|
356
|
+
return cancelled unless get_input("rdf_type",:rdf_type,layer,6)
|
357
|
+
ret = self.get_string_array("please enter data sources, empty line ends input.",7)
|
358
|
+
layer[:data_sources] = ret.blank? ? [""] : ret
|
359
|
+
|
360
|
+
c = get_input_from_list("category (#{layer[:category]})", $user[:admin] ? $categories: $categories[1..-1], 7)
|
361
|
+
layer[:category] = c if c.length
|
362
|
+
return cancelled unless get_input("subcategory",:subcategory,layer,8)
|
363
|
+
|
364
|
+
if $user[:admin]
|
365
|
+
c = get_input_from_list("owner (#{layer[:owner][:name]})", $users.map {|u| u[:name]} , 9)
|
366
|
+
layer[:owner] = c if c.length
|
367
|
+
end
|
368
|
+
|
369
|
+
if $user[:admin]
|
370
|
+
return cancelled unless get_input("authoritative",:authoritative,layer,10)
|
371
|
+
layer[:authoritative] = isTrue?(layer[:authoritative])
|
372
|
+
end
|
373
|
+
|
374
|
+
return cancelled unless get_input("webservice_url",:webservice_url,layer,11)
|
375
|
+
return cancelled unless get_input("update_rate",:update_rate,layer,12)
|
376
|
+
|
377
|
+
# '@context', 'fields'
|
378
|
+
|
379
|
+
layer.delete_if{ |k, v| (v == l[k]) or v.blank? }
|
380
|
+
|
381
|
+
if layer.empty?
|
382
|
+
$outwin.clear
|
383
|
+
$outwin.setpos(1,0)
|
384
|
+
$outwin.addstr "No change.."
|
385
|
+
$outwin.refresh
|
386
|
+
else
|
387
|
+
|
388
|
+
$outwin.setpos(10,0)
|
389
|
+
$outwin.addstr JSON.pretty_generate(layer) + "\n\n"
|
390
|
+
$outwin.refresh
|
391
|
+
|
392
|
+
$outwin.setpos(11,0)
|
393
|
+
if yesNo?("is this correct? ")
|
394
|
+
apiCall('updating layer') do
|
395
|
+
json = ''
|
396
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
397
|
+
begin
|
398
|
+
json = $api.patch "/layers/#{layer[:name]}", layer
|
399
|
+
getLayersList
|
400
|
+
json = $layers.select { |i| (i[:name] == layer[:name]) }[0]
|
401
|
+
rescue Exception => ex
|
402
|
+
json = ex.message
|
403
|
+
end
|
404
|
+
$api.release
|
405
|
+
end
|
406
|
+
json
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
end
|
412
|
+
|
413
|
+
|
414
|
+
def deleteLayer
|
415
|
+
return unless $user
|
416
|
+
layer = selectLayer("layer to delete")
|
417
|
+
return if layer.blank?
|
418
|
+
|
419
|
+
if yesNo?("Are you sure to delete layer #{layer[:name]}?",1)
|
420
|
+
apiCall('deleting layer',"Layer #{layer[:name]} is deleted.") do
|
421
|
+
json = ''
|
422
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
423
|
+
json = $api.delete "/layers/#{layer[:name]}"
|
424
|
+
$api.release
|
425
|
+
getLayersList
|
426
|
+
end
|
427
|
+
json
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
|
433
|
+
def getLayersList
|
434
|
+
begin
|
435
|
+
$layers = $api.get('/layers?per_page=1000')
|
436
|
+
$layers = $layers[:features].map { |f| f[:properties]}
|
437
|
+
rescue Exception => e
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
def showLayers
|
442
|
+
i = 1
|
443
|
+
$layers.each do |u|
|
444
|
+
s = sprintf("%-15s ",u[:name])
|
445
|
+
s += sprintf("#{u[:category]}/#{u[:subcategory]}, #{u[:owner][:fullname]}, #{u[:description]}")
|
446
|
+
$outwin.setpos(i,0)
|
447
|
+
$outwin.addstr s.remove_non_ascii
|
448
|
+
i += 1
|
449
|
+
end
|
450
|
+
$outwin.refresh
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
############# users ##########################################################
|
455
|
+
|
456
|
+
def getUserList
|
457
|
+
$users = []
|
458
|
+
begin
|
459
|
+
$api.authenticate($credentials_hash[:login],$credentials_hash[:password]) if $user
|
460
|
+
$users = $api.get('/owners?per_page=1000')
|
461
|
+
$api.release if $user
|
462
|
+
rescue Exception => e
|
463
|
+
sherror("getUserList: " + e.message)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def showUsers
|
468
|
+
i = 2
|
469
|
+
$users.each do |u|
|
470
|
+
s = u[:admin] ? 'a ' : ' '
|
471
|
+
s += sprintf("%-12s ",u[:name]) if $user[:admin]
|
472
|
+
s += sprintf("%-20s #{u[:email]}", u[:fullname])
|
473
|
+
s += " (#{u[:domains]})"
|
474
|
+
$outwin.setpos(i,0)
|
475
|
+
$outwin.addstr s.remove_non_ascii
|
476
|
+
i += 1
|
477
|
+
end
|
478
|
+
$outwin.refresh
|
479
|
+
end
|
480
|
+
|
481
|
+
|
482
|
+
|
483
|
+
def deleteUser(u)
|
484
|
+
return unless ($user and $user[:admin])
|
485
|
+
user = selectUser("user to delete")
|
486
|
+
return if user.nil?
|
487
|
+
if (u[:name] =~ /citysdk/i)
|
488
|
+
return sherror("User 'citysdk' cannot be deleted.")
|
489
|
+
end
|
490
|
+
if yesNo?("Are you sure; deleting user #{u[:name]}?",1)
|
491
|
+
apiCall('deleting user',"User #{u[:name]} is deleted.") do
|
492
|
+
json = ''
|
493
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
494
|
+
json = $api.delete "/owners/#{u[:name]}"
|
495
|
+
$api.release
|
496
|
+
getUserList
|
497
|
+
end
|
498
|
+
json
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
|
504
|
+
def createUser
|
505
|
+
if $user and $user[:admin]
|
506
|
+
user = {}
|
507
|
+
$outwin.clear
|
508
|
+
$outwin.setpos(1,0)
|
509
|
+
$outwin.addstr "create new user:"
|
510
|
+
$outwin.refresh
|
511
|
+
user[:admin] = 'y/n'
|
512
|
+
|
513
|
+
return cancelled unless get_input("login name",:name,user,3)
|
514
|
+
return cancelled if user[:name].blank?
|
515
|
+
return cancelled unless get_input("full name",:fullname,user,4)
|
516
|
+
return cancelled unless get_input("email",:email,user,5)
|
517
|
+
return cancelled unless get_input("organization",:organization,user,6)
|
518
|
+
return cancelled unless get_input("website",:website,user,7)
|
519
|
+
return cancelled unless get_input("domains",:domains,user,8,'test, ')
|
520
|
+
return cancelled unless get_input("admin",:admin,user,9)
|
521
|
+
return cancelled unless get_input("password",:password,user,10)
|
522
|
+
|
523
|
+
user[:admin] = isTrue?(user[:admin])
|
524
|
+
user[:domains] = user[:domains].split(",").map {|d| d.strip }.join(",")
|
525
|
+
|
526
|
+
apiCall('add new user') do
|
527
|
+
json = ''
|
528
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
529
|
+
begin
|
530
|
+
json = $api.post "/owners", user
|
531
|
+
getUserList
|
532
|
+
rescue => ex
|
533
|
+
json = ex.message
|
534
|
+
end
|
535
|
+
$api.release
|
536
|
+
end
|
537
|
+
json
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
|
543
|
+
def selectUser(prompt)
|
544
|
+
return $user unless $user[:admin]
|
545
|
+
n = get_input_from_list(prompt, $users.map {|u| u[:name]}, 1)
|
546
|
+
l = $users.select { |i| (i[:name] == n) }
|
547
|
+
if l.blank?
|
548
|
+
sherror("Non-existant user: #{n}")
|
549
|
+
return nil
|
550
|
+
end
|
551
|
+
l[0]
|
552
|
+
end
|
553
|
+
|
554
|
+
|
555
|
+
|
556
|
+
def editUser
|
557
|
+
return unless $user
|
558
|
+
u = selectUser("user to edit")
|
559
|
+
return if u.nil?
|
560
|
+
user = u.deep_copy
|
561
|
+
$outwin.clear
|
562
|
+
$outwin.setpos(1,0)
|
563
|
+
$outwin.addstr "edit user #{user[:name]}:"
|
564
|
+
$outwin.refresh
|
565
|
+
|
566
|
+
vp = 3
|
567
|
+
|
568
|
+
return cancelled unless get_input("full name",:fullname,user,vp+=1)
|
569
|
+
return cancelled unless get_input("email",:email,user,vp+=1)
|
570
|
+
return cancelled unless get_input("organization",:organization,user,vp+=1)
|
571
|
+
return cancelled unless get_input("website",:website,user,vp+=1)
|
572
|
+
return cancelled unless get_input("domains",:domains,user,vp+=1) if $user[:admin]
|
573
|
+
return cancelled unless get_input("password",:password,user,vp+=1)
|
574
|
+
if $user[:admin]
|
575
|
+
return cancelled unless get_input("admin",:admin,user,vp+=1)
|
576
|
+
user[:admin] = isTrue?(user[:admin])
|
577
|
+
end
|
578
|
+
|
579
|
+
user.delete_if{ |k, v| (v == u[k]) or v.blank? }
|
580
|
+
user[:domains] = user[:domains].split(",").map {|d| d.strip }.join(",") if user[:domains]
|
581
|
+
|
582
|
+
if user.empty?
|
583
|
+
$outwin.clear
|
584
|
+
$outwin.setpos(1,0)
|
585
|
+
$outwin.addstr "No change.."
|
586
|
+
$outwin.refresh
|
587
|
+
else
|
588
|
+
|
589
|
+
$outwin.setpos(vp+=2,0)
|
590
|
+
$outwin.addstr JSON.pretty_generate(user) + "\n\n"
|
591
|
+
$outwin.refresh
|
592
|
+
|
593
|
+
if yesNo?("is this correct? ")
|
594
|
+
apiCall('updating user') do
|
595
|
+
json = ''
|
596
|
+
if $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
597
|
+
begin
|
598
|
+
json = $api.patch "/owners/#{u[:name]}", user
|
599
|
+
getUserList
|
600
|
+
rescue => ex
|
601
|
+
json = ex.message
|
602
|
+
end
|
603
|
+
$api.release
|
604
|
+
end
|
605
|
+
json
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
end
|
610
|
+
|
611
|
+
end
|
612
|
+
|
613
|
+
|
614
|
+
############# files ##########################################################
|
615
|
+
def loadConfig
|
616
|
+
f = $file_hash[:file_path].gsub(/\.\w+$/,'.cfg')
|
617
|
+
if File.exists?(f)
|
618
|
+
naf = {}
|
619
|
+
$file_hash = JSON.parse(File.read(f), symbolize_names: true)
|
620
|
+
$file_hash[:password] = $credentials_hash[:password]
|
621
|
+
$file_hash[:host] = $credentials_hash[:host]
|
622
|
+
$file_hash[:login] = $credentials_hash[:login]
|
623
|
+
$file_hash[:fields].map! { |f| (f.to_sym rescue f) || f }
|
624
|
+
$file_hash[:original_fields].map! { |f| (f.to_sym rescue f) || f }
|
625
|
+
$file_hash[:alternate_fields].each { |k,v| naf[(k.to_sym rescue k) || k] = v }
|
626
|
+
$file_hash[:alternate_fields] = naf
|
627
|
+
$file_reader.setId_Name
|
628
|
+
# log JSON.pretty_generate($file_hash)
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
|
633
|
+
def saveConfig
|
634
|
+
return unless $file_reader
|
635
|
+
f = $file_hash[:file_path].gsub(/\.\w+$/,'.cfg')
|
636
|
+
File.open(f,"w") do |fd|
|
637
|
+
fh = $file_hash.deep_copy
|
638
|
+
fh[:password] = '***'
|
639
|
+
fd.write(JSON.pretty_generate(fh))
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
|
644
|
+
def saveFile
|
645
|
+
return unless $file_reader
|
646
|
+
r = {}
|
647
|
+
started = false
|
648
|
+
return cancelled unless get_input("Please enter file name",:fn,r,1)
|
649
|
+
if r[:fn]
|
650
|
+
f = File.expand_path(r[:fn])
|
651
|
+
File.open(f,"w") do |fd|
|
652
|
+
fd.write('{"type": "FeatureCollection", "features": ['+"\n")
|
653
|
+
$file_reader.content.each do |o|
|
654
|
+
fd.write(",") if started
|
655
|
+
started = true
|
656
|
+
fd.write(formatObject(o).to_json)
|
657
|
+
fd.write("\n")
|
658
|
+
end
|
659
|
+
fd.write('] }')
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
|
665
|
+
def formatObject(o)
|
666
|
+
begin
|
667
|
+
tp = {}
|
668
|
+
# log JSON.pretty_generate($file_hash)
|
669
|
+
o[:properties][:data].each do |k,v|
|
670
|
+
if $file_hash[:fields].include?(k)
|
671
|
+
k2 = $file_hash[:alternate_fields][k]
|
672
|
+
k2 = k if k2.blank?
|
673
|
+
tp[k2] = v
|
674
|
+
end
|
675
|
+
end
|
676
|
+
o[:properties][:data] = tp
|
677
|
+
|
678
|
+
if $file_hash[:srid] and $file_hash[:srid] != 4326
|
679
|
+
o[:crs] = {
|
680
|
+
type: 'EPSG',
|
681
|
+
properties: {
|
682
|
+
code: $file_hash[:srid]
|
683
|
+
}
|
684
|
+
}
|
685
|
+
end
|
686
|
+
rescue => e
|
687
|
+
sherror("Error: #{e.message}")
|
688
|
+
end
|
689
|
+
o
|
690
|
+
end
|
691
|
+
|
692
|
+
def showSample
|
693
|
+
return unless $file_reader
|
694
|
+
begin
|
695
|
+
o = $file_reader.content[rand($file_reader.content.length)].deep_copy
|
696
|
+
# log JSON.pretty_generate(o)
|
697
|
+
if o[:geometry] and o[:geometry].class == Hash
|
698
|
+
o[:geometry][:coordinates] = ['...'] if o[:geometry][:coordinates] and o[:geometry][:type] != 'Point'
|
699
|
+
else
|
700
|
+
o[:geometry] = nil
|
701
|
+
end
|
702
|
+
$outwin.clear
|
703
|
+
$outwin.setpos(1,0)
|
704
|
+
$outwin.addstr formatObject(o).to_yaml # JSON.pretty_generate(formatObject(o))
|
705
|
+
$outwin.refresh
|
706
|
+
rescue => e
|
707
|
+
sherror("Error: #{e.message}")
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
|
712
|
+
def fileSummary(vp)
|
713
|
+
|
714
|
+
$outwin.setpos(vp+=1,0)
|
715
|
+
$outwin.addstr sprintf("%14s #{$file_hash[:file_path]}", "file:")
|
716
|
+
|
717
|
+
$outwin.setpos(vp+=1,0)
|
718
|
+
$outwin.addstr sprintf("%14s #{$file_hash[:rowcount]}", "total rows:")
|
719
|
+
|
720
|
+
$outwin.setpos(vp+=1,0)
|
721
|
+
$outwin.addstr sprintf("%14s #{$file_hash[:unique_id].to_s}","unique id:")
|
722
|
+
|
723
|
+
if $file_hash[:postcode]
|
724
|
+
$outwin.setpos(vp+=1,0)
|
725
|
+
$outwin.addstr sprintf("%14s #{$file_hash[:postcode]}","postcode in:")
|
726
|
+
end
|
727
|
+
|
728
|
+
if $file_hash[:housenumber]
|
729
|
+
$outwin.setpos(vp+=1,0)
|
730
|
+
$outwin.addstr sprintf("%14s #{$file_hash[:housenumber]}","address/number in:")
|
731
|
+
end
|
732
|
+
|
733
|
+
if $file_hash[:hasgeometry]
|
734
|
+
$outwin.setpos(vp+=1,0)
|
735
|
+
$outwin.addstr sprintf("%14s found in %s; srid: #{$file_hash[:srid]}","geometry:",$file_hash[:hasgeometry])
|
736
|
+
end
|
737
|
+
vp
|
738
|
+
end
|
739
|
+
|
740
|
+
def loadFile
|
741
|
+
$file_reader = nil
|
742
|
+
return cancelled unless get_input("file path",:file_path,$credentials_hash ,1)
|
743
|
+
begin
|
744
|
+
$file_hash = $file_hash.merge $credentials_hash.dup
|
745
|
+
$file_hash[:file_path] = File.expand_path($file_hash[:file_path])
|
746
|
+
$file_reader = FileReader.new($file_hash)
|
747
|
+
$file_hash[:layername] = $layer[:name] if $layer
|
748
|
+
fileSummary(0)
|
749
|
+
$outwin.refresh
|
750
|
+
rescue => e
|
751
|
+
sherror("Error reading file: #{e.inspect}")
|
752
|
+
end
|
753
|
+
|
754
|
+
end
|
755
|
+
|
756
|
+
def editFields
|
757
|
+
props = $file_reader.content[rand($file_reader.content.length)][:properties][:data]
|
758
|
+
|
759
|
+
$file_hash[:fields] = []
|
760
|
+
$file_hash[:alternate_fields] = {}
|
761
|
+
$file_hash[:original_fields].each do |f|
|
762
|
+
$file_hash[:fields] << f
|
763
|
+
end
|
764
|
+
|
765
|
+
$outwin.clear
|
766
|
+
outAddstr(1, "For each field, please choose Accept, Rename or Ignore")
|
767
|
+
vp = 3
|
768
|
+
accepted_fields = []
|
769
|
+
accepted_fields << $file_hash[:unique_id]
|
770
|
+
curs_set(2)
|
771
|
+
$file_hash[:fields].each do |f|
|
772
|
+
outAddstr(vp,sprintf("-- sample: #{props[f]} --"))
|
773
|
+
field = getFieldHeader(vp+2,0,sprintf("Field: '#{f}'; (a/r/i) "),f)
|
774
|
+
return cancelled if field == 27
|
775
|
+
if field
|
776
|
+
accepted_fields << f
|
777
|
+
$file_hash[:alternate_fields][f] = field
|
778
|
+
end
|
779
|
+
end
|
780
|
+
curs_set(0)
|
781
|
+
$file_hash[:fields] = accepted_fields.uniq
|
782
|
+
$file_reader.guessName
|
783
|
+
$file_reader.getAddress
|
784
|
+
$outwin.clear
|
785
|
+
fileSummary(1)
|
786
|
+
$outwin.refresh
|
787
|
+
end
|
788
|
+
|
789
|
+
|
790
|
+
############# utils ##########################################################
|
791
|
+
|
792
|
+
|
793
|
+
def assignGeometry
|
794
|
+
list = $file_hash[:fields].map {|f| $file_hash[:alternate_fields][f].to_s}
|
795
|
+
$outwin.clear
|
796
|
+
x = get_input_from_list('please select x (longitude) field: ', list , 3)
|
797
|
+
y = get_input_from_list(' select y (latitude) field: ', list , 3)
|
798
|
+
unless (x.blank? and y.blank?)
|
799
|
+
$file_reader.findGeometry(x.to_sym, y.to_sym)
|
800
|
+
# $file_reader.guessSRID
|
801
|
+
end
|
802
|
+
$outwin.clear
|
803
|
+
fileSummary(1)
|
804
|
+
$outwin.refresh
|
805
|
+
end
|
806
|
+
|
807
|
+
def assignAddress
|
808
|
+
list = $layer[:fields].map {|f| f[:name]}
|
809
|
+
$outwin.clear
|
810
|
+
x = get_input_from_list('please select x (longitude) field: ', list , 3)
|
811
|
+
y = get_input_from_list(' select y (latitude) field: ', list , 3)
|
812
|
+
unless (x.blank? and y.blank?)
|
813
|
+
end
|
814
|
+
end
|
815
|
+
|
816
|
+
|
817
|
+
|
818
|
+
def getFieldHeader(v,h,s,f)
|
819
|
+
a = ''
|
820
|
+
$outwin.setpos(v,h)
|
821
|
+
$outwin.clrtoeol
|
822
|
+
$outwin.addstr s
|
823
|
+
$outwin.refresh
|
824
|
+
|
825
|
+
a = charIn(["A","a","R","r","I","i"])
|
826
|
+
return 27 if a.ord == 27
|
827
|
+
|
828
|
+
case a.downcase
|
829
|
+
when 'a'
|
830
|
+
return f
|
831
|
+
when 'i'
|
832
|
+
return nil
|
833
|
+
when 'r'
|
834
|
+
$outwin.setpos(v,h)
|
835
|
+
$outwin.clrtoeol
|
836
|
+
$outwin.addstr "Replacement name for field '#{f}': "
|
837
|
+
$outwin.refresh
|
838
|
+
echo
|
839
|
+
s = $outwin.getstr
|
840
|
+
noecho
|
841
|
+
s
|
842
|
+
end
|
843
|
+
end
|
844
|
+
|
845
|
+
|
846
|
+
def isTrue?(o)
|
847
|
+
return true if
|
848
|
+
(o == true) or
|
849
|
+
(o =~ /^y$/i) or
|
850
|
+
(o =~ /^t$/i) or
|
851
|
+
(o =~ /^true$/i) or
|
852
|
+
(o =~ /^yes$/i) or
|
853
|
+
(o =~ /^j$/i) or
|
854
|
+
(o =~ /^ja$/i)
|
855
|
+
false
|
856
|
+
end
|
857
|
+
|
858
|
+
def cancelled
|
859
|
+
outMessage("Cancelled...")
|
860
|
+
end
|
861
|
+
|
862
|
+
|
863
|
+
def doExit
|
864
|
+
File.open($configFile, "w") do |f|
|
865
|
+
f.write($credentials_hash.to_json)
|
866
|
+
f.write("\n")
|
867
|
+
end
|
868
|
+
close_screen
|
869
|
+
exit!(0)
|
870
|
+
end
|
871
|
+
|
872
|
+
|
873
|
+
def doConnect
|
874
|
+
begin
|
875
|
+
if $api.nil?
|
876
|
+
return if ($credentials_hash[:host].nil? or $credentials_hash[:host]=~/^\s*$/)
|
877
|
+
$api = API.new($credentials_hash[:host])
|
878
|
+
end
|
879
|
+
if $user.nil?
|
880
|
+
if $credentials_hash[:login] and $credentials_hash[:login]!~/^\s*$/
|
881
|
+
if $credentials_hash[:password] and $credentials_hash[:password]!~/^\s*$/
|
882
|
+
res = $api.authenticate($credentials_hash[:login],$credentials_hash[:password])
|
883
|
+
$user = $api.get '/owners/' + $credentials_hash[:login]
|
884
|
+
$api.release
|
885
|
+
end
|
886
|
+
end
|
887
|
+
end
|
888
|
+
rescue Exception => e
|
889
|
+
sherror("Error connecting to host: #{e.message}")
|
890
|
+
end
|
891
|
+
if $api
|
892
|
+
getUserList
|
893
|
+
getLayersList
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
|
898
|
+
def setHost
|
899
|
+
vp = 1
|
900
|
+
$api = $user = $error = nil
|
901
|
+
return cancelled unless get_input("hostname",:host,$credentials_hash,1)
|
902
|
+
return cancelled unless get_input("user name",:login,$credentials_hash,2)
|
903
|
+
return cancelled unless get_input("password",:password,$credentials_hash,3)
|
904
|
+
doConnect
|
905
|
+
runFromTop
|
906
|
+
end
|
907
|
+
|
908
|
+
|
909
|
+
def showMatches(l,v)
|
910
|
+
$outwin.clear
|
911
|
+
l.each do |i|
|
912
|
+
$outwin.setpos(v+=1,0)
|
913
|
+
$outwin.addstr("- #{i}")
|
914
|
+
end
|
915
|
+
$outwin.refresh
|
916
|
+
end
|
917
|
+
|
918
|
+
def get_input_from_list(prompt, list, vp, res = '')
|
919
|
+
# 259 up
|
920
|
+
# 258 dn
|
921
|
+
udindex = -1
|
922
|
+
matchl = list
|
923
|
+
prv = inp = nil
|
924
|
+
curs_set(1)
|
925
|
+
$outwin.keypad(true)
|
926
|
+
while true
|
927
|
+
$outwin.setpos(vp,0)
|
928
|
+
$outwin.clrtoeol
|
929
|
+
$outwin.addstr("#{prompt} -> #{res}")
|
930
|
+
$outwin.refresh
|
931
|
+
prv = inp
|
932
|
+
inp = $outwin.getch
|
933
|
+
|
934
|
+
case inp.ord
|
935
|
+
|
936
|
+
when 259 # up
|
937
|
+
if udindex == -1
|
938
|
+
matchl = list.select { |i| i.starts_with?(res) }
|
939
|
+
udindex = 0
|
940
|
+
end
|
941
|
+
res = matchl[udindex -= 1] if (udindex > 0)
|
942
|
+
when 258 # down
|
943
|
+
if udindex == -1
|
944
|
+
matchl = list.select { |i| i.starts_with?(res) }
|
945
|
+
udindex = 0
|
946
|
+
end
|
947
|
+
res = matchl[udindex += 1] if (udindex < matchl.length-1)
|
948
|
+
when 27
|
949
|
+
outMessage("Cancelled")
|
950
|
+
$outwin.keypad(false)
|
951
|
+
return ''
|
952
|
+
when 9
|
953
|
+
if prv.ord == 9
|
954
|
+
showMatches(matchl,vp+1)
|
955
|
+
else
|
956
|
+
matchl = list.select { |i| i.starts_with?(res)}
|
957
|
+
res = matchl.sharedStart if matchl[0]
|
958
|
+
udindex = -1
|
959
|
+
end
|
960
|
+
when 10,13
|
961
|
+
break
|
962
|
+
when 127,8
|
963
|
+
udindex = -1
|
964
|
+
res = res[0...-1]
|
965
|
+
else
|
966
|
+
udindex = -1
|
967
|
+
res << inp if (inp.ord > 31)
|
968
|
+
end
|
969
|
+
end
|
970
|
+
$outwin.keypad(false)
|
971
|
+
curs_set(0)
|
972
|
+
res
|
973
|
+
end
|
974
|
+
|
975
|
+
|
976
|
+
def self.get_string_array(prompt,vp)
|
977
|
+
ret = []
|
978
|
+
echo
|
979
|
+
tempwin=$outwin.subwin( $outheight - vp - 2, $outwidth-2, $vpos+vp+2, 2 )
|
980
|
+
tempwin.setpos(1,1)
|
981
|
+
tempwin.addstr(prompt)
|
982
|
+
vp = 2
|
983
|
+
tempwin.setpos(vp,1)
|
984
|
+
loop do
|
985
|
+
tempwin.setpos(vp,1)
|
986
|
+
tempwin.addstr("-> ")
|
987
|
+
tempwin.refresh
|
988
|
+
s = tempwin.getstr
|
989
|
+
break if s.length == 0
|
990
|
+
vp += 1
|
991
|
+
ret << s
|
992
|
+
end
|
993
|
+
noecho
|
994
|
+
tempwin.clear
|
995
|
+
tempwin.close
|
996
|
+
$outwin.refresh
|
997
|
+
ret
|
998
|
+
end
|
999
|
+
|
1000
|
+
|
1001
|
+
def get_input(prompt, symbol, hash, vp, defs=nil)
|
1002
|
+
res = defs ? defs : ''
|
1003
|
+
echo
|
1004
|
+
curs_set(1)
|
1005
|
+
$outwin.keypad(true)
|
1006
|
+
|
1007
|
+
while true
|
1008
|
+
if hash[symbol] and symbol != :password
|
1009
|
+
outAddstr(vp,"#{prompt} (#{hash[symbol]}) -> #{res}")
|
1010
|
+
else
|
1011
|
+
outAddstr(vp,"#{prompt} -> #{res}")
|
1012
|
+
end
|
1013
|
+
$outwin.refresh
|
1014
|
+
inp = $outwin.getch
|
1015
|
+
case inp.ord
|
1016
|
+
when 27
|
1017
|
+
outMessage("Cancelled")
|
1018
|
+
return false
|
1019
|
+
when 10,13
|
1020
|
+
break
|
1021
|
+
when 127,8
|
1022
|
+
res = res[0...-1]
|
1023
|
+
else
|
1024
|
+
res << inp if (inp.ord > 31)
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
curs_set(0)
|
1029
|
+
noecho
|
1030
|
+
hash[symbol] = res.strip if res.length > 0
|
1031
|
+
$outwin.keypad(false)
|
1032
|
+
true
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
|
1036
|
+
|
1037
|
+
def banner
|
1038
|
+
clear
|
1039
|
+
setpos(0,0)
|
1040
|
+
addstr "CitySDK API, interactive console. "
|
1041
|
+
setpos(1,0)
|
1042
|
+
addstr "host: #{$credentials_hash[:host]}" if $api
|
1043
|
+
addstr "; user: #{$credentials_hash[:login]}" if $user
|
1044
|
+
addstr "; layer: #{$layer[:name]} " if $layer
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def runFromTop
|
1048
|
+
if ($user and $user[:admin])
|
1049
|
+
$topMenu.replace(2, MenuItem.new('3','users', lambda{$userMenu.run}))
|
1050
|
+
else
|
1051
|
+
$topMenu.replace(2, MenuItem.new('3','edit user', lambda{editUser()}))
|
1052
|
+
end
|
1053
|
+
$topMenu.run
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
def yesNo?(p,v=nil)
|
1057
|
+
$outwin.setpos(v,0) if v
|
1058
|
+
$outwin.addstr("#{p} ")
|
1059
|
+
$outwin.refresh
|
1060
|
+
['Y','y','j','J'].include? $outwin.getch
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
|
1064
|
+
|
1065
|
+
def apiCall(error,noerror='',&block)
|
1066
|
+
begin
|
1067
|
+
json = yield
|
1068
|
+
$outwin.clear
|
1069
|
+
$outwin.setpos(1,0)
|
1070
|
+
if json.class == Hash
|
1071
|
+
$outwin.addstr JSON.pretty_generate(json) + "\n"
|
1072
|
+
elsif json == ''
|
1073
|
+
$outwin.addstr noerror + "\n"
|
1074
|
+
elsif json.class == Array
|
1075
|
+
if json[0] == {}
|
1076
|
+
$outwin.addstr noerror + "\n"
|
1077
|
+
else
|
1078
|
+
$outwin.addstr JSON.pretty_generate(json[0]) + "\n"
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
$outwin.refresh
|
1082
|
+
rescue Exception => e
|
1083
|
+
sherror("Error #{error}: #{e.message}")
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
def outAddstr(v,s)
|
1088
|
+
$outwin.setpos(v,0)
|
1089
|
+
$outwin.clrtoeol
|
1090
|
+
$outwin.addstr(s)
|
1091
|
+
$outwin.refresh
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
|
1095
|
+
def sherror(e)
|
1096
|
+
setpos(Curses.lines-1,1)
|
1097
|
+
clrtoeol
|
1098
|
+
addstr($error=e)
|
1099
|
+
refresh
|
1100
|
+
log $error unless $error.blank?
|
1101
|
+
false
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
def log(m)
|
1105
|
+
File.open(File.expand_path('~/csdk.log'), "a") do |f|
|
1106
|
+
f.write("#{m}\n")
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
|
1111
|
+
def not_yet
|
1112
|
+
outMessage("Not yet implemented")
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
def charIn(arr)
|
1116
|
+
a = $outwin.getch
|
1117
|
+
while !(arr.include?(a) or a.ord == 27)
|
1118
|
+
a = $outwin.getch
|
1119
|
+
end
|
1120
|
+
a
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
def outObject(o,v,h)
|
1124
|
+
arr = o.to_yaml.split("\n")
|
1125
|
+
arr.each do |a|
|
1126
|
+
$outwin.setpos(v+=1,h)
|
1127
|
+
$outwin.addstr a.strip
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
def outMessage(m)
|
1132
|
+
$outwin.clear
|
1133
|
+
$outwin.setpos(1,0)
|
1134
|
+
$outwin.addstr m
|
1135
|
+
$outwin.refresh
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
|
1139
|
+
|
1140
|
+
|
1141
|
+
############# run!! ##########################################################
|
1142
|
+
|
1143
|
+
|
1144
|
+
|
1145
|
+
$credentials_hash = {}
|
1146
|
+
$configFile = File.expand_path("~/.csdk")
|
1147
|
+
$outwin = nil
|
1148
|
+
$api = nil
|
1149
|
+
$user = $layer = nil
|
1150
|
+
$error = nil
|
1151
|
+
$file_hash = {}
|
1152
|
+
|
1153
|
+
if File.exists? $configFile
|
1154
|
+
begin
|
1155
|
+
$credentials_hash = JSON.parse( File.read($configFile), symbolize_names: true)
|
1156
|
+
rescue
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
|
1161
|
+
$fileMenu = Menu.new( [
|
1162
|
+
MenuItem.new('2','layer: add or edit fields', lambda { layerAddFields }),
|
1163
|
+
MenuItem.new('3','layer: add or edit context', lambda { not_yet }),
|
1164
|
+
MenuItem.new('5','file: load & analyse', lambda{loadFile}),
|
1165
|
+
MenuItem.new('6','file: rename fields', lambda{editFields}),
|
1166
|
+
MenuItem.new('7','file: show sample', lambda{showSample}),
|
1167
|
+
MenuItem.new('8','file: assign geom columns', lambda { assignGeometry }),
|
1168
|
+
MenuItem.new('9','file: do import', lambda { doImport }),
|
1169
|
+
MenuItem.new('s','file: save processed file', lambda { saveFile }),
|
1170
|
+
MenuItem.new('c','file: save config', lambda { saveConfig }),
|
1171
|
+
MenuItem.new('q','back', lambda{$layer = nil; $topMenu.run})
|
1172
|
+
])
|
1173
|
+
|
1174
|
+
$layerMenu = Menu.new( [
|
1175
|
+
MenuItem.new('1','list layers', lambda{showLayers}),
|
1176
|
+
MenuItem.new('2','layer summary', lambda { layerSummary }),
|
1177
|
+
|
1178
|
+
MenuItem.new('3','create layer', lambda{createLayer}),
|
1179
|
+
MenuItem.new('4','edit layer', lambda { editLayer }),
|
1180
|
+
MenuItem.new('5','clear objects', lambda { clearLayer }),
|
1181
|
+
|
1182
|
+
MenuItem.new('6','delete layer', lambda { deleteLayer }),
|
1183
|
+
MenuItem.new('q','back', lambda{$topMenu.run})
|
1184
|
+
])
|
1185
|
+
|
1186
|
+
$userMenu = Menu.new( [
|
1187
|
+
MenuItem.new('1','list users', lambda{showUsers}),
|
1188
|
+
MenuItem.new('2','create user', lambda{createUser}),
|
1189
|
+
MenuItem.new('3','edit user', lambda{ editUser } ),
|
1190
|
+
MenuItem.new('4','delete user', lambda { deleteUser }),
|
1191
|
+
MenuItem.new('q','back', lambda{$topMenu.run})
|
1192
|
+
])
|
1193
|
+
|
1194
|
+
$topMenu = Menu.new( [
|
1195
|
+
MenuItem.new('1','set host & credentials', lambda{setHost}),
|
1196
|
+
MenuItem.new('2','layers', lambda{$layerMenu.run}),
|
1197
|
+
MenuItem.new('3','edit user', lambda{}),
|
1198
|
+
MenuItem.new('4','analysis / upload', lambda{
|
1199
|
+
$fileMenu.run
|
1200
|
+
}),
|
1201
|
+
MenuItem.new('q','quit', lambda{doExit})
|
1202
|
+
])
|
1203
|
+
|
1204
|
+
|
1205
|
+
while ARGV[0]
|
1206
|
+
s = ARGV.shift
|
1207
|
+
$nohttps = s=='nohttps'
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
init_screen
|
1211
|
+
nl
|
1212
|
+
noecho
|
1213
|
+
|
1214
|
+
trap(:INT) do
|
1215
|
+
doExit
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
|
1219
|
+
doConnect
|
1220
|
+
runFromTop
|