inat-get 0.8.0.11
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 +7 -0
- data/LICENSE +674 -0
- data/README.md +16 -0
- data/Rakefile +4 -0
- data/bin/inat-get +59 -0
- data/docs/logo.png +0 -0
- data/inat-get.gemspec +33 -0
- data/lib/extra/enum.rb +184 -0
- data/lib/extra/period.rb +252 -0
- data/lib/extra/uuid.rb +90 -0
- data/lib/inat/app/application.rb +50 -0
- data/lib/inat/app/config/messagelevel.rb +22 -0
- data/lib/inat/app/config/shiftage.rb +24 -0
- data/lib/inat/app/config/updatemode.rb +20 -0
- data/lib/inat/app/config.rb +296 -0
- data/lib/inat/app/globals.rb +80 -0
- data/lib/inat/app/info.rb +21 -0
- data/lib/inat/app/logging.rb +35 -0
- data/lib/inat/app/preamble.rb +27 -0
- data/lib/inat/app/status.rb +74 -0
- data/lib/inat/app/task/context.rb +47 -0
- data/lib/inat/app/task/dsl.rb +24 -0
- data/lib/inat/app/task.rb +75 -0
- data/lib/inat/data/api.rb +218 -0
- data/lib/inat/data/cache.rb +9 -0
- data/lib/inat/data/db.rb +87 -0
- data/lib/inat/data/ddl.rb +18 -0
- data/lib/inat/data/entity/comment.rb +29 -0
- data/lib/inat/data/entity/flag.rb +22 -0
- data/lib/inat/data/entity/identification.rb +45 -0
- data/lib/inat/data/entity/observation.rb +172 -0
- data/lib/inat/data/entity/observationphoto.rb +25 -0
- data/lib/inat/data/entity/observationsound.rb +26 -0
- data/lib/inat/data/entity/photo.rb +31 -0
- data/lib/inat/data/entity/place.rb +57 -0
- data/lib/inat/data/entity/project.rb +94 -0
- data/lib/inat/data/entity/projectadmin.rb +21 -0
- data/lib/inat/data/entity/projectobservationrule.rb +50 -0
- data/lib/inat/data/entity/request.rb +58 -0
- data/lib/inat/data/entity/sound.rb +27 -0
- data/lib/inat/data/entity/taxon.rb +94 -0
- data/lib/inat/data/entity/user.rb +67 -0
- data/lib/inat/data/entity/vote.rb +22 -0
- data/lib/inat/data/entity.rb +291 -0
- data/lib/inat/data/enums/conservationstatus.rb +30 -0
- data/lib/inat/data/enums/geoprivacy.rb +14 -0
- data/lib/inat/data/enums/iconictaxa.rb +23 -0
- data/lib/inat/data/enums/identificationcategory.rb +13 -0
- data/lib/inat/data/enums/licensecode.rb +16 -0
- data/lib/inat/data/enums/projectadminrole.rb +11 -0
- data/lib/inat/data/enums/projecttype.rb +37 -0
- data/lib/inat/data/enums/qualitygrade.rb +12 -0
- data/lib/inat/data/enums/rank.rb +60 -0
- data/lib/inat/data/model.rb +551 -0
- data/lib/inat/data/query.rb +1145 -0
- data/lib/inat/data/sets/dataset.rb +104 -0
- data/lib/inat/data/sets/list.rb +190 -0
- data/lib/inat/data/sets/listers.rb +15 -0
- data/lib/inat/data/sets/wrappers.rb +137 -0
- data/lib/inat/data/types/extras.rb +88 -0
- data/lib/inat/data/types/location.rb +89 -0
- data/lib/inat/data/types/std.rb +293 -0
- data/lib/inat/report/table.rb +135 -0
- data/lib/inat/utils/deep.rb +30 -0
- metadata +137 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'extra/enum'
|
4
|
+
|
5
|
+
class MessageLevel < Enum
|
6
|
+
|
7
|
+
item :TRACE, -1
|
8
|
+
item :DEBUG, data: 0
|
9
|
+
item :INFO, data: 1
|
10
|
+
item :WARNING, data: 2
|
11
|
+
item :ERROR, data: 3
|
12
|
+
item :FATAL, data: 4
|
13
|
+
item :UNKNOWN, data: 5
|
14
|
+
|
15
|
+
item_alias :WARN => :WARNING
|
16
|
+
|
17
|
+
def severity
|
18
|
+
self.data
|
19
|
+
end
|
20
|
+
|
21
|
+
freeze
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShiftAge
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def parse source
|
8
|
+
source = source.to_i if /\d+/ === source
|
9
|
+
source = source.to_s.downcase.intern if Symbol === source || String === source
|
10
|
+
case source
|
11
|
+
when nil
|
12
|
+
0
|
13
|
+
when Integer
|
14
|
+
source
|
15
|
+
when :daily, :weekly, :monthly
|
16
|
+
source
|
17
|
+
else
|
18
|
+
raise TypeError, "Invalid shift age value: #{source}!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'extra/enum'
|
4
|
+
|
5
|
+
class UpdateMode < Enum
|
6
|
+
|
7
|
+
items :UPDATE,
|
8
|
+
:FORCE,
|
9
|
+
:RELOAD,
|
10
|
+
:MINIMAL,
|
11
|
+
:OFFLINE
|
12
|
+
|
13
|
+
item_alias :DEFAULT => :UPDATE,
|
14
|
+
:FORCE_UPDATE => :FORCE,
|
15
|
+
:FORCE_RELOAD => :RELOAD,
|
16
|
+
:SKIP_EXISTING => :MINIMAL,
|
17
|
+
:NO_UPDATE => :OFFLINE
|
18
|
+
|
19
|
+
freeze
|
20
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require_relative '../utils/deep'
|
7
|
+
require_relative '../../extra/period'
|
8
|
+
require_relative 'info'
|
9
|
+
require_relative 'config/messagelevel'
|
10
|
+
require_relative 'config/updatemode'
|
11
|
+
require_relative 'config/shiftage'
|
12
|
+
|
13
|
+
class Application
|
14
|
+
|
15
|
+
include AppInfo
|
16
|
+
|
17
|
+
API_DEFAULT = 'https://api.inaturalist.org/v1/'
|
18
|
+
DEFAULT_LOG = "./#{ NAME }.log"
|
19
|
+
|
20
|
+
DEFAULTS = {
|
21
|
+
threads: {
|
22
|
+
enable: true,
|
23
|
+
tasks: 3,
|
24
|
+
},
|
25
|
+
data: {
|
26
|
+
cache: true,
|
27
|
+
directory: File.expand_path("~/.cache/#{ NAME }/"),
|
28
|
+
update: UpdateMode::UPDATE,
|
29
|
+
update_period: Period::DAY,
|
30
|
+
outdate_period: Period::WEEK,
|
31
|
+
},
|
32
|
+
verbose: MessageLevel::ERROR,
|
33
|
+
log: {
|
34
|
+
enable: false,
|
35
|
+
file: DEFAULT_LOG,
|
36
|
+
level: MessageLevel::WARNING,
|
37
|
+
shift: {
|
38
|
+
age: nil,
|
39
|
+
size: nil,
|
40
|
+
},
|
41
|
+
},
|
42
|
+
api: {
|
43
|
+
root: API_DEFAULT,
|
44
|
+
locale: nil,
|
45
|
+
preferred_place_id: nil,
|
46
|
+
open_timeout: nil,
|
47
|
+
read_timeout: nil,
|
48
|
+
},
|
49
|
+
preamble: [],
|
50
|
+
}
|
51
|
+
|
52
|
+
CONFIG_FILE = File.expand_path "~/.config/#{ NAME }.yml"
|
53
|
+
|
54
|
+
private def parse_files files
|
55
|
+
files.map do |file|
|
56
|
+
if file.start_with?('@')
|
57
|
+
list = file[1..]
|
58
|
+
File.readlines(list, chomp: true).filter { |l| !l.empty? }
|
59
|
+
else
|
60
|
+
file
|
61
|
+
end
|
62
|
+
end.flatten
|
63
|
+
end
|
64
|
+
|
65
|
+
private def parse_args!
|
66
|
+
options = {}
|
67
|
+
opts = OptionParser::new USAGE do |o|
|
68
|
+
|
69
|
+
o.accept UpdateMode do |mode|
|
70
|
+
UpdateMode::parse mode, case_sensitive: false
|
71
|
+
end
|
72
|
+
|
73
|
+
o.accept MessageLevel do |level|
|
74
|
+
MessageLevel::parse level, case_sensitive: false
|
75
|
+
end
|
76
|
+
|
77
|
+
o.accept ShiftAge do |shift_age|
|
78
|
+
ShiftAge::parse shift_age
|
79
|
+
end
|
80
|
+
|
81
|
+
o.accept Period do |period|
|
82
|
+
Period::parse period
|
83
|
+
end
|
84
|
+
|
85
|
+
o.separator ''
|
86
|
+
o.separator "\e[1mInformation:\e[0m"
|
87
|
+
|
88
|
+
o.on '-h', '--help', 'Show help and exit.' do
|
89
|
+
puts ABOUT
|
90
|
+
puts
|
91
|
+
puts opts.help
|
92
|
+
exit 0
|
93
|
+
end
|
94
|
+
|
95
|
+
o.on '-?', '--usage', 'Show short help and exit.' do
|
96
|
+
puts opts.help
|
97
|
+
exit 0
|
98
|
+
end
|
99
|
+
|
100
|
+
o.on '--about', 'Show program info and exit.' do
|
101
|
+
puts ABOUT
|
102
|
+
exit 0
|
103
|
+
end
|
104
|
+
|
105
|
+
o.on '--version', 'Show version and exit.' do
|
106
|
+
puts VERSION
|
107
|
+
exit 0
|
108
|
+
end
|
109
|
+
|
110
|
+
o.separator ''
|
111
|
+
o.separator "\e[1mConfiguration:\e[0m"
|
112
|
+
|
113
|
+
o.on '-c', '--config FILE', String, "Configuration file instead standard (~/.config/#{ NAME }.yml)." do |value|
|
114
|
+
options[:config_file] = value
|
115
|
+
end
|
116
|
+
|
117
|
+
o.separator ''
|
118
|
+
o.separator "\e[1mFlow Control:\e[0m"
|
119
|
+
|
120
|
+
o.on '-1', '--no-threads', 'Disable threads.' do
|
121
|
+
options[:threads] ||= {}
|
122
|
+
options[:threads][:enable] = false
|
123
|
+
end
|
124
|
+
|
125
|
+
o.on '-t', '--tasks NUMBER', Integer, 'Limit number of concurrent tasks.' do |value|
|
126
|
+
options[:threads] ||= {}
|
127
|
+
options[:threads][:tasks] = value
|
128
|
+
end
|
129
|
+
|
130
|
+
o.separator ''
|
131
|
+
o.separator "\e[1mCache Control:\e[0m"
|
132
|
+
|
133
|
+
o.on '-u', '--update MODE', UpdateMode, "Update mode:", " 'default' (or 'update')",
|
134
|
+
" 'force-update' (or 'force')",
|
135
|
+
" 'force-reload' (or 'reload')",
|
136
|
+
" 'skip-existing' (or 'minimal')",
|
137
|
+
" 'no-update' (or 'offline')" do |value|
|
138
|
+
options[:data] ||= {}
|
139
|
+
options[:data][:update] = value
|
140
|
+
end
|
141
|
+
|
142
|
+
o.on '-f', '--force-update', 'Force update.' do
|
143
|
+
options[:data] ||= {}
|
144
|
+
options[:data][:update] = UpdateMode::FORCE
|
145
|
+
end
|
146
|
+
|
147
|
+
o.on '-F', '--force-reload', '--reload', 'Force reload.' do
|
148
|
+
options[:data] ||= {}
|
149
|
+
options[:data][:update] = UpdateMode::RELOAD
|
150
|
+
end
|
151
|
+
|
152
|
+
o.on '--skip-existing', 'Skip updating if exist.' do
|
153
|
+
options[:data] ||= {}
|
154
|
+
options[:data][:update] = UpdateMode::MINIMAL
|
155
|
+
end
|
156
|
+
|
157
|
+
o.on '-n', '--offline', '--no-update', 'No updating.' do
|
158
|
+
options[:data] ||= {}
|
159
|
+
options[:data][:update] = UpdateMode::OFFLINE
|
160
|
+
end
|
161
|
+
|
162
|
+
o.separator ''
|
163
|
+
|
164
|
+
o.on '--update-period PERIOD', Period, 'Update period.' do |value|
|
165
|
+
options[:data] ||= {}
|
166
|
+
options[:data][:update_period] = value
|
167
|
+
end
|
168
|
+
|
169
|
+
o.on '--obsolete-period PERIOD', Period, 'Obsolete period.' do |value|
|
170
|
+
options[:data] ||= {}
|
171
|
+
options[:data][:obsolete_period] = value
|
172
|
+
end
|
173
|
+
|
174
|
+
o.on '--cache-directory PATH', String, 'Cache directory path.' do |value|
|
175
|
+
options[:data] ||= {}
|
176
|
+
options[:data][:directory] = value
|
177
|
+
end
|
178
|
+
|
179
|
+
o.separator ''
|
180
|
+
|
181
|
+
o.on '--clean-requests', 'Clean outdated requests in data cache.' do
|
182
|
+
options[:preamble] << :clean_requests
|
183
|
+
end
|
184
|
+
|
185
|
+
o.on '--clean-observations', 'Clean observations without request in data cache.' do
|
186
|
+
options[:preamble] << :clean_observations
|
187
|
+
end
|
188
|
+
|
189
|
+
o.on '--clean-orphans', 'Clean orphan objects in data cache.' do
|
190
|
+
options[:preamble] << :clean_orphans
|
191
|
+
end
|
192
|
+
|
193
|
+
o.on '--clean-all', 'Clean data cache.' do
|
194
|
+
options[:preamble] << :clean_all
|
195
|
+
end
|
196
|
+
|
197
|
+
o.separator ''
|
198
|
+
o.separator "\e[1mAPI Parameters:\e[0m"
|
199
|
+
|
200
|
+
o.on '-A', '--api-root URI', String, 'API root' do |value|
|
201
|
+
options[:api] ||= {}
|
202
|
+
options[:api][:root] = value
|
203
|
+
end
|
204
|
+
|
205
|
+
o.on '-L', '--locale LOCALE', String, 'Set the locale.' do |value|
|
206
|
+
value = nil if value.downcase == 'none'
|
207
|
+
options[:api] ||= {}
|
208
|
+
options[:api][:locale] = value
|
209
|
+
end
|
210
|
+
|
211
|
+
o.on '-P', '--preferred-place ID', Integer, 'Preferred place for API.' do |value|
|
212
|
+
value = nil if value == 0
|
213
|
+
options[:api] ||= {}
|
214
|
+
options[:api][:preferred_place_id] = value
|
215
|
+
end
|
216
|
+
|
217
|
+
o.separator ''
|
218
|
+
o.separator "\e[1mLogging Control:\e[0m"
|
219
|
+
|
220
|
+
o.on '--verbose-level LEVEL', MessageLevel, 'Set the logging level for $STDERR.', " 'fatal'",
|
221
|
+
" 'error'",
|
222
|
+
" 'warn' (or 'warning')",
|
223
|
+
" 'info'",
|
224
|
+
" 'debug'" do |value|
|
225
|
+
options[:verbose] = value
|
226
|
+
end
|
227
|
+
|
228
|
+
o.on '-v', '--verbose', 'Set verbose level to INFO.' do
|
229
|
+
options[:verbose] = MessageLevel::INFO
|
230
|
+
end
|
231
|
+
|
232
|
+
o.on '--log-level LEVEL', MessageLevel, 'Set the logging level for file log' do |value|
|
233
|
+
options[:log] ||= {}
|
234
|
+
options[:log][:level] = value
|
235
|
+
end
|
236
|
+
|
237
|
+
o.on '--log-file FILE', String, 'File name for log.' do |value|
|
238
|
+
value = DEFAULT_LOG if /^default$/i === value
|
239
|
+
options[:log] ||= {}
|
240
|
+
options[:log][:file] = value
|
241
|
+
end
|
242
|
+
|
243
|
+
o.on '-l', '--log [FILE]', String, "Enable logging to file. If file is not specified './#{ NAME }.log' used.",
|
244
|
+
" 'none', 'no' or 'false' to disabling." do |value|
|
245
|
+
options[:log] ||= {}
|
246
|
+
case value
|
247
|
+
when nil, /^(true|yes|default)$/i
|
248
|
+
options[:log][:enable] = true
|
249
|
+
options[:log][:file] = DEFAULT_LOG
|
250
|
+
when /^(false|no|none)$/i
|
251
|
+
options[:log][:enable] = false
|
252
|
+
options[:log][:file] = DEFAULT_LOG
|
253
|
+
else
|
254
|
+
options[:log][:enable] = true
|
255
|
+
options[:log][:file] = value.to_s
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
o.on '--log-shift-age AGE', ShiftAge, 'Log file shift age.' do |value|
|
260
|
+
value = nil if value == 0
|
261
|
+
options[:log] ||= {}
|
262
|
+
options[:log][:shift] ||= {}
|
263
|
+
options[:log][:shift][:age] = value
|
264
|
+
end
|
265
|
+
|
266
|
+
o.on '--log-shift-size SIZE', Integer, 'Log file shift size.' do |value|
|
267
|
+
value = nil if value == 0
|
268
|
+
options[:log] ||= {}
|
269
|
+
options[:log][:shift] ||= {}
|
270
|
+
options[:log][:shift][:size] = value
|
271
|
+
end
|
272
|
+
|
273
|
+
o.separator ''
|
274
|
+
|
275
|
+
o.separator "\e[1mFile Arguments:\e[0m"
|
276
|
+
o.separator "\t‹task[, ...]›\t\t One or more names of task files.\n" +
|
277
|
+
"\t\t\t\t If name has not extension try to read '‹task›' than '‹task›.inat' than '‹task›.rb'."
|
278
|
+
|
279
|
+
end
|
280
|
+
files = parse_files(opts.parse(ARGV))
|
281
|
+
[ options, files ]
|
282
|
+
end
|
283
|
+
|
284
|
+
private def setup!
|
285
|
+
@config = DEFAULTS
|
286
|
+
options, @files = parse_args!
|
287
|
+
config_file = options[:config_file] || CONFIG_FILE
|
288
|
+
@config.deep_merge! YAML.load_file(config_file, symbolize_names: true, freeze: true) if File.exist?(config_file)
|
289
|
+
@config.deep_merge! options
|
290
|
+
@config[:verbose] = MessageLevel::parse @config[:verbose]
|
291
|
+
@config[:log][:level] = MessageLevel::parse @config[:log][:level]
|
292
|
+
end
|
293
|
+
|
294
|
+
attr_reader :config
|
295
|
+
|
296
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Globals
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def logger
|
8
|
+
@@logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def logger= value
|
12
|
+
@@logger = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_task
|
16
|
+
Thread.current.thread_variable_get 'current_task'
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_task= value
|
20
|
+
Thread.current.thread_variable_set 'current_task', value
|
21
|
+
end
|
22
|
+
|
23
|
+
def config
|
24
|
+
@@config
|
25
|
+
end
|
26
|
+
|
27
|
+
def config= value
|
28
|
+
@@config = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def status
|
32
|
+
@@status
|
33
|
+
end
|
34
|
+
|
35
|
+
def status= value
|
36
|
+
@@status = value
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
G = Globals
|
44
|
+
|
45
|
+
module LogDSL
|
46
|
+
|
47
|
+
def included mod
|
48
|
+
mod.extend LogDSL
|
49
|
+
end
|
50
|
+
|
51
|
+
def log message, level: MessageLevel::INFO
|
52
|
+
G.status.wrap do
|
53
|
+
G.logger.log(G.current_task&.name || '‹main›', level, message)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def error message, exception: nil
|
58
|
+
log message, level: MessageLevel::ERROR
|
59
|
+
raise exception, message if Class === exception && Exception >= exception
|
60
|
+
end
|
61
|
+
|
62
|
+
def warning message
|
63
|
+
log message, level: MessageLevel::WARNING
|
64
|
+
end
|
65
|
+
|
66
|
+
def info message
|
67
|
+
log message, level: MessageLevel::INFO
|
68
|
+
end
|
69
|
+
|
70
|
+
def debug message
|
71
|
+
log message, level: MessageLevel::DEBUG
|
72
|
+
end
|
73
|
+
|
74
|
+
def echo message, level: MessageLevel::UNKNOWN
|
75
|
+
log message, level: level
|
76
|
+
end
|
77
|
+
|
78
|
+
module_function :log, :error, :warning, :info, :debug, :echo
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
|
5
|
+
AUTHOR = 'Ivan Shikhalev'
|
6
|
+
EMAIL = 'shkikhalev@gmail.com'
|
7
|
+
|
8
|
+
VERSION = '0.8.0.11'
|
9
|
+
HOMEPAGE = 'https://github.com/shikhalev/inat-get'
|
10
|
+
SOURCE_URL = 'https://github.com/shikhalev/inat-get'
|
11
|
+
|
12
|
+
EXE = File.basename $0
|
13
|
+
NAME = File.basename $0, '.rb'
|
14
|
+
|
15
|
+
USAGE = "\e[1mUsage $\e[0m #{EXE} [options] ‹task[, ...]›"
|
16
|
+
ABOUT = "\e[1mINat::Get v#{VERSION}\e[0m — A toolset for fetching and processing data from \e[4miNaturalist.org\e[0m.\n" +
|
17
|
+
"\n" +
|
18
|
+
"\e[1mSource:\e[0m \e[4m#{HOMEPAGE}\e[0m\n" +
|
19
|
+
"\e[1mAuthor:\e[0m #{AUTHOR} ‹\e[4m#{EMAIL}\e[0m›"
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
class DualLogger
|
6
|
+
|
7
|
+
def config
|
8
|
+
@application.config
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize application
|
12
|
+
@application = application
|
13
|
+
v_para = {
|
14
|
+
level: config[:verbose].severity
|
15
|
+
}
|
16
|
+
@verb = Logger::new STDERR, **v_para
|
17
|
+
if config[:log][:enable]
|
18
|
+
f_para = {
|
19
|
+
level: config[:log][:level].severity,
|
20
|
+
}
|
21
|
+
f_para[:shift_age] = config[:log][:shift][:age] if config[:log][:shift][:age]
|
22
|
+
f_para[:shift_level] = config[:log][:shift][:level] if config[:log][:shift][:level]
|
23
|
+
@file = Logger::new config[:log][:file], **f_para
|
24
|
+
else
|
25
|
+
@file = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def log task, level, message
|
30
|
+
task = task.name if task.respond_to?(:name)
|
31
|
+
@verb.log(level.severity, message, task)
|
32
|
+
@file.log(level.severity, message, task) if @file
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppPreamble
|
4
|
+
|
5
|
+
private def do_clean_requests
|
6
|
+
# NEED: implement
|
7
|
+
end
|
8
|
+
|
9
|
+
private def do_clean_observations
|
10
|
+
# NEED: implement
|
11
|
+
end
|
12
|
+
|
13
|
+
private def do_clean_orphans
|
14
|
+
# NEED: implement
|
15
|
+
end
|
16
|
+
|
17
|
+
private def do_clean_all
|
18
|
+
# NEED: implement
|
19
|
+
end
|
20
|
+
|
21
|
+
private def preamble!
|
22
|
+
config[:preamble].each do |name|
|
23
|
+
self.send "do_#{name}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'globals'
|
4
|
+
|
5
|
+
# TODO: переделать на отдельный поток и очередь статусов. Ввести отдельно ключ, как идентификатор запроса, например.
|
6
|
+
|
7
|
+
module Status
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
private def ellipsis src, len
|
12
|
+
return '' unless String === src
|
13
|
+
if src.length <= len
|
14
|
+
src
|
15
|
+
else
|
16
|
+
src[.. len - 4] + '...'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# private def get_num
|
21
|
+
# @num ||= 0
|
22
|
+
# @num += 1
|
23
|
+
# @num
|
24
|
+
# end
|
25
|
+
|
26
|
+
def init
|
27
|
+
@lines = {}
|
28
|
+
@mutex = Mutex::new
|
29
|
+
@shift = 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def up
|
33
|
+
$stderr.print "\e[#{ @shift }A\r" if @shift > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def out
|
37
|
+
$stderr.print "\e[0K\n"
|
38
|
+
@lines.each do |key, value|
|
39
|
+
$stderr.print "#{ value }\e[0K\n"
|
40
|
+
end
|
41
|
+
$stderr.print "\e[0K\n"
|
42
|
+
@shift = @lines.size + 2
|
43
|
+
end
|
44
|
+
|
45
|
+
def wrap
|
46
|
+
@mutex.synchronize do
|
47
|
+
up
|
48
|
+
yield
|
49
|
+
out
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def status key, value
|
54
|
+
task = ellipsis G.current_task&.name, 16
|
55
|
+
key = task if key == nil
|
56
|
+
line = format("%16s | %s", key.to_s, value)
|
57
|
+
@mutex.synchronize do
|
58
|
+
@lines ||= {}
|
59
|
+
@lines[key] = line
|
60
|
+
up
|
61
|
+
out
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
private def status str
|
68
|
+
self.class.status str
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
Status::init
|
74
|
+
G.status = Status
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'dsl'
|
4
|
+
require_relative '../../report/table'
|
5
|
+
|
6
|
+
class Task::Context
|
7
|
+
|
8
|
+
include Task::DSL
|
9
|
+
include TableDSL
|
10
|
+
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
def config
|
14
|
+
@task.config
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger
|
18
|
+
@task.logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def done?
|
22
|
+
@done
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize task, name, path
|
26
|
+
@task = task
|
27
|
+
@name = name
|
28
|
+
@done = false
|
29
|
+
source_code = File.readlines(path).map { |line| " #{line}" }.join('')
|
30
|
+
full_code = "define_singleton_method :execute do\n" +
|
31
|
+
" begin\n" +
|
32
|
+
" #{source_code}\n" +
|
33
|
+
" Status::status nil, 'DONE'\n" +
|
34
|
+
" rescue Exception => e\n" +
|
35
|
+
" error \"\#{e.inspect}\"\n" +
|
36
|
+
" error \"\#{e.backtrace.join(\"\\n\\t\")}\"\n" +
|
37
|
+
" Status::status nil, 'ERROR : ' + e.inspect\n" +
|
38
|
+
" rescue\n" +
|
39
|
+
" error 'Unknown error!'\n" +
|
40
|
+
" Status::status nil, 'ERROR'\n" +
|
41
|
+
" end\n" +
|
42
|
+
" @done = true\n" +
|
43
|
+
"end"
|
44
|
+
self.instance_eval full_code
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../globals'
|
4
|
+
require_relative '../config/messagelevel'
|
5
|
+
|
6
|
+
require_relative '../../data/entity/observation'
|
7
|
+
require_relative '../../data/query'
|
8
|
+
require_relative '../../data/sets/dataset'
|
9
|
+
require_relative '../../data/sets/list'
|
10
|
+
|
11
|
+
class Task; end
|
12
|
+
|
13
|
+
module Task::DSL
|
14
|
+
|
15
|
+
include LogDSL
|
16
|
+
|
17
|
+
def select **params
|
18
|
+
query = Query::new(**params)
|
19
|
+
DataSet::new nil, query.observations
|
20
|
+
end
|
21
|
+
|
22
|
+
module_function :select
|
23
|
+
|
24
|
+
end
|