inat-channel 0.8.0.14 → 0.9.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/README.md +327 -94
- data/bin/inat-channel +14 -15
- data/lib/inat-channel/api.rb +55 -31
- data/lib/inat-channel/config.rb +60 -14
- data/lib/inat-channel/data.rb +267 -36
- data/lib/inat-channel/data_convert.rb +108 -0
- data/lib/inat-channel/data_types.rb +201 -0
- data/lib/inat-channel/icons.rb +118 -41
- data/lib/inat-channel/lock.rb +6 -6
- data/lib/inat-channel/logger.rb +10 -2
- data/lib/inat-channel/message.rb +20 -112
- data/lib/inat-channel/telegram.rb +30 -12
- data/lib/inat-channel/template.rb +106 -0
- data/lib/inat-channel/version.rb +7 -2
- data/lib/inat-channel.rb +0 -1
- metadata +4 -29
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
require 'time'
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
require_relative 'data_types'
|
|
6
|
+
|
|
7
|
+
module INatChannel
|
|
8
|
+
|
|
9
|
+
module DataConvert
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
|
|
13
|
+
def convert_observation observation_source
|
|
14
|
+
begin
|
|
15
|
+
id = observation_source[:id]
|
|
16
|
+
url = observation_source[:uri]
|
|
17
|
+
uuid = observation_source[:uuid]
|
|
18
|
+
user = convert_user observation_source[:user]
|
|
19
|
+
taxon = convert_taxon observation_source[:taxon], observation_source[:identifications]
|
|
20
|
+
places = convert_places observation_source[:place_ids]
|
|
21
|
+
datetime = DateTime.parse(observation_source[:time_observed_at] || observation_source[:observed_on_string])
|
|
22
|
+
location = convert_location observation_source[:geojson]
|
|
23
|
+
description = convert_description observation_source[:description]
|
|
24
|
+
place_guess = observation_source[:place_guess]
|
|
25
|
+
rescue => e
|
|
26
|
+
IC::logger.error e.full_message
|
|
27
|
+
IC::logger.info JSON.pretty_generate(observation_source)
|
|
28
|
+
raise e
|
|
29
|
+
end
|
|
30
|
+
Observation::new id: id, url: url, uuid: uuid, user: user, taxon: taxon, places: places, datetime: datetime, location: location, description: description, place_guess: place_guess
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def convert_location location_source
|
|
36
|
+
return nil unless location_source
|
|
37
|
+
lat = location_source.dig :coordinates, 1
|
|
38
|
+
lng = location_source.dig :coordinates, 0
|
|
39
|
+
return nil unless lat && lng
|
|
40
|
+
Location::new lat: lat, lng: lng
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def convert_description description_source
|
|
44
|
+
return nil if description_source.nil? || description_source.empty?
|
|
45
|
+
Description::new value: description_source
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def convert_user user_source
|
|
49
|
+
User::new id: user_source[:id], login: user_source[:login], name: user_source[:name]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def convert_taxon taxon_source, identifications
|
|
53
|
+
id = taxon_source[:id]
|
|
54
|
+
scientific_name = taxon_source[:name]
|
|
55
|
+
common_name = taxon_source[:preferred_common_name]
|
|
56
|
+
source_ancestors = nil
|
|
57
|
+
identifications.each do |ident|
|
|
58
|
+
it = ident[:taxon]
|
|
59
|
+
if it[:id] == id
|
|
60
|
+
source_ancestors = it[:ancestors]
|
|
61
|
+
break
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
ancestors = if source_ancestors
|
|
65
|
+
source_ancestors.map do |anc|
|
|
66
|
+
Ancestor::new id: anc[:id], scientific_name: anc[:name]
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
taxon_source[:ancestor_ids].map do |aid|
|
|
70
|
+
Ancestor::new id: aid
|
|
71
|
+
end
|
|
72
|
+
# TODO: load names from API by ancestor_ids
|
|
73
|
+
end
|
|
74
|
+
ancestors << Ancestor::new(id: taxon_source[:id], scientific_name: taxon_source[:name])
|
|
75
|
+
Taxon::new id: id, scientific_name: scientific_name, common_name: common_name, ancestors: ancestors
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def convert_places place_ids
|
|
79
|
+
places_config = IC::CONFIG[:places]
|
|
80
|
+
return nil unless places_config
|
|
81
|
+
result = []
|
|
82
|
+
places_config.each do |_, items|
|
|
83
|
+
items.each do |item|
|
|
84
|
+
ids = Set[*item[:place_ids]]
|
|
85
|
+
if ids.intersect?(place_ids)
|
|
86
|
+
result << Place::new(text: item[:text], link: item[:link], tag: item[:tag])
|
|
87
|
+
break
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
result
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
module IC
|
|
101
|
+
|
|
102
|
+
def convert_observation source
|
|
103
|
+
INatChannel::DataConvert::convert_observation source
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
module_function :convert_observation
|
|
107
|
+
|
|
108
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
require 'time'
|
|
3
|
+
require 'sanitize'
|
|
4
|
+
|
|
5
|
+
require_relative 'icons'
|
|
6
|
+
|
|
7
|
+
class Date
|
|
8
|
+
def icon
|
|
9
|
+
IC::ICONS[:calendar]
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Time
|
|
14
|
+
def icon
|
|
15
|
+
IC::clock_icon self
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class DateTime
|
|
20
|
+
def icon
|
|
21
|
+
IC::ICONS[:calendar]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class String
|
|
26
|
+
def to_tag
|
|
27
|
+
"\##{self.gsub(/\s+/, '_').gsub(/-/, '_').gsub(/[^a-zA-Zа-яА-ЯёЁ_]/, '')}"
|
|
28
|
+
end
|
|
29
|
+
def limit len
|
|
30
|
+
return self if length <= len
|
|
31
|
+
|
|
32
|
+
short = self[0, len]
|
|
33
|
+
last_space = short.rindex(/\s/)
|
|
34
|
+
last_sign = short.rindex(/[,.;:!?]/)
|
|
35
|
+
if last_space
|
|
36
|
+
if last_sign && last_sign + 1 > last_space
|
|
37
|
+
return short[0, last_sign + 1] + '...'
|
|
38
|
+
end
|
|
39
|
+
return short[0, last_space] + '...'
|
|
40
|
+
else
|
|
41
|
+
if last_sign
|
|
42
|
+
return short[0, last_sign + 1] + '...'
|
|
43
|
+
end
|
|
44
|
+
return short + '...'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Array
|
|
50
|
+
def to_tags
|
|
51
|
+
self.map do |item|
|
|
52
|
+
if item.respond_to?(:to_tag)
|
|
53
|
+
item.to_tag
|
|
54
|
+
else
|
|
55
|
+
item&.to_s.to_tag
|
|
56
|
+
end
|
|
57
|
+
end.compact
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Observation = Data::define :taxon, :id, :uuid, :url, :user, :datetime, :places, :place_guess, :description, :location do
|
|
62
|
+
def icon
|
|
63
|
+
IC::ICONS[:observation]
|
|
64
|
+
end
|
|
65
|
+
def date
|
|
66
|
+
datetime.to_date
|
|
67
|
+
end
|
|
68
|
+
def time
|
|
69
|
+
datetime.to_time
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Taxon = Data::define :scientific_name, :common_name, :id, :ancestors do
|
|
74
|
+
def icon
|
|
75
|
+
IC::ancestors_icon ancestors.map(&:id)
|
|
76
|
+
end
|
|
77
|
+
def title
|
|
78
|
+
if common_name && !common_name.empty?
|
|
79
|
+
"<b>#{common_name}</b> <i>(#{scientific_name})</i>"
|
|
80
|
+
else
|
|
81
|
+
"<b><i>#{scientific_name}</i></b>"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
def url
|
|
85
|
+
"https://www.inaturalist.org/taxa/#{id}"
|
|
86
|
+
end
|
|
87
|
+
def to_tags
|
|
88
|
+
ancestors.to_tags
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
Ancestor = Data::define :scientific_name, :id do
|
|
93
|
+
def to_tag
|
|
94
|
+
scientific_name.to_tag
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
Place = Data::define :text, :link, :tag do
|
|
99
|
+
def icon
|
|
100
|
+
IC::ICONS[:place]
|
|
101
|
+
end
|
|
102
|
+
def to_tag
|
|
103
|
+
tag&.to_tag
|
|
104
|
+
end
|
|
105
|
+
def title
|
|
106
|
+
text
|
|
107
|
+
end
|
|
108
|
+
def url
|
|
109
|
+
link
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
User = Data::define :id, :login, :name do
|
|
114
|
+
def icon
|
|
115
|
+
IC::ICONS[:user]
|
|
116
|
+
end
|
|
117
|
+
def title
|
|
118
|
+
if name && !name.empty?
|
|
119
|
+
name
|
|
120
|
+
else
|
|
121
|
+
"@#{login}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
def url
|
|
125
|
+
"https://www.inaturalist.org/people/#{id}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
module IC
|
|
130
|
+
|
|
131
|
+
SANITIZE_HTML_CONFIG = {
|
|
132
|
+
elements: [ 'b', 'strong', 'i', 'em', 'u', 's', 'strike', 'del', 'a', 'code', 'pre', 'tg-spoiler', 'blockquote' ],
|
|
133
|
+
attributes: { 'a' => [ 'href' ] },
|
|
134
|
+
protocols: { 'a' => { 'href' => [ 'http', 'https', 'mailto', 'tg' ] } },
|
|
135
|
+
remove_contents: [ 'script', 'style' ]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
SANITIZE_TEXT_CONFIG = {
|
|
139
|
+
elements: [],
|
|
140
|
+
remove_contents: [ 'script', 'style' ]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
Description = Data::define :value do
|
|
146
|
+
def icon
|
|
147
|
+
IC::ICONS[:description]
|
|
148
|
+
end
|
|
149
|
+
def text
|
|
150
|
+
Sanitize.fragment(value, IC::SANITIZE_TEXT_CONFIG).limit(IC::CONFIG.dig(:tg_bot, :desc_limit))
|
|
151
|
+
end
|
|
152
|
+
def html
|
|
153
|
+
sanitized = Sanitize.fragment value, IC::SANITIZE_HTML_CONFIG
|
|
154
|
+
if sanitized.length > IC::CONFIG.dig(:tg_bot, :desc_limit)
|
|
155
|
+
# В отличие от простого текста, обрезка HTML требует куда более изощренной логики, что неоправданно
|
|
156
|
+
text
|
|
157
|
+
else
|
|
158
|
+
sanitized
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
Location = Data::define :lat, :lng do
|
|
164
|
+
def icon
|
|
165
|
+
IC::ICONS[:location]
|
|
166
|
+
end
|
|
167
|
+
def dms
|
|
168
|
+
lat_dir = lat >= 0 ? "N" : "S"
|
|
169
|
+
lng_dir = lng >= 0 ? "E" : "W"
|
|
170
|
+
lat_abs = lat.abs
|
|
171
|
+
lng_abs = lng.abs
|
|
172
|
+
lat_d = lat_abs.floor
|
|
173
|
+
lat_m = ((lat_abs - lat_d) * 60).floor
|
|
174
|
+
lat_s = ((lat_abs - lat_d - lat_m / 60.0) * 3600).round
|
|
175
|
+
lng_d = lng_abs.floor
|
|
176
|
+
lng_m = ((lng_abs - lng_d) * 60).floor
|
|
177
|
+
lng_s = ((lng_abs - lng_d - lng_m / 60.0) * 3600).round
|
|
178
|
+
"%d°%02d'%02d\"%s %d°%02d'%02d\"%s" % [lat_d, lat_m, lat_s, lat_dir, lng_d, lng_m, lng_s, lng_dir]
|
|
179
|
+
end
|
|
180
|
+
def decimal
|
|
181
|
+
lat_dir = lat >= 0 ? "N" : "S"
|
|
182
|
+
lng_dir = lng >= 0 ? "E" : "W"
|
|
183
|
+
lat_abs = lat.abs
|
|
184
|
+
lng_abs = lng.abs
|
|
185
|
+
"%.4f°%s, %.4f°%s" % [lat_abs, lat_dir, lng_abs, lng_dir]
|
|
186
|
+
end
|
|
187
|
+
def google
|
|
188
|
+
# "https://www.google.com/maps/search/?api=1&query=#{lat},#{lng}&z=#{FORMATS[:zoom]}&ll=#{lat},#{lng}"
|
|
189
|
+
"https://www.google.com/maps/place/#{lat},#{lng}/@#{lat},#{lng},#{IC::CONFIG.dig(:tg_bot, :link_zoom)}z/"
|
|
190
|
+
end
|
|
191
|
+
def yandex
|
|
192
|
+
"https://yandex.ru/maps/?ll=#{lng},#{lat}&z=#{IC::CONFIG.dig(:tg_bot, :link_zoom)}&pt=#{lng},#{lat},pm2rdm1"
|
|
193
|
+
end
|
|
194
|
+
def osm
|
|
195
|
+
"https://www.openstreetmap.org/?mlat=#{lat}&mlon=#{lng}#map=#{IC::CONFIG.dig(:tg_bot, :link_zoom)}/#{lat}/#{lng}"
|
|
196
|
+
end
|
|
197
|
+
def url
|
|
198
|
+
osm
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
data/lib/inat-channel/icons.rb
CHANGED
|
@@ -1,58 +1,135 @@
|
|
|
1
|
-
|
|
2
1
|
module INatChannel
|
|
3
|
-
|
|
4
2
|
module Icons
|
|
5
|
-
|
|
6
3
|
TAXA_ICONS = {
|
|
7
|
-
48460
|
|
8
|
-
47126
|
|
9
|
-
47170
|
|
10
|
-
47686
|
|
11
|
-
151817 =>
|
|
12
|
-
67333
|
|
13
|
-
1
|
|
14
|
-
136329 =>
|
|
15
|
-
47124
|
|
16
|
-
47163
|
|
17
|
-
47178
|
|
18
|
-
196614 =>
|
|
19
|
-
47187
|
|
20
|
-
47158
|
|
21
|
-
47119
|
|
22
|
-
71261
|
|
23
|
-
18874
|
|
24
|
-
48222
|
|
25
|
-
47115
|
|
26
|
-
3
|
|
27
|
-
40151
|
|
28
|
-
26036
|
|
29
|
-
20978
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
4
|
+
48460 => "🧬",
|
|
5
|
+
47126 => "🌿",
|
|
6
|
+
47170 => "🍄",
|
|
7
|
+
47686 => "🦠",
|
|
8
|
+
151817 => "🦠",
|
|
9
|
+
67333 => "🦠",
|
|
10
|
+
1 => "🐾",
|
|
11
|
+
136329 => "🌲",
|
|
12
|
+
47124 => "🌸",
|
|
13
|
+
47163 => "🍃",
|
|
14
|
+
47178 => "🐟",
|
|
15
|
+
196614 => "🦈",
|
|
16
|
+
47187 => "🦀",
|
|
17
|
+
47158 => "🪲",
|
|
18
|
+
47119 => "🕷️",
|
|
19
|
+
71261 => "🦅",
|
|
20
|
+
18874 => "🦜",
|
|
21
|
+
48222 => "🌊",
|
|
22
|
+
47115 => "🐚",
|
|
23
|
+
3 => "🐦",
|
|
24
|
+
40151 => "🦌",
|
|
25
|
+
26036 => "🐍",
|
|
26
|
+
20978 => "🐸",
|
|
27
|
+
|
|
28
|
+
# TODO: add ALL taxa with iNat icons and some other large group
|
|
29
|
+
}
|
|
33
30
|
|
|
34
31
|
ICONS = {
|
|
35
|
-
:user =>
|
|
36
|
-
:place =>
|
|
37
|
-
:calendar =>
|
|
38
|
-
:location =>
|
|
39
|
-
:observation =>
|
|
40
|
-
:description =>
|
|
41
|
-
:default_taxon =>
|
|
42
|
-
|
|
43
|
-
}
|
|
32
|
+
:user => "👤",
|
|
33
|
+
:place => "🗺️",
|
|
34
|
+
:calendar => "📅",
|
|
35
|
+
:location => "📍",
|
|
36
|
+
:observation => "📷",
|
|
37
|
+
:description => "📝",
|
|
38
|
+
:default_taxon => "🧬",
|
|
39
|
+
# TODO: add other icons like calendar, place, etc.
|
|
40
|
+
}
|
|
44
41
|
|
|
45
42
|
class << self
|
|
46
|
-
|
|
47
43
|
def taxon_icon taxon
|
|
48
|
-
taxon[:ancestor_ids]
|
|
49
|
-
|
|
44
|
+
ancestors_icon taxon[:ancestor_ids]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def ancestors_icon ancestor_ids
|
|
48
|
+
ancestor_ids.reverse_each do |ancestor_id|
|
|
49
|
+
return TAXA_ICONS[ancestor_id] if TAXA_ICONS[ancestor_id]
|
|
50
50
|
end
|
|
51
51
|
return ICONS[:default_taxon]
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
def clock_icon time
|
|
55
|
+
hour = time.hour % 12
|
|
56
|
+
minute = time.min
|
|
57
|
+
|
|
58
|
+
if minute <= 20
|
|
59
|
+
# ≤20 мин - текущий час
|
|
60
|
+
case hour
|
|
61
|
+
when 0, 12 then "🕛"
|
|
62
|
+
when 1 then "🕐"
|
|
63
|
+
when 2 then "🕑"
|
|
64
|
+
when 3 then "🕒"
|
|
65
|
+
when 4 then "🕓"
|
|
66
|
+
when 5 then "🕔"
|
|
67
|
+
when 6 then "🕕"
|
|
68
|
+
when 7 then "🕖"
|
|
69
|
+
when 8 then "🕗"
|
|
70
|
+
when 9 then "🕘"
|
|
71
|
+
when 10 then "🕙"
|
|
72
|
+
when 11 then "🕚"
|
|
73
|
+
end
|
|
74
|
+
elsif minute < 40
|
|
75
|
+
# 21-39 мин - полчаса текущего часа
|
|
76
|
+
case hour
|
|
77
|
+
when 0, 12 then "🕧"
|
|
78
|
+
when 1 then "🕜"
|
|
79
|
+
when 2 then "🕝"
|
|
80
|
+
when 3 then "🕞"
|
|
81
|
+
when 4 then "🕟"
|
|
82
|
+
when 5 then "🕠"
|
|
83
|
+
when 6 then "🕡"
|
|
84
|
+
when 7 then "🕢"
|
|
85
|
+
when 8 then "🕣"
|
|
86
|
+
when 9 then "🕤"
|
|
87
|
+
when 10 then "🕥"
|
|
88
|
+
when 11 then "🕦"
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
# ≥40 мин - следующий час
|
|
92
|
+
next_hour = (hour + 1) % 12
|
|
93
|
+
case next_hour
|
|
94
|
+
when 0, 12 then "🕛"
|
|
95
|
+
when 1 then "🕐"
|
|
96
|
+
when 2 then "🕑"
|
|
97
|
+
when 3 then "🕒"
|
|
98
|
+
when 4 then "🕓"
|
|
99
|
+
when 5 then "🕔"
|
|
100
|
+
when 6 then "🕕"
|
|
101
|
+
when 7 then "🕖"
|
|
102
|
+
when 8 then "🕗"
|
|
103
|
+
when 9 then "🕘"
|
|
104
|
+
when 10 then "🕙"
|
|
105
|
+
when 11 then "🕚"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
54
110
|
end
|
|
55
111
|
|
|
56
112
|
end
|
|
57
113
|
|
|
58
114
|
end
|
|
115
|
+
|
|
116
|
+
module IC
|
|
117
|
+
|
|
118
|
+
TAXA_ICONS = INatChannel::Icons::TAXA_ICONS
|
|
119
|
+
ICONS = INatChannel::Icons::ICONS
|
|
120
|
+
|
|
121
|
+
def taxon_icon taxon
|
|
122
|
+
INatChannel::Icons::taxon_icon taxon
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def ancestors_icon ancestor_ids
|
|
126
|
+
INatChannel::Icons::ancestors_icon ancestor_ids
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def clock_icon time
|
|
130
|
+
INatChannel::Icons::clock_icon time
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
module_function :taxon_icon, :ancestors_icon, :clock_icon
|
|
134
|
+
|
|
135
|
+
end
|
data/lib/inat-channel/lock.rb
CHANGED
|
@@ -12,13 +12,13 @@ module INatChannel
|
|
|
12
12
|
class << self
|
|
13
13
|
|
|
14
14
|
def acquire!
|
|
15
|
-
file =
|
|
15
|
+
file = IC::CONFIG.dig(:lock_file, :path)
|
|
16
16
|
FileUtils.mkdir_p File.dirname(file)
|
|
17
17
|
|
|
18
18
|
if File.exist?(file)
|
|
19
19
|
data = load_data file
|
|
20
20
|
if stale?(data)
|
|
21
|
-
|
|
21
|
+
IC::logger.info "Remove stale lock: #{file}"
|
|
22
22
|
File.delete file
|
|
23
23
|
else
|
|
24
24
|
raise "Another instance is already running (PID: #{data[:pid]})"
|
|
@@ -30,15 +30,15 @@ module INatChannel
|
|
|
30
30
|
started_at: Time.now.utc.iso8601
|
|
31
31
|
}
|
|
32
32
|
File.write file, JSON.pretty_generate(data)
|
|
33
|
-
|
|
33
|
+
IC::logger.info "Lock acquired: #{file}"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def release!
|
|
37
|
-
file =
|
|
37
|
+
file = IC::CONFIG.dig(:lock_file, :path)
|
|
38
38
|
return nil unless File.exist?(file)
|
|
39
39
|
|
|
40
40
|
File.delete file
|
|
41
|
-
|
|
41
|
+
IC::logger.info "Lock release: #{file}"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
private
|
|
@@ -49,7 +49,7 @@ module INatChannel
|
|
|
49
49
|
{}
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
LOCK_TTL =
|
|
52
|
+
LOCK_TTL = 300 # 5 min
|
|
53
53
|
|
|
54
54
|
def stale? data
|
|
55
55
|
if data[:started_at]
|
data/lib/inat-channel/logger.rb
CHANGED
|
@@ -14,7 +14,7 @@ module INatChannel
|
|
|
14
14
|
|
|
15
15
|
def get_logger
|
|
16
16
|
lgr = ::Logger::new $stderr
|
|
17
|
-
lgr.level =
|
|
17
|
+
lgr.level = IC::CONFIG[:log_level]
|
|
18
18
|
lgr
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -22,7 +22,15 @@ module INatChannel
|
|
|
22
22
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module IC
|
|
28
|
+
|
|
29
|
+
def logger
|
|
30
|
+
INatChannel::Logger::logger
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module_function :logger
|
|
26
34
|
|
|
27
35
|
end
|
|
28
36
|
|