redmine_api_helper 0.3.24
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of redmine_api_helper might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +11 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE +339 -0
- data/README.md +30 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/date_helper/date.rb +311 -0
- data/lib/odf_writer/bookmark.rb +110 -0
- data/lib/odf_writer/bookmark_reader.rb +77 -0
- data/lib/odf_writer/document.rb +372 -0
- data/lib/odf_writer/field.rb +174 -0
- data/lib/odf_writer/field_reader.rb +78 -0
- data/lib/odf_writer/image.rb +158 -0
- data/lib/odf_writer/image_reader.rb +76 -0
- data/lib/odf_writer/images.rb +89 -0
- data/lib/odf_writer/list_style.rb +331 -0
- data/lib/odf_writer/nested.rb +156 -0
- data/lib/odf_writer/odf_helper.rb +56 -0
- data/lib/odf_writer/parser/default.rb +685 -0
- data/lib/odf_writer/path_finder.rb +114 -0
- data/lib/odf_writer/section.rb +120 -0
- data/lib/odf_writer/section_reader.rb +61 -0
- data/lib/odf_writer/style.rb +417 -0
- data/lib/odf_writer/table.rb +135 -0
- data/lib/odf_writer/table_reader.rb +61 -0
- data/lib/odf_writer/template.rb +222 -0
- data/lib/odf_writer/text.rb +97 -0
- data/lib/odf_writer/text_reader.rb +77 -0
- data/lib/odf_writer/version.rb +29 -0
- data/lib/redmine_api_helper/api_helper.rb +333 -0
- data/lib/redmine_api_helper/args_helper.rb +106 -0
- data/lib/redmine_api_helper/attachments_api_helper.rb +52 -0
- data/lib/redmine_api_helper/define_api_helpers.rb +78 -0
- data/lib/redmine_api_helper/document_categories_api_helper.rb +38 -0
- data/lib/redmine_api_helper/groups_api_helper.rb +80 -0
- data/lib/redmine_api_helper/helpers.rb +50 -0
- data/lib/redmine_api_helper/issue_priorities_api_helper.rb +38 -0
- data/lib/redmine_api_helper/issue_relations_api_helper.rb +66 -0
- data/lib/redmine_api_helper/issue_statuses_api_helper.rb +36 -0
- data/lib/redmine_api_helper/issues_api_helper.rb +124 -0
- data/lib/redmine_api_helper/my_account_api_helper.rb +45 -0
- data/lib/redmine_api_helper/news_api_helper.rb +73 -0
- data/lib/redmine_api_helper/project_memberships_api_helper.rb +77 -0
- data/lib/redmine_api_helper/projects_api_helper.rb +73 -0
- data/lib/redmine_api_helper/roles_api_helper.rb +52 -0
- data/lib/redmine_api_helper/scripts_api_helper.rb +87 -0
- data/lib/redmine_api_helper/search_api_helper.rb +38 -0
- data/lib/redmine_api_helper/time_entries_api_helper.rb +73 -0
- data/lib/redmine_api_helper/time_entry_activities_api_helper.rb +38 -0
- data/lib/redmine_api_helper/trackers_api_helper.rb +38 -0
- data/lib/redmine_api_helper/users_api_helper.rb +73 -0
- data/lib/redmine_api_helper/version.rb +24 -0
- data/lib/redmine_api_helper/wiki_pages_api_helper.rb +66 -0
- data/lib/redmine_api_helper.rb +88 -0
- data/redmine_api_helper.gemspec +35 -0
- metadata +148 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "deep_try"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,311 @@
|
|
1
|
+
##
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# Extension for Date to calculate a forward date with compressed syntax
|
5
|
+
#
|
6
|
+
# Copyright 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>
|
7
|
+
#
|
8
|
+
# This program is free software; you can redistribute it and/or
|
9
|
+
# modify it under the terms of the GNU General Public License
|
10
|
+
# as published by the Free Software Foundation; either version 2
|
11
|
+
# of the License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU General Public License
|
19
|
+
# along with this program; if not, write to the Free Software
|
20
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
21
|
+
#
|
22
|
+
|
23
|
+
class Date
|
24
|
+
|
25
|
+
####################################################################################
|
26
|
+
#
|
27
|
+
# Date.today.calc(<rule>) -> [<new_date>, <new_rule>]
|
28
|
+
# Date.today.forward(<rule) -> <new_date>
|
29
|
+
#
|
30
|
+
# <rule>: [DWmMqY][n][FMLWX][-!W*]
|
31
|
+
#
|
32
|
+
# 1. parameter: epoch-identifier (necessary)
|
33
|
+
#
|
34
|
+
# D - stands for _D_ays
|
35
|
+
# W - stands for _W_eeks
|
36
|
+
# m - stands for _m_ondays
|
37
|
+
# M - stands for _M_onths
|
38
|
+
# q - stands for _q_uarters
|
39
|
+
# Y - stands for _Y_ears
|
40
|
+
#
|
41
|
+
# 2. parameter: integer (necessary)
|
42
|
+
#
|
43
|
+
# n - stands for the number of epochs
|
44
|
+
#
|
45
|
+
# 3. position of day within epoch
|
46
|
+
#
|
47
|
+
# F - stands for _F_irst day of epoch, like first day of month, monday, first day of
|
48
|
+
# quarter, or January 1st
|
49
|
+
# M - stands for _M_id of epoch, like 15th day of month, wednesday, 15th of
|
50
|
+
# mid-of-quarter or June 30th
|
51
|
+
# L - stands for _L_ast of epoch, like last day of month, friday (last working day),
|
52
|
+
# last day of quarter or December 31st
|
53
|
+
# W - stands for _W_eekday (Monday), if calculated day falls on a Saturday or Sunday
|
54
|
+
# X - stands for no correction
|
55
|
+
#
|
56
|
+
# 3. control of date calculation
|
57
|
+
#
|
58
|
+
# - - the sign "-" is a killswitch. After one date calculation the resubmission rule is
|
59
|
+
# deleted, so no further resubmissions happen
|
60
|
+
# ! - the sign "!" is a force sign to force date calculation even if a resubmission date
|
61
|
+
# present
|
62
|
+
# W - the sign "W" in this position corrects the date to the next working day
|
63
|
+
# * - the sign "*" is a mock switch. The mock-switch is deleted from the resubmission-rule
|
64
|
+
# and no resubmission-date is calculated. Needed for Redmine Attribute-Quickie plugin
|
65
|
+
#
|
66
|
+
# Example: W1F - one week further, first day, so monday
|
67
|
+
# Example: M3M - three months further from today, mid-term, so 15th of month
|
68
|
+
# Example: q1- - next quarter, first day of quarter, so 1st Jan., Apr., Jul. or Oct., after
|
69
|
+
# one calculation further calculations are stopped, rule is deleted.
|
70
|
+
# Example: D1- - tomorrow, then delete rule
|
71
|
+
# Example: D1-! - tomorrow!, regardsless of date
|
72
|
+
#
|
73
|
+
# Resubmission dates are always calculated for the future, never for the past.
|
74
|
+
# So W0F would calculate "next monday"", if calculated on a friday, though W0 stands for
|
75
|
+
# this week (W0 zero weeks further, first day, monday) and would calculate last monday.
|
76
|
+
# In this case, the calculated date is advanced monday further into the future.
|
77
|
+
# So q0M would calculate "this quarter mid-term" if calculated near to lapse of current
|
78
|
+
# quarter. In this case the calculated date is advanced one quarter into the future.
|
79
|
+
####################################################################################
|
80
|
+
|
81
|
+
####################################################################################
|
82
|
+
# calc
|
83
|
+
####################################################################################
|
84
|
+
def calc( rule )
|
85
|
+
|
86
|
+
new_date = nil
|
87
|
+
new_rule = nil
|
88
|
+
|
89
|
+
if rule.present?
|
90
|
+
|
91
|
+
m = parse_rule( rule )
|
92
|
+
|
93
|
+
if m['mockswitch']
|
94
|
+
|
95
|
+
# mockswitch does not calculate anything
|
96
|
+
# mockswitch is removed from new_rule, however
|
97
|
+
new_rule = unmock( rule, m )
|
98
|
+
|
99
|
+
elsif self <= Date.today || m['force']
|
100
|
+
|
101
|
+
if( m['epoch'] && m['num'] )
|
102
|
+
|
103
|
+
new_date = move( m['epoch'], m['num'] )
|
104
|
+
new_date = new_date.adjust( m['epoch'], m['pos'], self )
|
105
|
+
new_date = new_date.adjust("D", "W", new_date) if m["workingday"] # adjust working day
|
106
|
+
|
107
|
+
if m['killswitch']
|
108
|
+
new_rule = ""
|
109
|
+
else
|
110
|
+
new_rule = rule
|
111
|
+
end
|
112
|
+
|
113
|
+
end #if
|
114
|
+
end #if
|
115
|
+
end #if
|
116
|
+
|
117
|
+
[new_date, new_rule]
|
118
|
+
|
119
|
+
end #def
|
120
|
+
|
121
|
+
####################################################################################
|
122
|
+
# forward
|
123
|
+
####################################################################################
|
124
|
+
def forward( rule )
|
125
|
+
calc( rule ).first
|
126
|
+
end #def
|
127
|
+
|
128
|
+
####################################################################################
|
129
|
+
# calculate num times advance of epoch
|
130
|
+
# epoch: D - n days
|
131
|
+
# epoch: W - n weeks
|
132
|
+
# epoch: M - n months
|
133
|
+
# epoch: Y - n years
|
134
|
+
#
|
135
|
+
# epoch: C - n calendar weeks (absolute, within this year, not relative)
|
136
|
+
# epoch: m - n mondays
|
137
|
+
# epoch: q - q quarters
|
138
|
+
####################################################################################
|
139
|
+
def move( epoch, num )
|
140
|
+
case epoch
|
141
|
+
when "D"
|
142
|
+
self + num.to_i
|
143
|
+
when "W"
|
144
|
+
self + num.to_i * 7
|
145
|
+
when "M"
|
146
|
+
self >> num.to_i
|
147
|
+
when "Y"
|
148
|
+
self >> num.to_i * 12
|
149
|
+
when "m"
|
150
|
+
(self + num.to_i * 7).monday
|
151
|
+
when "q"
|
152
|
+
(self >> num.to_i * 3).beginning_of_quarter
|
153
|
+
when "C"
|
154
|
+
# calendar week 1 is the week containing Jan. 4th
|
155
|
+
change(:month => 1, :day => 4).advance( :weeks => (num.to_i - 1))
|
156
|
+
else
|
157
|
+
self
|
158
|
+
end #case
|
159
|
+
end #def
|
160
|
+
|
161
|
+
####################################################################################
|
162
|
+
# calculate time adjustmentfor pos in epoch (_F_irst, _M_id, _L_ast, _W_orking day)
|
163
|
+
# epoch: D - day: W - Monday
|
164
|
+
# epoch: W - week: F - Monday, M - Wednesday, L - Friday, W - Monday
|
165
|
+
# epoch: M - month: F - 1st, M - 15th, L - last, W - Monday
|
166
|
+
# epoch: Y - year: F - 01/01, M - 06/30, L - 12/31, W - Monday
|
167
|
+
# epoch: q - quarter: W - Monday
|
168
|
+
####################################################################################
|
169
|
+
def adjust( epoch, pos, ref=self )
|
170
|
+
|
171
|
+
case epoch
|
172
|
+
when "D"
|
173
|
+
case pos
|
174
|
+
when "W"
|
175
|
+
# if saturday or sunday, fall back to last monday, then add one week for monday coming up
|
176
|
+
(wday % 6) != 0 ? self : monday.advance(:days => 7)
|
177
|
+
else
|
178
|
+
self
|
179
|
+
end #case
|
180
|
+
|
181
|
+
when "W", "C"
|
182
|
+
# week
|
183
|
+
case pos
|
184
|
+
when "F"
|
185
|
+
# Monday
|
186
|
+
monday < ref ? advance( :weeks => 1).monday : monday
|
187
|
+
|
188
|
+
when "M"
|
189
|
+
# Wednesday = Monday + 2 days
|
190
|
+
monday.advance(:days => 2) < ref ? advance( :weeks => 1).monday.advance(:days => 2) : monday.advance(:days => 2)
|
191
|
+
|
192
|
+
when "L"
|
193
|
+
# Friday = Monday + 4 days
|
194
|
+
monday.advance(:days => 4) < ref ? advance( :weeks => 1).monday.advance(:days => 4) : monday.advance(:days => 4)
|
195
|
+
|
196
|
+
when "W"
|
197
|
+
# if saturday or sunday, fall back to last monday, then add one week for monday coming up
|
198
|
+
(wday % 6) != 0 ? self : monday.advance(:days => 7)
|
199
|
+
|
200
|
+
else
|
201
|
+
self
|
202
|
+
end #case
|
203
|
+
|
204
|
+
when "M"
|
205
|
+
# month
|
206
|
+
case pos
|
207
|
+
when "F"
|
208
|
+
# 1st
|
209
|
+
change(:day => 1) < ref ? advance( :months => 1).beginning_of_month.change(:day => 1) : change(:day => 1)
|
210
|
+
|
211
|
+
when "M"
|
212
|
+
# 15th
|
213
|
+
change(:day => 15) < ref ? advance( :months => 1).beginning_of_month.change(:day => 15) : change(:day => 15)
|
214
|
+
|
215
|
+
when "L"
|
216
|
+
# last day
|
217
|
+
end_of_month
|
218
|
+
|
219
|
+
when "W"
|
220
|
+
# if saturday or sunday, fall back to last monday, then add one week for monday coming up
|
221
|
+
(wday % 6) != 0 ? self : monday.advance(:days => 7)
|
222
|
+
else
|
223
|
+
self
|
224
|
+
end #case
|
225
|
+
|
226
|
+
when "Y"
|
227
|
+
# year
|
228
|
+
case pos
|
229
|
+
when "F"
|
230
|
+
# Jan. 1st
|
231
|
+
beginning_of_year < ref ? advanve(:years => 1).beginning_of_year : beginning_of_year
|
232
|
+
|
233
|
+
when "M"
|
234
|
+
# Jun. 30th
|
235
|
+
change(:month => 5, :day => 30) < ref ? advance(:years => 1).change(:month => 5, :day => 30) : change(:month => 5, :day => 30)
|
236
|
+
|
237
|
+
when "L"
|
238
|
+
# Dec. 31st
|
239
|
+
change(:month => 12, :day => 31)
|
240
|
+
|
241
|
+
when "W"
|
242
|
+
# if saturday or sunday, fall back to last monday, then add one week for monday coming up
|
243
|
+
(wday % 6) != 0 ? self : monday.advance(:days => 7)
|
244
|
+
else
|
245
|
+
self
|
246
|
+
end #case
|
247
|
+
|
248
|
+
when "q"
|
249
|
+
case pos
|
250
|
+
when "F"
|
251
|
+
# Jan. 1st
|
252
|
+
beginning_of_quarter < ref ? advance( :months => 3).beginning_of_quarter : beginning_of_quarter
|
253
|
+
|
254
|
+
when "M"
|
255
|
+
# mid quarter
|
256
|
+
beginning_of_quarter.advance(:months => 1).advance(:days => 14) < ref ? advance(:quarters => 1).beginning_of_quarter.advance(:months => 1).advance(:days => 14) : beginning_of_quarter.advance(:months => 1).advance(:days => 14)
|
257
|
+
|
258
|
+
when "L"
|
259
|
+
# end quarter
|
260
|
+
end_of_quarter
|
261
|
+
|
262
|
+
when "W"
|
263
|
+
# if saturday or sunday, fall back to last monday, then add one week for monday coming up
|
264
|
+
(wday % 6) != 0 ? self : monday.advance(:days => 7)
|
265
|
+
else
|
266
|
+
self
|
267
|
+
end #case
|
268
|
+
|
269
|
+
else
|
270
|
+
self
|
271
|
+
end #case epoch
|
272
|
+
end #def
|
273
|
+
|
274
|
+
|
275
|
+
####################################################################################
|
276
|
+
# private
|
277
|
+
####################################################################################
|
278
|
+
private
|
279
|
+
|
280
|
+
####################################################################################
|
281
|
+
# parse_rule
|
282
|
+
####################################################################################
|
283
|
+
def parse_rule(rule)
|
284
|
+
|
285
|
+
m = {}
|
286
|
+
matches = /(?<epoch>[DWMYCmq])(?<num>[0-9]+)(?<pos>[XFMLW]?)(?<kfmw>[-!W\*]*)(?<trailing_rest>.*)/.match(rule)
|
287
|
+
|
288
|
+
if matches.present?
|
289
|
+
m.merge!(Hash[ matches.names.zip( matches.captures ) ])
|
290
|
+
m.merge!('killswitch' => m['kfmw'].match(/-/).to_s.presence)
|
291
|
+
m.merge!('force' => m['kfmw'].match(/!/).to_s.presence)
|
292
|
+
m.merge!('mockswitch' => m['kfmw'].match(/\*/).to_s.presence)
|
293
|
+
m.merge!('workingday' => m['kfmw'].match(/W/).to_s.presence)
|
294
|
+
end
|
295
|
+
m.compact
|
296
|
+
end #def
|
297
|
+
|
298
|
+
####################################################################################
|
299
|
+
# parse_rule
|
300
|
+
####################################################################################
|
301
|
+
def unmock( rule, m=nil )
|
302
|
+
m = parse_rule( rule ) unless m
|
303
|
+
if m['mockswitch'].present?
|
304
|
+
"#{m['epoch']}#{m['num']}#{m['pos']}#{m['killswitch']}#{m['force']}#{m['trailing_rest']}"
|
305
|
+
else
|
306
|
+
nil
|
307
|
+
end
|
308
|
+
end #def
|
309
|
+
|
310
|
+
end #class
|
311
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Ruby Gem to create a self populating Open Document Format (.odf) text file.
|
4
|
+
#
|
5
|
+
# Copyright 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License
|
9
|
+
# as published by the Free Software Foundation; either version 2
|
10
|
+
# of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
20
|
+
#
|
21
|
+
|
22
|
+
module ODFWriter
|
23
|
+
|
24
|
+
########################################################################################
|
25
|
+
#
|
26
|
+
# Bookmark: replace bookmarks with given name
|
27
|
+
#
|
28
|
+
########################################################################################
|
29
|
+
class Bookmark < Field
|
30
|
+
|
31
|
+
######################################################################################
|
32
|
+
#
|
33
|
+
# replace!
|
34
|
+
#
|
35
|
+
######################################################################################
|
36
|
+
def replace!(doc, item = nil)
|
37
|
+
|
38
|
+
nodes = find_bookmark_nodes(doc)
|
39
|
+
return if nodes.blank?
|
40
|
+
|
41
|
+
text = value(item)
|
42
|
+
text = text.encode(universal_newline: true)
|
43
|
+
|
44
|
+
text_node_array = text.split(/\n/).map{|a| Nokogiri::XML::Text.new(a, doc) }
|
45
|
+
unless text_node_array.length == 1
|
46
|
+
text_node_array = text_node_array.inject([]) do |collector, node|
|
47
|
+
collector << Nokogiri::XML::Node.new("line-break", doc) unless collector.empty?
|
48
|
+
collector << node
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
nodes.each do |node|
|
53
|
+
|
54
|
+
case node.name
|
55
|
+
|
56
|
+
when "bookmark"
|
57
|
+
text_node_array.each {|tn| node.before(tn)}
|
58
|
+
|
59
|
+
#
|
60
|
+
# find bookmark
|
61
|
+
#
|
62
|
+
bm = "text:bookmark[@text:name='#{@name}']"
|
63
|
+
bmn = doc.xpath(".//.//*[self::#{bm}]")
|
64
|
+
#
|
65
|
+
# delete bookmark
|
66
|
+
#
|
67
|
+
bmn.each {|b| b.remove }
|
68
|
+
|
69
|
+
when "bookmark-start"
|
70
|
+
text_node_array.each {|tn| node.before(tn)}
|
71
|
+
|
72
|
+
#
|
73
|
+
# find bookmark-start
|
74
|
+
#
|
75
|
+
bms = "text:bookmark-start[@text:name='#{@name}']"
|
76
|
+
bmsn = doc.xpath(".//.//*[self::#{bms}]")
|
77
|
+
#
|
78
|
+
# find bookmark text
|
79
|
+
#
|
80
|
+
bme = "text:bookmark-end[@text:name='#{@name}']"
|
81
|
+
bmen = doc.xpath(".//.//*[self::#{bme}]")
|
82
|
+
#
|
83
|
+
# find bookmark-end
|
84
|
+
#
|
85
|
+
bmn = doc.xpath(".//text()[preceding-sibling::#{bms} and following-sibling::#{bme}]")
|
86
|
+
#
|
87
|
+
# delete bookmark -start, text and -end
|
88
|
+
#
|
89
|
+
bmn.each {|b| b.remove }
|
90
|
+
bmsn.each {|b| b.remove }
|
91
|
+
bmen.each {|b| b.remove }
|
92
|
+
|
93
|
+
end #case
|
94
|
+
end #each
|
95
|
+
|
96
|
+
end #def
|
97
|
+
|
98
|
+
######################################################################################
|
99
|
+
#
|
100
|
+
# private
|
101
|
+
#
|
102
|
+
######################################################################################
|
103
|
+
private
|
104
|
+
|
105
|
+
def find_bookmark_nodes(doc)
|
106
|
+
doc.xpath(".//*[self::text:bookmark[@text:name='#{@name}'] or self::text:bookmark-start[@text:name='#{@name}']]")
|
107
|
+
end #def
|
108
|
+
|
109
|
+
end #class
|
110
|
+
end #module
|