inat-get 0.8.0.11 → 0.8.0.13
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/.yardopts +6 -0
- data/bin/inat-get +1 -1
- data/inat-get.gemspec +6 -6
- data/lib/extra/enum.rb +4 -0
- data/lib/extra/period.rb +15 -0
- data/lib/inat/app/application.rb +4 -3
- data/lib/inat/app/config/messagelevel.rb +3 -1
- data/lib/inat/app/config/shiftage.rb +1 -1
- data/lib/inat/app/config/updatemode.rb +1 -1
- data/lib/inat/app/config.rb +6 -2
- data/lib/inat/app/globals.rb +6 -3
- data/lib/inat/app/info.rb +18 -13
- data/lib/inat/app/logging.rb +3 -3
- data/lib/inat/app/preamble.rb +1 -1
- data/lib/inat/app/status.rb +3 -9
- data/lib/inat/app/task/context.rb +9 -3
- data/lib/inat/app/task/dsl.rb +5 -3
- data/lib/inat/app/task.rb +2 -2
- data/lib/inat/data/api.rb +210 -181
- data/lib/inat/data/db.rb +9 -4
- data/lib/inat/data/ddl.rb +24 -5
- data/lib/inat/data/entity/comment.rb +8 -4
- data/lib/inat/data/entity/flag.rb +7 -3
- data/lib/inat/data/entity/identification.rb +9 -4
- data/lib/inat/data/entity/observation.rb +27 -14
- data/lib/inat/data/entity/observationphoto.rb +7 -3
- data/lib/inat/data/entity/observationsound.rb +6 -7
- data/lib/inat/data/entity/photo.rb +9 -3
- data/lib/inat/data/entity/place.rb +11 -2
- data/lib/inat/data/entity/project.rb +16 -10
- data/lib/inat/data/entity/projectadmin.rb +4 -4
- data/lib/inat/data/entity/projectobservationrule.rb +3 -4
- data/lib/inat/data/entity/request.rb +7 -3
- data/lib/inat/data/entity/sound.rb +7 -2
- data/lib/inat/data/entity/taxon.rb +11 -3
- data/lib/inat/data/entity/user.rb +7 -3
- data/lib/inat/data/entity/vote.rb +7 -3
- data/lib/inat/data/entity.rb +38 -24
- data/lib/inat/data/enums/conservationstatus.rb +3 -3
- data/lib/inat/data/enums/geoprivacy.rb +3 -1
- data/lib/inat/data/enums/iconictaxa.rb +1 -1
- data/lib/inat/data/enums/identificationcategory.rb +1 -1
- data/lib/inat/data/enums/licensecode.rb +5 -2
- data/lib/inat/data/enums/projectadminrole.rb +1 -1
- data/lib/inat/data/enums/projecttype.rb +5 -3
- data/lib/inat/data/enums/qualitygrade.rb +1 -1
- data/lib/inat/data/enums/rank.rb +1 -1
- data/lib/inat/data/model.rb +73 -24
- data/lib/inat/data/query.rb +14 -9
- data/lib/inat/data/sets/dataset.rb +10 -6
- data/lib/inat/data/sets/list.rb +8 -3
- data/lib/inat/data/sets/listers.rb +10 -4
- data/lib/inat/data/sets/wrappers.rb +111 -82
- data/lib/inat/data/types/location.rb +17 -8
- data/lib/inat/data/types/std.rb +171 -176
- data/lib/inat/report/report_dsl.rb +205 -0
- data/lib/inat/report/table.rb +11 -3
- data/lib/inat/utils/deep.rb +25 -19
- metadata +6 -5
- data/lib/inat/data/cache.rb +0 -9
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'table'
|
4
|
+
|
5
|
+
module INat::Report::DSL
|
6
|
+
|
7
|
+
include INat
|
8
|
+
include INat::Report
|
9
|
+
include INat::Report::Table::DSL
|
10
|
+
|
11
|
+
private def class_title object, is_observer: true
|
12
|
+
case object
|
13
|
+
when Entity::Taxon
|
14
|
+
'Таксон'
|
15
|
+
when Entity::Place
|
16
|
+
'Территория'
|
17
|
+
when Entity::Project
|
18
|
+
'Проект'
|
19
|
+
when Entity::User
|
20
|
+
if is_observer
|
21
|
+
'Наблюдатель'
|
22
|
+
else
|
23
|
+
'Пользователь'
|
24
|
+
end
|
25
|
+
when Report::Period
|
26
|
+
'Период'
|
27
|
+
else
|
28
|
+
'Объект'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def species_table list, observers: false, details: true
|
33
|
+
raise ArgumentError, 'Unconsistent flags!' if observers && !details
|
34
|
+
object_title = class_title list.first.object
|
35
|
+
species = table do
|
36
|
+
column '#', width: 3, align: :right, data: :line_no
|
37
|
+
column object_title, data: :object
|
38
|
+
if details
|
39
|
+
column 'Наблюдения', data: :observations
|
40
|
+
else
|
41
|
+
column 'Наблюдения', data: :observations, width: 6, align: :right
|
42
|
+
end
|
43
|
+
end
|
44
|
+
users = nil
|
45
|
+
user_rows = []
|
46
|
+
if observers
|
47
|
+
@@prefix ||= 0
|
48
|
+
@@prefix += 1
|
49
|
+
users = table do
|
50
|
+
column '#', width: 3, align: :right, data: :line_no, marker: true
|
51
|
+
column 'Наблюдатель', data: :user
|
52
|
+
column 'Виды', width: 6, align: :right, data: :species
|
53
|
+
column 'Наблюдения', width: 6, align: :right, data: :observations
|
54
|
+
end
|
55
|
+
by_user = list.to_dataset.to_list Listers::USER
|
56
|
+
by_user.each do |ds|
|
57
|
+
ls = ds.to_list Listers::SPECIES
|
58
|
+
user = ds.object
|
59
|
+
user_rows << { user: user, anchor: "#{ @@prefix }-user-#{ user.id }", species: ls.count, observations: ds.count }
|
60
|
+
end
|
61
|
+
user_rows.sort_by! { |row| -row[:species] }
|
62
|
+
users << user_rows
|
63
|
+
end
|
64
|
+
species_rows = []
|
65
|
+
list.each do |ds|
|
66
|
+
observations = if details
|
67
|
+
if observers
|
68
|
+
ds.observations.map { |o| "#{ o }<sup><a href=\"\##{ @@prefix }-user-#{ o.user.id }\">#{ user_rows.index { |row| row[:user] == o.user } + 1 }</a></sup>" }
|
69
|
+
else
|
70
|
+
ds.observations.map(&:to_s)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
[ ds.count.to_s ]
|
74
|
+
end
|
75
|
+
species_rows << { object: ds.object, observations: observations.join(', ') }
|
76
|
+
end
|
77
|
+
species << species_rows
|
78
|
+
if observers
|
79
|
+
[ species, users ]
|
80
|
+
else
|
81
|
+
species
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
LAST_STYLE = 'font-size:110%;'
|
86
|
+
SUMMARY_STYLE = 'font-weight:bold;'
|
87
|
+
|
88
|
+
def history_table list, news: true, summary: true, base_link: nil, last: nil, extras: false, last_style: LAST_STYLE, summary_style: SUMMARY_STYLE
|
89
|
+
object_title = class_title list.first.object, is_observer: false
|
90
|
+
history = table do
|
91
|
+
column '#', width: 3, align: :right, data: :line_no
|
92
|
+
column object_title, data: :object
|
93
|
+
column 'Наблюдения', width: 6, align: :right, data: :observations
|
94
|
+
column 'Виды', width: 6, align: :right, data: :species
|
95
|
+
if news
|
96
|
+
column 'Новые', width: 6, align: :right, data: :news
|
97
|
+
end
|
98
|
+
end
|
99
|
+
history_rows = []
|
100
|
+
last_object = nil
|
101
|
+
last_ds = nil
|
102
|
+
last_ls = nil
|
103
|
+
delta = nil
|
104
|
+
if news || summary
|
105
|
+
base_ls = List::zero Listers::SPECIES
|
106
|
+
end
|
107
|
+
list.each do |ds|
|
108
|
+
last_object = ds.object
|
109
|
+
last_ds = ds
|
110
|
+
last_ls = ds.to_list Listers::SPECIES
|
111
|
+
row = { observations: last_ds.count, species: last_ls.count }
|
112
|
+
if base_link && last_object.respond_to?(:query_params)
|
113
|
+
link = base_link + '&' + last_object.query_params
|
114
|
+
row[:object] = "<a href=\"#{ link }\">#{ last_object }</a>"
|
115
|
+
else
|
116
|
+
row[:object] = last_object
|
117
|
+
end
|
118
|
+
if news || summary
|
119
|
+
delta = last_ls - base_ls
|
120
|
+
base_ls += last_ls
|
121
|
+
row[:news] = delta.count
|
122
|
+
end
|
123
|
+
history_rows << row
|
124
|
+
end
|
125
|
+
if last && last != last_object
|
126
|
+
row = { object: last, observations: 0, species: 0 }
|
127
|
+
if news
|
128
|
+
row[:news] = 0
|
129
|
+
delta = List::zero Listers::SPECIES
|
130
|
+
last_object = last
|
131
|
+
last_ds = DataSet::zero
|
132
|
+
last_ls = List::zero Listers::SPECIES
|
133
|
+
end
|
134
|
+
history_rows << row
|
135
|
+
end
|
136
|
+
history_rows.last[:style] = last_style if last_style
|
137
|
+
if summary
|
138
|
+
# Почему base_ls, а не list?
|
139
|
+
# 1. list в некоторых случаях может быть не List, а просто массив DataSet с заполненным object.
|
140
|
+
# 2. Даже в базовом случае list сформирован не по видам.
|
141
|
+
row = { line_no: '', observations: base_ls.observation_count, species: base_ls.count }
|
142
|
+
row[:style] = summary_style if summary_style
|
143
|
+
history_rows << row
|
144
|
+
end
|
145
|
+
history << history_rows
|
146
|
+
if extras
|
147
|
+
[ history, last_ds, delta ]
|
148
|
+
else
|
149
|
+
history
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Вариант history_table с другими предустановками.
|
154
|
+
def summary_table list, extras: false, summary_style: SUMMARY_STYLE
|
155
|
+
history_table list, news: false, summary: true, base_link: nil, last: nil, extras: extras, last_style: nil, summary_style: summary_style
|
156
|
+
end
|
157
|
+
|
158
|
+
# TODO: разобраться с разными count в разных контекстах.
|
159
|
+
def rating_table list, limit: 1, count: 3, details: true, key: :species, summary: false
|
160
|
+
object_title = class_title list.first&.object
|
161
|
+
rating = table do
|
162
|
+
column '#', width: 3, align: :right, data: :line_no
|
163
|
+
column object_title, data: :object
|
164
|
+
column 'Виды', width: 6, align: :right, data: :species
|
165
|
+
if details
|
166
|
+
column 'Наблюдения', data: :observations
|
167
|
+
else
|
168
|
+
column 'Наблюдения', width: 6, align: :right, data: :observations
|
169
|
+
end
|
170
|
+
end
|
171
|
+
rating_rows = []
|
172
|
+
list.each do |ds|
|
173
|
+
ls = ds.to_list Listers::SPECIES
|
174
|
+
row = { object: ds.object, species: ls.count, count: ds.count }
|
175
|
+
if details
|
176
|
+
row[:observations] = ds.observations.map(&:to_s).join(', ')
|
177
|
+
else
|
178
|
+
row[:observations] = ds.count
|
179
|
+
end
|
180
|
+
rating_rows << row
|
181
|
+
end
|
182
|
+
size = rating_rows.size
|
183
|
+
key = :count if key == :observations
|
184
|
+
if limit
|
185
|
+
rating_rows.filter! { |row| row[key] >= limit }
|
186
|
+
end
|
187
|
+
rating_rows.sort_by! { |row| -row[key] }
|
188
|
+
if count
|
189
|
+
rating_rows = rating_rows.take(count)
|
190
|
+
end
|
191
|
+
if summary
|
192
|
+
full_ls = List === list ? list : list.reduce(DataSet::zero, :|).to_list
|
193
|
+
rating_rows << { line_no: '', species: full_ls.count, count: full_ls.observation_count }
|
194
|
+
end
|
195
|
+
rating << rating_rows
|
196
|
+
if count
|
197
|
+
[ rating, size ]
|
198
|
+
else
|
199
|
+
rating
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
module_function :species_table, :history_table, :summary_table, :rating_table
|
204
|
+
|
205
|
+
end
|
data/lib/inat/report/table.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class Table
|
3
|
+
class INat::Report::Table
|
4
4
|
|
5
5
|
attr_reader :columns
|
6
6
|
|
@@ -41,6 +41,10 @@ class Table
|
|
41
41
|
@rows
|
42
42
|
end
|
43
43
|
|
44
|
+
def empty?
|
45
|
+
@rows.empty?
|
46
|
+
end
|
47
|
+
|
44
48
|
def << data
|
45
49
|
if Array === data
|
46
50
|
rows(*data)
|
@@ -124,12 +128,16 @@ class Table
|
|
124
128
|
|
125
129
|
end
|
126
130
|
|
127
|
-
module
|
131
|
+
module INat::Report::Table::DSL
|
128
132
|
|
129
|
-
|
133
|
+
include INat::Report
|
134
|
+
|
135
|
+
def table &block
|
130
136
|
tbl = Table::new
|
131
137
|
tbl.instance_eval(&block) if block_given?
|
132
138
|
tbl
|
133
139
|
end
|
134
140
|
|
141
|
+
module_function :table
|
142
|
+
|
135
143
|
end
|
data/lib/inat/utils/deep.rb
CHANGED
@@ -1,30 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
# TODO: убрать, все равно конфиг надо парсить нормально
|
4
|
+
|
5
|
+
module DeepMerge
|
6
|
+
|
7
|
+
refine Hash do
|
8
|
+
|
9
|
+
def deep_merge! other
|
10
|
+
other.each do |key, value|
|
11
|
+
if has_key?(key) && self[key].respond_to?(:deep_merge!)
|
12
|
+
self[key].deep_merge! value
|
13
|
+
else
|
14
|
+
self[key] = value
|
15
|
+
end
|
11
16
|
end
|
17
|
+
self
|
12
18
|
end
|
13
|
-
self
|
14
|
-
end
|
15
19
|
|
16
|
-
end
|
20
|
+
end
|
17
21
|
|
18
|
-
|
22
|
+
refine Array do
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
def deep_merge! other
|
25
|
+
other.each do |value|
|
26
|
+
next if self.include?(value)
|
27
|
+
next if String === value && self.include?(value.intern)
|
28
|
+
next if Symbol === value && self.include?(value.to_s)
|
29
|
+
self << value
|
30
|
+
end
|
31
|
+
self
|
26
32
|
end
|
27
|
-
|
33
|
+
|
28
34
|
end
|
29
35
|
|
30
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inat-get
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.0.
|
4
|
+
version: 0.8.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Shikhalev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sqlite3
|
@@ -46,6 +46,7 @@ executables:
|
|
46
46
|
extensions: []
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
|
+
- ".yardopts"
|
49
50
|
- LICENSE
|
50
51
|
- README.md
|
51
52
|
- Rakefile
|
@@ -69,7 +70,6 @@ files:
|
|
69
70
|
- lib/inat/app/task/context.rb
|
70
71
|
- lib/inat/app/task/dsl.rb
|
71
72
|
- lib/inat/data/api.rb
|
72
|
-
- lib/inat/data/cache.rb
|
73
73
|
- lib/inat/data/db.rb
|
74
74
|
- lib/inat/data/ddl.rb
|
75
75
|
- lib/inat/data/entity.rb
|
@@ -107,11 +107,12 @@ files:
|
|
107
107
|
- lib/inat/data/types/extras.rb
|
108
108
|
- lib/inat/data/types/location.rb
|
109
109
|
- lib/inat/data/types/std.rb
|
110
|
+
- lib/inat/report/report_dsl.rb
|
110
111
|
- lib/inat/report/table.rb
|
111
112
|
- lib/inat/utils/deep.rb
|
112
113
|
homepage: https://github.com/shikhalev/inat-get
|
113
114
|
licenses:
|
114
|
-
- GPL-3.0
|
115
|
+
- GPL-3.0-or-later
|
115
116
|
metadata:
|
116
117
|
homepage_uri: https://github.com/shikhalev/inat-get
|
117
118
|
source_code_uri: https://github.com/shikhalev/inat-get
|
@@ -130,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
131
|
- !ruby/object:Gem::Version
|
131
132
|
version: '0'
|
132
133
|
requirements: []
|
133
|
-
rubygems_version: 3.
|
134
|
+
rubygems_version: 3.5.3
|
134
135
|
signing_key:
|
135
136
|
specification_version: 4
|
136
137
|
summary: Client for iNaturalist API.
|