rgfa 1.2.1
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/bin/gfadiff.rb +420 -0
- data/bin/rgfa-findcrisprs.rb +208 -0
- data/bin/rgfa-mergelinear.rb +14 -0
- data/bin/rgfa-simdebruijn.rb +86 -0
- data/lib/rgfa.rb +376 -0
- data/lib/rgfa/byte_array.rb +74 -0
- data/lib/rgfa/cigar.rb +157 -0
- data/lib/rgfa/connectivity.rb +131 -0
- data/lib/rgfa/containments.rb +97 -0
- data/lib/rgfa/error.rb +3 -0
- data/lib/rgfa/field_array.rb +87 -0
- data/lib/rgfa/field_parser.rb +109 -0
- data/lib/rgfa/field_validator.rb +241 -0
- data/lib/rgfa/field_writer.rb +108 -0
- data/lib/rgfa/headers.rb +76 -0
- data/lib/rgfa/line.rb +721 -0
- data/lib/rgfa/line/containment.rb +87 -0
- data/lib/rgfa/line/header.rb +92 -0
- data/lib/rgfa/line/link.rb +379 -0
- data/lib/rgfa/line/path.rb +106 -0
- data/lib/rgfa/line/segment.rb +209 -0
- data/lib/rgfa/linear_paths.rb +285 -0
- data/lib/rgfa/lines.rb +155 -0
- data/lib/rgfa/links.rb +242 -0
- data/lib/rgfa/logger.rb +192 -0
- data/lib/rgfa/multiplication.rb +156 -0
- data/lib/rgfa/numeric_array.rb +196 -0
- data/lib/rgfa/paths.rb +98 -0
- data/lib/rgfa/rgl.rb +194 -0
- data/lib/rgfa/segment_ends_path.rb +9 -0
- data/lib/rgfa/segment_info.rb +162 -0
- data/lib/rgfa/segments.rb +99 -0
- data/lib/rgfa/sequence.rb +65 -0
- data/lib/rgfatools.rb +102 -0
- data/lib/rgfatools/artifacts.rb +29 -0
- data/lib/rgfatools/copy_number.rb +126 -0
- data/lib/rgfatools/invertible_segments.rb +104 -0
- data/lib/rgfatools/linear_paths.rb +140 -0
- data/lib/rgfatools/multiplication.rb +194 -0
- data/lib/rgfatools/p_bubbles.rb +66 -0
- data/lib/rgfatools/superfluous_links.rb +64 -0
- metadata +97 -0
data/lib/rgfa/lines.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require_relative "error"
|
2
|
+
|
3
|
+
#
|
4
|
+
# Methods for the RGFA class, which allow to handle lines of multiple types.
|
5
|
+
#
|
6
|
+
module RGFA::Lines
|
7
|
+
|
8
|
+
# Add a line to a RGFA
|
9
|
+
#
|
10
|
+
# @overload <<(gfa_line_string)
|
11
|
+
# @param [String] gfa_line_string representation of a RGFA line
|
12
|
+
# @overload <<(gfa_line)
|
13
|
+
# @param [RGFA::Line] gfa_line instance of a subclass of RGFA::Line
|
14
|
+
# @raise [RGFA::DuplicatedLabelError] if multiple segment or path lines
|
15
|
+
# with the same name are added
|
16
|
+
# @return [RGFA] self
|
17
|
+
def <<(gfa_line)
|
18
|
+
gfa_line = gfa_line.to_rgfa_line(validate: @validate)
|
19
|
+
rt = gfa_line.record_type
|
20
|
+
case rt
|
21
|
+
when :H
|
22
|
+
add_header(gfa_line)
|
23
|
+
when :S
|
24
|
+
add_segment(gfa_line)
|
25
|
+
when :L
|
26
|
+
add_link(gfa_line)
|
27
|
+
when :C
|
28
|
+
add_containment(gfa_line)
|
29
|
+
when :P
|
30
|
+
add_path(gfa_line)
|
31
|
+
else
|
32
|
+
raise # this never happens, as already catched by gfa_line init
|
33
|
+
end
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Delete elements from the RGFA graph
|
38
|
+
# @overload rm(segment)
|
39
|
+
# @param segment [String, RGFA::Line::Segment] segment name or instance
|
40
|
+
# @overload rm(path)
|
41
|
+
# @param path [String, RGFA::Line::Segment] path name or instance
|
42
|
+
# @overload rm(link)
|
43
|
+
# @param link [RGFA::Line::Link] link
|
44
|
+
# @overload rm(containment)
|
45
|
+
# @param link [RGFA::Line::Containment] containment
|
46
|
+
# @overload rm(:headers)
|
47
|
+
# Remove all headers
|
48
|
+
# @overload rm(array)
|
49
|
+
# Calls {#rm} using each element of the array as argument
|
50
|
+
# @param array [Array]
|
51
|
+
# @overload rm(method_name, *args)
|
52
|
+
# Call a method of RGFA instance, then {#rm} for each returned value
|
53
|
+
# @param method_name [Symbol] method to call
|
54
|
+
# @param args arguments of the method
|
55
|
+
# @return [RGFA] self
|
56
|
+
def rm(x, *args)
|
57
|
+
if x.kind_of?(RGFA::Line)
|
58
|
+
raise ArgumentError,
|
59
|
+
"One argument required if first RGFA::Line" if !args.empty?
|
60
|
+
case x.record_type
|
61
|
+
when :H then raise ArgumentError, "Cannot remove single header lines"
|
62
|
+
when :S then delete_segment(x)
|
63
|
+
when :P then delete_path(x)
|
64
|
+
when :L then delete_link(x)
|
65
|
+
when :C then delete_containment(x)
|
66
|
+
end
|
67
|
+
elsif x.kind_of?(Symbol)
|
68
|
+
if @segments.has_key?(x)
|
69
|
+
if !args.empty?
|
70
|
+
raise ArgumentError, "One arguments required if first segment name"
|
71
|
+
end
|
72
|
+
delete_segment(x)
|
73
|
+
elsif @paths.has_key?(x)
|
74
|
+
if !args.empty?
|
75
|
+
raise ArgumentError, "One argument required if first path name"
|
76
|
+
end
|
77
|
+
delete_path(x)
|
78
|
+
elsif x == :headers
|
79
|
+
if !args.empty?
|
80
|
+
raise ArgumentError, "One argument required if first :headers"
|
81
|
+
end
|
82
|
+
delete_headers
|
83
|
+
else
|
84
|
+
if respond_to?(x)
|
85
|
+
rm(send(x, *args))
|
86
|
+
else
|
87
|
+
raise ArgumentError, "Cannot remove #{x.inspect}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
elsif x.kind_of?(String)
|
91
|
+
rm(x.to_sym, *args)
|
92
|
+
elsif x.kind_of?(Array)
|
93
|
+
x.each {|elem| rm(elem, *args)}
|
94
|
+
elsif x.nil?
|
95
|
+
return self
|
96
|
+
else
|
97
|
+
raise ArgumentError, "Cannot remove #{x.inspect}"
|
98
|
+
end
|
99
|
+
return self
|
100
|
+
end
|
101
|
+
|
102
|
+
# Rename a segment or a path
|
103
|
+
#
|
104
|
+
# @param old_name [String] the name of the segment or path to rename
|
105
|
+
# @param new_name [String] the new name for the segment or path
|
106
|
+
#
|
107
|
+
# @raise[RGFA::DuplicatedLabelError]
|
108
|
+
# if +new_name+ is already a segment or path name
|
109
|
+
# @return [RGFA] self
|
110
|
+
def rename(old_name, new_name)
|
111
|
+
old_name = old_name.to_sym
|
112
|
+
new_name = new_name.to_sym
|
113
|
+
s = segment(old_name)
|
114
|
+
pt = nil
|
115
|
+
if s.nil?
|
116
|
+
pt = path(old_name)
|
117
|
+
if pt.nil?
|
118
|
+
raise RGFA::LineMissingError,
|
119
|
+
"#{old_name} is not a path or segment name"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
if segment(new_name) or path(new_name)
|
123
|
+
raise RGFA::DuplicatedLabelError,
|
124
|
+
"#{new_name} is already a path or segment name"
|
125
|
+
end
|
126
|
+
if s
|
127
|
+
s.name = new_name
|
128
|
+
@segments.delete(old_name)
|
129
|
+
@segments[new_name] = s
|
130
|
+
else
|
131
|
+
pt.path_name = new_name
|
132
|
+
@paths.delete(old_name)
|
133
|
+
@paths[new_name] = pt
|
134
|
+
end
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def lines
|
141
|
+
headers + segments + links + containments + paths
|
142
|
+
end
|
143
|
+
|
144
|
+
def each_line(&block)
|
145
|
+
lines.each(&block)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
# Exception raised if a label for segment or path is duplicated
|
151
|
+
class RGFA::DuplicatedLabelError < RGFA::Error; end
|
152
|
+
|
153
|
+
# The error raised by banged line finders if no line respecting the criteria
|
154
|
+
# exist in the RGFA
|
155
|
+
class RGFA::LineMissingError < RGFA::Error; end
|
data/lib/rgfa/links.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
require_relative "error"
|
2
|
+
|
3
|
+
#
|
4
|
+
# Methods for the RGFA class, which allow to handle links in the graph.
|
5
|
+
#
|
6
|
+
module RGFA::Links
|
7
|
+
|
8
|
+
def add_link(gfa_line)
|
9
|
+
gfa_line = gfa_line.to_rgfa_line(validate: @validate)
|
10
|
+
gfa_line.normalize!
|
11
|
+
l = nil
|
12
|
+
if segment(gfa_line.from) and segment(gfa_line.to)
|
13
|
+
l = link_from_to(gfa_line.oriented_from,
|
14
|
+
gfa_line.oriented_to,
|
15
|
+
gfa_line.overlap)
|
16
|
+
end
|
17
|
+
if l.nil?
|
18
|
+
@links << gfa_line
|
19
|
+
[:from, :to].each do |dir|
|
20
|
+
segment_name = gfa_line.send(dir).to_sym
|
21
|
+
orient = gfa_line.send(:"#{dir}_orient").to_sym
|
22
|
+
if !@segments.has_key?(segment_name)
|
23
|
+
raise RGFA::LineMissingError if @segments_first_order
|
24
|
+
@segments[segment_name] =
|
25
|
+
RGFA::Line::Segment.new({:name => segment_name},
|
26
|
+
virtual: true)
|
27
|
+
end
|
28
|
+
@segments[segment_name].links[dir][orient] << gfa_line
|
29
|
+
gfa_line.send(:"#{dir}=", @segments[segment_name])
|
30
|
+
end
|
31
|
+
elsif l.virtual?
|
32
|
+
l.real!(gfa_line)
|
33
|
+
else
|
34
|
+
return
|
35
|
+
end
|
36
|
+
end
|
37
|
+
protected :add_link
|
38
|
+
|
39
|
+
# Deletes a link and all paths depending on it
|
40
|
+
#
|
41
|
+
# @param l [RGFA::Line::Link] link instance
|
42
|
+
# @return [RGFA] self
|
43
|
+
def delete_link(l)
|
44
|
+
@links.delete(l)
|
45
|
+
segment(l.from).links[:from][l.from_orient].delete(l)
|
46
|
+
segment(l.to).links[:to][l.to_orient].delete(l)
|
47
|
+
l.paths.each {|pt, orient| delete_path(pt)}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Remove all links of a segment end end except that to the other specified
|
51
|
+
# segment end.
|
52
|
+
# @param segment_end [RGFA::SegmentEnd] the segment end
|
53
|
+
# @param other_end [RGFA::SegmentEnd] the other segment end
|
54
|
+
# @param conserve_components [Boolean] <i>(defaults to: +false+)</i>
|
55
|
+
# Do not remove links if removing them breaks the graph into unconnected
|
56
|
+
# components.
|
57
|
+
# @return [RGFA] self
|
58
|
+
def delete_other_links(segment_end, other_end, conserve_components: false)
|
59
|
+
other_end = other_end.to_segment_end
|
60
|
+
links_of(segment_end).each do |l|
|
61
|
+
if l.other_end(segment_end) != other_end
|
62
|
+
if !conserve_components or !cut_link?(l)
|
63
|
+
delete_link(l)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# All links of the graph
|
70
|
+
# @return [Array<RGFA::Line::Link>]
|
71
|
+
def links
|
72
|
+
@links
|
73
|
+
end
|
74
|
+
|
75
|
+
# Finds links of the specified end of segment.
|
76
|
+
#
|
77
|
+
# @param [RGFA::SegmentEnd] segment_end a segment end
|
78
|
+
#
|
79
|
+
# @return [Array<RGFA::Line::Link>] if segment_end[1] == :E,
|
80
|
+
# links from sn with from_orient + and to sn with to_orient -
|
81
|
+
# @return [Array<RGFA::Line::Link>] if segment_end[1] == :B,
|
82
|
+
# links to sn with to_orient + and from sn with from_orient -
|
83
|
+
#
|
84
|
+
# @note to add or remove links, use the appropriate methods;
|
85
|
+
# adding or removing links from the returned array will not work
|
86
|
+
def links_of(segment_end)
|
87
|
+
segment_end = segment_end.to_segment_end
|
88
|
+
s = segment!(segment_end.segment)
|
89
|
+
o = segment_end.end_type == :E ? [:+,:-] : [:-,:+]
|
90
|
+
s.links[:from][o[0]] + s.links[:to][o[1]]
|
91
|
+
end
|
92
|
+
|
93
|
+
# Finds segment ends connected to the specified segment end.
|
94
|
+
#
|
95
|
+
# @param [RGFA::SegmentEnd] segment_end a segment end
|
96
|
+
#
|
97
|
+
# @return [Array<RGFA::SegmentEnd>>] segment ends connected by links
|
98
|
+
# to +segment_end+
|
99
|
+
def neighbours(segment_end)
|
100
|
+
links_of(segment_end).map {|l| l.other_end(segment_end) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Searches all links between +segment_end1+ and +segment_end2+
|
104
|
+
#
|
105
|
+
# @!macro [new] two_segment_ends
|
106
|
+
# @param segment_end1 [RGFA::SegmentEnd] a segment end
|
107
|
+
# @param segment_end2 [RGFA::SegmentEnd] a segment end
|
108
|
+
# @return [Array<RGFA::Line::Link>] (possibly empty)
|
109
|
+
def links_between(segment_end1, segment_end2)
|
110
|
+
segment_end1 = segment_end1.to_segment_end
|
111
|
+
segment_end2 = segment_end2.to_segment_end
|
112
|
+
links_of(segment_end1).select do |l|
|
113
|
+
l.other_end(segment_end1) == segment_end2
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# @!macro [new] link
|
118
|
+
# Searches a link between +segment_end1+ and +segment_end2+
|
119
|
+
# @!macro two_segment_ends
|
120
|
+
# @return [RGFA::Line::Link] the first link found
|
121
|
+
# @return [nil] if no link is found.
|
122
|
+
def link(segment_end1, segment_end2)
|
123
|
+
segment_end1 = segment_end1.to_segment_end
|
124
|
+
segment_end2 = segment_end2.to_segment_end
|
125
|
+
links_of(segment_end1).each do |l|
|
126
|
+
return l if l.other_end(segment_end1) == segment_end2
|
127
|
+
end
|
128
|
+
return nil
|
129
|
+
end
|
130
|
+
|
131
|
+
# @!macro link
|
132
|
+
# @raise [RGFA::LineMissingError] if no link is found.
|
133
|
+
def link!(segment_end1, segment_end2)
|
134
|
+
l = link(segment_end1, segment_end2)
|
135
|
+
raise RGFA::LineMissingError,
|
136
|
+
"No link was found: "+
|
137
|
+
"#{segment_end1.to_s} -- "+
|
138
|
+
"#{segment_end2.to_s}" if l.nil?
|
139
|
+
l
|
140
|
+
end
|
141
|
+
|
142
|
+
# Find links from the segment in the specified orientation
|
143
|
+
# (or the equivalent links, i.e. to the segment in opposite orientation).
|
144
|
+
#
|
145
|
+
# @param [RGFA::OrientedSegment] oriented_segment a segment with orientation
|
146
|
+
# @param equivalent [Boolean] return also equivalent links.
|
147
|
+
# @return [Array<RGFA::Line::Link>]
|
148
|
+
# @note to add or remove links, use the appropriate methods;
|
149
|
+
# adding or removing links from the returned array will not work
|
150
|
+
def links_from(oriented_segment, equivalent = true)
|
151
|
+
oriented_segment = oriented_segment.to_oriented_segment
|
152
|
+
s = segment!(oriented_segment.segment)
|
153
|
+
retval = s.links[:from][oriented_segment.orient]
|
154
|
+
if equivalent
|
155
|
+
retval + s.links[:to][oriented_segment.orient_inverted]
|
156
|
+
else
|
157
|
+
retval
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Find links to the segment in the specified orientation
|
162
|
+
# (or the equivalent links, i.e. from the segment in opposite orientation).
|
163
|
+
#
|
164
|
+
# @param [RGFA::OrientedSegment] oriented_segment a segment with orientation
|
165
|
+
# @param equivalent [Boolean] return also equivalent links.
|
166
|
+
# @return [Array<RGFA::Line::Link>]
|
167
|
+
# @note to add or remove links, use the appropriate methods;
|
168
|
+
# adding or removing links from the returned array will not work
|
169
|
+
def links_to(oriented_segment, equivalent = true)
|
170
|
+
oriented_segment = oriented_segment.to_oriented_segment
|
171
|
+
s = segment!(oriented_segment.segment)
|
172
|
+
retval = s.links[:to][oriented_segment.orient]
|
173
|
+
if equivalent
|
174
|
+
retval + s.links[:from][oriented_segment.orient_inverted]
|
175
|
+
else
|
176
|
+
retval
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Search all links from a segment S1 in a given orientation
|
181
|
+
# to another segment S2 in a given, or the equivalent
|
182
|
+
# links from S2 to S1 with inverted orientations.
|
183
|
+
#
|
184
|
+
# @param [RGFA::OrientedSegment] oriented_segment1 a segment with orientation
|
185
|
+
# @param [RGFA::OrientedSegment] oriented_segment2 a segment with orientation
|
186
|
+
# @param [RGFA::CIGAR] cigar shall match if not empty/undef
|
187
|
+
# @param equivalent [Boolean] return also equivalent links.
|
188
|
+
# @return [Array<RGFA::Line::Link>]
|
189
|
+
# @note to add or remove links, use the appropriate methods;
|
190
|
+
# adding or removing links from the returned array will not work
|
191
|
+
def links_from_to(oriented_segment1, oriented_segment2,
|
192
|
+
cigar = [], equivalent = true)
|
193
|
+
oriented_segment1 = oriented_segment1.to_oriented_segment
|
194
|
+
oriented_segment2 = oriented_segment2.to_oriented_segment
|
195
|
+
links_from(oriented_segment1, equivalent).select do |l|
|
196
|
+
l.compatible?(oriented_segment1, oriented_segment2, cigar, equivalent)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Search the link from a segment S1 in a given orientation
|
201
|
+
# to another segment S2 in a given, or the equivalent
|
202
|
+
# link from S2 to S1 with inverted orientations.
|
203
|
+
#
|
204
|
+
# @param [RGFA::OrientedSegment] oriented_segment1 a segment with orientation
|
205
|
+
# @param [RGFA::OrientedSegment] oriented_segment2 a segment with orientation
|
206
|
+
# @param [RGFA::CIGAR] cigar shall match if not empty/undef
|
207
|
+
# @param equivalent [Boolean] return also equivalent links.
|
208
|
+
# @return [RGFA::Line::Link] the first link found
|
209
|
+
# @return [nil] if no link is found.
|
210
|
+
def link_from_to(oriented_segment1, oriented_segment2,
|
211
|
+
cigar = [], equivalent = true)
|
212
|
+
oriented_segment1 = oriented_segment1.to_oriented_segment
|
213
|
+
oriented_segment2 = oriented_segment2.to_oriented_segment
|
214
|
+
links_from(oriented_segment1, equivalent).select do |l|
|
215
|
+
return l if l.compatible?(oriented_segment1, oriented_segment2,
|
216
|
+
cigar, equivalent)
|
217
|
+
end
|
218
|
+
return nil
|
219
|
+
end
|
220
|
+
|
221
|
+
# Search the link from a segment S1 in a given orientation
|
222
|
+
# to another segment S2 in a given, or the equivalent
|
223
|
+
# link from S2 to S1 with inverted orientations.
|
224
|
+
#
|
225
|
+
# @param [RGFA::OrientedSegment] oriented_segment1 a segment with orientation
|
226
|
+
# @param [RGFA::OrientedSegment] oriented_segment2 a segment with orientation
|
227
|
+
# @param [RGFA::CIGAR] cigar shall match if not empty/undef
|
228
|
+
# @param equivalent [Boolean] return also equivalent links.
|
229
|
+
# @return [RGFA::Line::Link] the first link found
|
230
|
+
# @raise [RGFA::LineMissingError] if no link is found.
|
231
|
+
def link_from_to!(oriented_segment1, oriented_segment2,
|
232
|
+
cigar = [], equivalent = true)
|
233
|
+
l = link_from_to(oriented_segment1, oriented_segment2,
|
234
|
+
cigar, equivalent)
|
235
|
+
raise RGFA::LineMissingError,
|
236
|
+
"No link was found: "+
|
237
|
+
"#{oriented_segment1.join(":")} -> "+
|
238
|
+
"#{oriented_segment2.join(":")}" if l.nil?
|
239
|
+
l
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
data/lib/rgfa/logger.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
#
|
2
|
+
# This class allows to output a message to the log file or STDERR and
|
3
|
+
# to keep track of the progress of a method which takes long time to complete.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
#
|
7
|
+
class RGFA::Logger
|
8
|
+
|
9
|
+
# Information about the progress of a computation
|
10
|
+
ProgressData = Struct.new(:counter, :units, :partsize,
|
11
|
+
:lastpart, :total, :starttime,
|
12
|
+
:strlen)
|
13
|
+
|
14
|
+
# Create a Logger instance
|
15
|
+
#
|
16
|
+
# @param channel [#puts]
|
17
|
+
# where to output (default: STDERR)
|
18
|
+
# @param prefix [String]
|
19
|
+
# output prefix (default: "#")
|
20
|
+
# @param verbose_level [Integer]
|
21
|
+
# 0: no logging; >0: the higher, the more logging
|
22
|
+
# @return [RGFA::Logger]
|
23
|
+
def initialize(verbose_level: 1, channel: STDERR, prefix: "#")
|
24
|
+
@progress = false
|
25
|
+
if !verbose_level.kind_of?(Integer)
|
26
|
+
raise ArgumentError, "verbose_level must be an Integer"
|
27
|
+
end
|
28
|
+
if !channel.respond_to?(:puts)
|
29
|
+
raise TypeError, "channel must provide a puts method"
|
30
|
+
end
|
31
|
+
@channel = channel
|
32
|
+
@pfx = prefix
|
33
|
+
@verbose_level = verbose_level
|
34
|
+
@data = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Output a message
|
38
|
+
#
|
39
|
+
# @param msg [String] message to output
|
40
|
+
# @param min_verbose_level [Integer]
|
41
|
+
# @return [void]
|
42
|
+
def log(msg, min_verbose_level=1)
|
43
|
+
@channel.puts "#@pfx #{msg}" if @verbose_level >= min_verbose_level
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Enable output from the Logger instance
|
48
|
+
#
|
49
|
+
# @param part [Float]
|
50
|
+
# - part = 0 => output at every call of {RGFA::Logger.progress_log}
|
51
|
+
# - 0 < part < 1 => output once per part of the total progress
|
52
|
+
# (e.g. 0.001 = log every 0.1% progress)
|
53
|
+
# - part = 1 => output only total elapsed time
|
54
|
+
# @return [void]
|
55
|
+
def enable_progress(part: 0.1)
|
56
|
+
if part < 0 or part > 1
|
57
|
+
raise ArgumentError, "part must be in range [0..1]"
|
58
|
+
end
|
59
|
+
@progress = true
|
60
|
+
@part = part
|
61
|
+
@channel.puts "#@pfx Progress logging enabled" if @verbose_level > 0
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# Disable progress logging
|
66
|
+
# @return [void]
|
67
|
+
def disable_progress
|
68
|
+
@progress = false
|
69
|
+
@channel.puts "#@pfx Progress logging disabled" if @verbose_level > 0
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# @!macro progress_init
|
74
|
+
# Initialize progress logging for a computation
|
75
|
+
# @param symbol [Symbol] a symbol assigned to the computation
|
76
|
+
# @param units [String] a string with the name of the units, in plural
|
77
|
+
# @param total [Integer] total number of units
|
78
|
+
# @param initmsg [String] an optional message to output at the beginning
|
79
|
+
# @return [void]
|
80
|
+
def progress_init(symbol, units, total, initmsg = nil)
|
81
|
+
return nil if !@progress or total == 0
|
82
|
+
str = "#@pfx 0.0% #{units} processed"
|
83
|
+
@data[symbol] = ProgressData.new(0, units, (@part*total).to_i, 1, total,
|
84
|
+
Time.now, str.size)
|
85
|
+
@channel.puts "#@pfx #{initmsg}" if initmsg
|
86
|
+
@channel.print str if @part != 1
|
87
|
+
return nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# @!macro [new] progress_log
|
91
|
+
# Updates progress logging for a computation
|
92
|
+
# @!macro [new] prlog
|
93
|
+
# @param symbol [Symbol] the symbol assigned to the computation at
|
94
|
+
# init time
|
95
|
+
# @param keyargs [Hash] additional units to display, with their current
|
96
|
+
# value (e.g. segments_processed: 10000)
|
97
|
+
# @param progress [Integer] how many units were processed
|
98
|
+
# @return [void]
|
99
|
+
def progress_log(symbol, progress=1, **keyargs)
|
100
|
+
return nil if !@progress or @part == 1
|
101
|
+
data = @data[symbol]
|
102
|
+
return nil if data.nil?
|
103
|
+
data.counter += progress
|
104
|
+
if data.counter == data.total
|
105
|
+
progress_end(symbol)
|
106
|
+
elsif data.partsize == 0 or
|
107
|
+
(data.counter / data.partsize).to_i > data.lastpart
|
108
|
+
return nil if data.partsize == 0 and @part > 0
|
109
|
+
# this means total is very small
|
110
|
+
data.lastpart = data.counter / data.partsize if data.partsize > 0
|
111
|
+
done = data.counter.to_f / data.total
|
112
|
+
t = Time.now - data.starttime
|
113
|
+
eta = (t / done) - t
|
114
|
+
tstr= ("Elapsed: %02dh %02dmin %02ds" % [t/3600, t/60%60, t%60])
|
115
|
+
etastr = ("ETA: %02dh %02dmin %02ds" % [eta/3600, eta/60%60, eta%60])
|
116
|
+
donestr = "%.1f" % (done*100)
|
117
|
+
keystr = ""
|
118
|
+
keyargs.each {|k,v| keystr << "; #{k}: #{v}"}
|
119
|
+
str = "#@pfx #{donestr}% #{data.units} processed "+
|
120
|
+
"[#{tstr}; #{etastr}#{keystr}]"
|
121
|
+
if str.size > data.strlen
|
122
|
+
data.strlen = str.size
|
123
|
+
spacediff = ""
|
124
|
+
else
|
125
|
+
spacediff = " "*(data.strlen-str.size)
|
126
|
+
end
|
127
|
+
@channel.print "\r#{str}#{spacediff}"
|
128
|
+
@channel.flush
|
129
|
+
end
|
130
|
+
return nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# @!macro [new] progress_end
|
134
|
+
# Completes progress logging for a computation
|
135
|
+
# @!macro prlog
|
136
|
+
# @return [void]
|
137
|
+
def progress_end(symbol, **keyargs)
|
138
|
+
return if !@progress
|
139
|
+
data = @data[symbol]
|
140
|
+
return if data.nil?
|
141
|
+
t = Time.now - data.starttime
|
142
|
+
tstr= ("Elapsed time: %02dh %02dmin %02ds" % [t/3600, t/60%60, t%60])
|
143
|
+
quantity = @part == 1 ? data.total.to_s : "100.0%"
|
144
|
+
keystr = ""
|
145
|
+
keyargs.each {|k,v| keystr << "; #{k}: #{v}"}
|
146
|
+
str = "#@pfx #{quantity} #{data.units} processed [#{tstr}#{keystr}]"
|
147
|
+
spacediff = " "*([data.strlen - str.size,0].max)
|
148
|
+
@channel.print "\r" if @part != 1
|
149
|
+
@channel.puts "#{str}#{spacediff}"
|
150
|
+
@channel.flush
|
151
|
+
@data.delete(symbol)
|
152
|
+
return nil
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
# Progress logging related-methods for RGFA class
|
158
|
+
module RGFA::LoggerSupport
|
159
|
+
|
160
|
+
# Activate logging of progress
|
161
|
+
# @return [RGFA] self
|
162
|
+
def enable_progress_logging(part: 0.1, channel: STDERR)
|
163
|
+
@progress = RGFA::Logger.new(channel: channel)
|
164
|
+
@progress.enable_progress(part: part)
|
165
|
+
return self
|
166
|
+
end
|
167
|
+
|
168
|
+
# @!macro progress_init
|
169
|
+
# @return [RGFA] self
|
170
|
+
# @api private
|
171
|
+
def progress_log_init(symbol, units, total, initmsg = nil)
|
172
|
+
@progress.progress_init(symbol, units, total, initmsg) if @progress
|
173
|
+
return self
|
174
|
+
end
|
175
|
+
|
176
|
+
# @!macro progress_log
|
177
|
+
# @return [RGFA] self
|
178
|
+
# @api private
|
179
|
+
def progress_log(symbol, progress=1, **keyargs)
|
180
|
+
@progress.progress_log(symbol, progress) if @progress
|
181
|
+
return self
|
182
|
+
end
|
183
|
+
|
184
|
+
# @!macro progress_end
|
185
|
+
# @return [RGFA] self
|
186
|
+
# @api private
|
187
|
+
def progress_log_end(symbol, **keyargs)
|
188
|
+
@progress.progress_end(symbol) if @progress
|
189
|
+
return self
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|