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.
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