inat-get 0.8.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +16 -0
  4. data/Rakefile +4 -0
  5. data/bin/inat-get +59 -0
  6. data/docs/logo.png +0 -0
  7. data/inat-get.gemspec +33 -0
  8. data/lib/extra/enum.rb +184 -0
  9. data/lib/extra/period.rb +252 -0
  10. data/lib/extra/uuid.rb +90 -0
  11. data/lib/inat/app/application.rb +50 -0
  12. data/lib/inat/app/config/messagelevel.rb +22 -0
  13. data/lib/inat/app/config/shiftage.rb +24 -0
  14. data/lib/inat/app/config/updatemode.rb +20 -0
  15. data/lib/inat/app/config.rb +296 -0
  16. data/lib/inat/app/globals.rb +80 -0
  17. data/lib/inat/app/info.rb +21 -0
  18. data/lib/inat/app/logging.rb +35 -0
  19. data/lib/inat/app/preamble.rb +27 -0
  20. data/lib/inat/app/status.rb +74 -0
  21. data/lib/inat/app/task/context.rb +47 -0
  22. data/lib/inat/app/task/dsl.rb +24 -0
  23. data/lib/inat/app/task.rb +75 -0
  24. data/lib/inat/data/api.rb +218 -0
  25. data/lib/inat/data/cache.rb +9 -0
  26. data/lib/inat/data/db.rb +87 -0
  27. data/lib/inat/data/ddl.rb +18 -0
  28. data/lib/inat/data/entity/comment.rb +29 -0
  29. data/lib/inat/data/entity/flag.rb +22 -0
  30. data/lib/inat/data/entity/identification.rb +45 -0
  31. data/lib/inat/data/entity/observation.rb +172 -0
  32. data/lib/inat/data/entity/observationphoto.rb +25 -0
  33. data/lib/inat/data/entity/observationsound.rb +26 -0
  34. data/lib/inat/data/entity/photo.rb +31 -0
  35. data/lib/inat/data/entity/place.rb +57 -0
  36. data/lib/inat/data/entity/project.rb +94 -0
  37. data/lib/inat/data/entity/projectadmin.rb +21 -0
  38. data/lib/inat/data/entity/projectobservationrule.rb +50 -0
  39. data/lib/inat/data/entity/request.rb +58 -0
  40. data/lib/inat/data/entity/sound.rb +27 -0
  41. data/lib/inat/data/entity/taxon.rb +94 -0
  42. data/lib/inat/data/entity/user.rb +67 -0
  43. data/lib/inat/data/entity/vote.rb +22 -0
  44. data/lib/inat/data/entity.rb +291 -0
  45. data/lib/inat/data/enums/conservationstatus.rb +30 -0
  46. data/lib/inat/data/enums/geoprivacy.rb +14 -0
  47. data/lib/inat/data/enums/iconictaxa.rb +23 -0
  48. data/lib/inat/data/enums/identificationcategory.rb +13 -0
  49. data/lib/inat/data/enums/licensecode.rb +16 -0
  50. data/lib/inat/data/enums/projectadminrole.rb +11 -0
  51. data/lib/inat/data/enums/projecttype.rb +37 -0
  52. data/lib/inat/data/enums/qualitygrade.rb +12 -0
  53. data/lib/inat/data/enums/rank.rb +60 -0
  54. data/lib/inat/data/model.rb +551 -0
  55. data/lib/inat/data/query.rb +1145 -0
  56. data/lib/inat/data/sets/dataset.rb +104 -0
  57. data/lib/inat/data/sets/list.rb +190 -0
  58. data/lib/inat/data/sets/listers.rb +15 -0
  59. data/lib/inat/data/sets/wrappers.rb +137 -0
  60. data/lib/inat/data/types/extras.rb +88 -0
  61. data/lib/inat/data/types/location.rb +89 -0
  62. data/lib/inat/data/types/std.rb +293 -0
  63. data/lib/inat/report/table.rb +135 -0
  64. data/lib/inat/utils/deep.rb +30 -0
  65. metadata +137 -0
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'listers'
4
+ require_relative 'list'
5
+
6
+ class DataSet
7
+
8
+ attr_reader :time
9
+ attr_reader :object
10
+ attr_reader :observations
11
+
12
+ def initialize object, observations, time: Time::new
13
+ @object = object
14
+ @time = time
15
+ @observations = observations.sort_by { |o| o.sort_key }.uniq
16
+ @sorted = true
17
+ @by_id = {}
18
+ @observations.each do |o|
19
+ @by_id[o.id] = o
20
+ end
21
+ end
22
+
23
+ def self.zero
24
+ new nil, []
25
+ end
26
+
27
+ include Enumerable
28
+
29
+ def each
30
+ if block_given?
31
+ @observations.sort_by! { |o| o.sort_key } unless @sorted
32
+ @sorted = true
33
+ @observations.each do |o|
34
+ yield o
35
+ end
36
+ else
37
+ to_enum :each
38
+ end
39
+ end
40
+
41
+ def reverse_each
42
+ if block_given?
43
+ @observations.sort_by! { |o| o.sort_key } unless @sorted
44
+ @sorted = true
45
+ @observations.reverse_each do |o|
46
+ yield o
47
+ end
48
+ else
49
+ to_enum :reverse_each
50
+ end
51
+ end
52
+
53
+ def count
54
+ @observations.size
55
+ end
56
+
57
+ def to_list lister = Listers::SPECIES, sorter: nil
58
+ List::new self, lister, sorter: sorter, time: @time
59
+ end
60
+
61
+ def include? observation
62
+ @by_id.has_key? observation.id
63
+ end
64
+
65
+ def empty?
66
+ @observations.empty?
67
+ end
68
+
69
+ def where &block
70
+ raise ArgumentError, "Block must be provided!", caller unless block_given?
71
+ DataSet::new nil, @observations.select(&block), time: @time
72
+ end
73
+
74
+ alias :=== :include?
75
+
76
+ def << observation
77
+ raise TypeError, "Argument must be an Observation (#{ observation.inspect })!" unless Observation === observation
78
+ if !self.include?(observation)
79
+ @observations << observation
80
+ @by_id[observation.id] = observation
81
+ @sorted = false
82
+ end
83
+ end
84
+
85
+ def | other
86
+ obj = @object == other.object ? @object : nil
87
+ DataSet::new obj, @observations + other.observations, time: Time::new
88
+ end
89
+
90
+ def & other
91
+ obj = @object == other.object ? @object : nil
92
+ DataSet::new obj, @observations.select { |o| other.include?(o) }, time: Time::new
93
+ end
94
+
95
+ def - other
96
+ obj = @object == other.object ? @object : nil
97
+ DataSet::new obj, @observations.select { |o| !other.include?(o) }, time: Time::new
98
+ end
99
+
100
+ def to_a
101
+ @observations.dup
102
+ end
103
+
104
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ autoload :DataSet, 'inat/data/sets/dataset'
4
+
5
+ class List
6
+
7
+ attr_reader :lister, :sorter
8
+
9
+ DEFAULT_SORTER = lambda { |ds| ds.object.respond_to?(:sort_key) ? ds.object.sort_key : ds.object }
10
+
11
+ def initialize source, lister, sorter: nil, time: Time::new
12
+ @lister = lister
13
+ @sorter = sorter || DEFAULT_SORTER
14
+ @time = time
15
+ @data = {}
16
+ source.each do |observation|
17
+ key = @lister.call observation
18
+ if key != nil
19
+ @data[key] ||= DataSet::new key, [], time: @time
20
+ @data[key] << observation
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.zero lister = Listers::SPECIES
26
+ new [], lister
27
+ end
28
+
29
+ include Enumerable
30
+
31
+ def [] key
32
+ @data[key]
33
+ end
34
+
35
+ def each
36
+ if block_given?
37
+ @data.values.sort_by { |ds| @sorter.call(ds) }.each do |ds|
38
+ yield ds
39
+ end
40
+ else
41
+ to_enum :each
42
+ end
43
+ end
44
+
45
+ def to_dataset
46
+ observations = []
47
+ @data.values.each do |ds|
48
+ observations += ds.observations
49
+ end
50
+ DataSet::new nil, observations, time: @time
51
+ end
52
+
53
+ def include? key
54
+ @data.has_key? key
55
+ end
56
+
57
+ def empty?
58
+ @data.empty?
59
+ end
60
+
61
+ def count
62
+ @data.size
63
+ end
64
+
65
+ def observation_count
66
+ @data.values.map { |ds| ds.count }.sum
67
+ end
68
+
69
+ def where &block
70
+ raise ArgumentError, "Block must be provided!", caller unless block_given?
71
+ result = List::new [], @lister, sorter: @sorter, time: @time
72
+ self.each do |ds|
73
+ result << ds if yield(ds)
74
+ end
75
+ result
76
+ end
77
+
78
+ def << some
79
+ case some
80
+ when Observation
81
+ key = @lister.call some
82
+ if key != nil
83
+ @data[key] ||= DataSet::new key, [], time: @time
84
+ @data[key] << some
85
+ end
86
+ when DataSet
87
+ if !some.empty?
88
+ valid = some.object != nil && some.all? { |o| @lister.call(o) == some.object }
89
+ if valid
90
+ if @data[some.object]
91
+ @data[some.object] = @data[some.object] | some
92
+ else
93
+ @data[some.object] = some
94
+ end
95
+ else
96
+ some.each do |o|
97
+ self << o
98
+ end
99
+ end
100
+ end
101
+ when Array
102
+ some.each do |o|
103
+ self << o
104
+ end
105
+ else
106
+ raise TypeError, "Can not add this object: #{ some.inspect }!", caller
107
+ end
108
+ end
109
+
110
+ alias :=== :include?
111
+
112
+ def cover? other
113
+ other.any? { |ds| self.include?(ds.object) }
114
+ end
115
+
116
+ include Comparable
117
+
118
+ def <=> other
119
+ return nil unless List === other
120
+ if self.cover?(other)
121
+ if other.cover?(self)
122
+ return 0
123
+ else
124
+ return 1
125
+ end
126
+ else
127
+ if other.cover?(self)
128
+ return -1
129
+ else
130
+ return nil
131
+ end
132
+ end
133
+ end
134
+
135
+ def apply! *data
136
+ @lister, @sorter, @time, @data = data
137
+ self
138
+ end
139
+
140
+ def clone
141
+ List::zero.apply! @lister, @sorter, @time, @data.dup
142
+ end
143
+
144
+ def add! other
145
+ other.each do |ds|
146
+ self << ds
147
+ end
148
+ self
149
+ end
150
+
151
+ def mul! other
152
+ @data.delete_if { | key, _ | !other.include?(key) }
153
+ other.each do |ds|
154
+ if self.include?(ds.object)
155
+ self << ds
156
+ end
157
+ end
158
+ self
159
+ end
160
+
161
+ def sub! other
162
+ @data.delete_if { | key, _ | other.include?(key) }
163
+ self
164
+ end
165
+
166
+ def add other
167
+ clone.add! other
168
+ end
169
+
170
+ def mul other
171
+ # А вот здесь будет эффективней старое решение
172
+ result = List::new [], @lister, sorter: @sorter, time: @time
173
+ @data.each do |key, ds|
174
+ if other.include?(key)
175
+ summa = ds | other[key]
176
+ self << summa
177
+ end
178
+ end
179
+ result
180
+ end
181
+
182
+ def sub other
183
+ clone.sub! other
184
+ end
185
+
186
+ alias :+ :add
187
+ alias :* :mul
188
+ alias :- :sub
189
+
190
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'wrappers'
4
+
5
+ module Listers
6
+
7
+ SPECIES = lambda { |o| o.normalized_taxon(Rank::COMPLEX .. Rank::HYBRID) }
8
+ GENUS = lambda { |o| o.normalized_taxon(Rank::GENUS) }
9
+ FAMILY = lambda { |o| o.normalized_taxon(Rank::FAMILY) }
10
+ YEAR = lambda { |o| Year[o.observed_on] }
11
+ MONTH = lambda { |o| Month[o.observed_on] }
12
+ DAY = lambda { |o| Day[o.observed_on] }
13
+ USER = lambda { |o| o.user }
14
+
15
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ class Year
6
+
7
+ class << self
8
+
9
+ private :new
10
+
11
+ def [] source
12
+ return nil if source == nil
13
+ return source if Year === source
14
+ year = case source
15
+ when Date, Time
16
+ source.year
17
+ when String
18
+ date = Date::parse source
19
+ date.year
20
+ when Integer
21
+ source
22
+ else
23
+ raise TypeError, "Invalid year key: #{ source.inspect }!", caller
24
+ end
25
+ @years ||= {}
26
+ @years[year] ||= new year
27
+ @years[year]
28
+ end
29
+
30
+ end
31
+
32
+ attr_reader :year
33
+
34
+ def initialize year
35
+ @year = year
36
+ end
37
+
38
+ include Comparable
39
+
40
+ def <=> other
41
+ return nil unless Year === other
42
+ @year <=> other.year
43
+ end
44
+
45
+ def - num
46
+ self.class[@year - num]
47
+ end
48
+
49
+ def to_s
50
+ "<i class=\"glyphicon glyphicon-calendar\"></i>  #{ @year }"
51
+ end
52
+
53
+ end
54
+
55
+ class Month
56
+
57
+ class << self
58
+
59
+ private :new
60
+
61
+ def [] source
62
+ return nil if source == nil
63
+ return source if Month === source
64
+ month = case source
65
+ when Date, Time
66
+ source.month
67
+ when String
68
+ date = Date::parse source
69
+ date.month
70
+ when Integer
71
+ source
72
+ else
73
+ raise TypeError, "Invalid month key: #{ source.inspect }!", caller
74
+ end
75
+ @months ||= {}
76
+ @months[month] ||= new month
77
+ @months[month]
78
+ end
79
+
80
+ end
81
+
82
+ attr_reader :month
83
+
84
+ def initialize month
85
+ @month = month
86
+ end
87
+
88
+ include Comparable
89
+
90
+ def <=> other
91
+ return nil unless Month === other
92
+ @month <=> other.month
93
+ end
94
+
95
+ end
96
+
97
+ class Day
98
+
99
+ class << self
100
+
101
+ attr_reader :day
102
+
103
+ def [] source
104
+ return nil if source == nil
105
+ return source if Day === source
106
+ day = case source
107
+ when Date, Time
108
+ source.day
109
+ when String
110
+ date = Date::parse source
111
+ date.day
112
+ when Integer
113
+ source
114
+ else
115
+ raise TypeError, "Invalid day key: #{ source.inspect }!", caller
116
+ end
117
+ @days ||= {}
118
+ @days[day] ||= new day
119
+ @days[day]
120
+ end
121
+
122
+ end
123
+
124
+ attr_reader :day
125
+
126
+ def initialize day
127
+ @day = day
128
+ end
129
+
130
+ include Comparable
131
+
132
+ def <=> other
133
+ return nil unless Day === other
134
+ @day <=> other.day
135
+ end
136
+
137
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'extra/enum'
4
+ require 'extra/uuid'
5
+
6
+ class Enum
7
+
8
+ class << self
9
+
10
+ pre_verbose = $VERBOSE
11
+ $VERBOSE = nil
12
+
13
+ alias :old_parse :parse
14
+
15
+ def parse src
16
+ return nil if src == nil
17
+ return src if self === src
18
+ case src
19
+ when Integer, Symbol
20
+ self[src]
21
+ when String
22
+ self.old_parse src, case_sensitive: false
23
+ end
24
+ end
25
+
26
+ $VERBOSE = pre_verbose
27
+
28
+ def ddl
29
+ if any? { |v| Integer === v.data }
30
+ {
31
+ name: :TEXT,
32
+ data: :INTEGER
33
+ }
34
+ else
35
+ :TEXT
36
+ end
37
+ end
38
+
39
+ def from_db src
40
+ return nil if src == nil
41
+ return self[src.intern] if String === src || Symbol === src
42
+ name = src[:name]&.intern
43
+ return nil if name == nil
44
+ self[name]
45
+ end
46
+
47
+ end
48
+
49
+ def to_db
50
+ if self.class.any? { |v| Integer === v.data }
51
+ {
52
+ name: name.to_s,
53
+ data: data
54
+ }
55
+ else
56
+ name.to_s
57
+ end
58
+ end
59
+
60
+ def to_query
61
+ to_s
62
+ end
63
+
64
+ end
65
+
66
+ class UUID
67
+
68
+ class << self
69
+
70
+ def ddl
71
+ :TEXT
72
+ end
73
+
74
+ def from_db src
75
+ parse src
76
+ end
77
+
78
+ end
79
+
80
+ def to_db
81
+ to_s
82
+ end
83
+
84
+ def to_query
85
+ to_s
86
+ end
87
+
88
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Location
4
+
5
+ class << self
6
+
7
+ def parse src
8
+ case src
9
+ when nil
10
+ nil
11
+ when String
12
+ parse src.split(',').map(&:to_f)
13
+ when Array
14
+ new(src[0], src[1]).freeze
15
+ when Hash
16
+ latitude = src[:latitude] || src['latitude']
17
+ longitude = src[:longitude] || src['longitude']
18
+ return nil if latitude == nil && longitude == nil
19
+ new(latitude, longitude).freeze
20
+ else
21
+ raise ArgumentError, "Source must be a String or Array or Hash!", caller
22
+ end
23
+ end
24
+
25
+ def from_db src
26
+ new(src[:latitude] || src['latitude'], src[:longitude] || src['longitude']).freeze
27
+ end
28
+
29
+ def ddl
30
+ {
31
+ latitude: :REAL,
32
+ longitude: :REAL
33
+ }
34
+ end
35
+
36
+ end
37
+
38
+ attr_reader :latitude, :longitude
39
+
40
+ def initialize latitude, longitude
41
+ raise ArgumentError, "Latitude must be a Numeric!", caller unless Numeric === latitude || latitude == nil
42
+ raise ArgumentError, "Longitude must be a Numeric!", caller unless Numeric === longitude || latitude == nil
43
+ @latitude = latitude
44
+ @longitude = longitude
45
+ end
46
+
47
+ def to_db
48
+ {
49
+ latitude: latitude,
50
+ longitude: longitude
51
+ }
52
+ end
53
+
54
+ end
55
+
56
+ class Radius
57
+
58
+ attr_reader :latitude, :longitude, :radius
59
+
60
+ def initialize latitude, longitude, radius
61
+ raise ArgumentError, "Latitude must be a Numeric!", caller unless Numeric === latitude
62
+ raise ArgumentError, "Longitude must be a Numeric!", caller unless Numeric === longitude
63
+ raise ArgumentError, "Radius must be a Numeric!", caller unless Numeric === radius || radius == nil
64
+ @latitude, @longitude, @radius = latitude, longitude, radius
65
+ end
66
+
67
+ end
68
+
69
+ class Sector
70
+
71
+ attr_reader :north, :east, :south, :west
72
+
73
+ def initialize north, east, south, west
74
+ raise ArgumentError, "North must be a Numeric!", caller unless Numeric === north
75
+ raise ArgumentError, "East must be a Numeric!", caller unless Numeric === east
76
+ raise ArgumentError, "South must be a Numeric!", caller unless Numeric === south
77
+ raise ArgumentError, "West must be a Numeric!", caller unless Numeric === west
78
+ @north, @east, @south, @west = north, east, south, west
79
+ end
80
+
81
+ end
82
+
83
+ def radius latitude, longitude, radius
84
+ Radius::new latitude, longitude, radius
85
+ end
86
+
87
+ def sector north, east, south, west
88
+ Sector::new north, east, south, west
89
+ end