clir-data_manager 0.3.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/.gitignore +10 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +44 -0
- data/Manual/Manuel_fr.md +784 -0
- data/Manual/Manuel_fr.pdf +0 -0
- data/README.md +279 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/clir-data_manager.gemspec +34 -0
- data/lib/clir/data_manager/Displayer.rb +30 -0
- data/lib/clir/data_manager/Editor.rb +208 -0
- data/lib/clir/data_manager/Manager.rb +1184 -0
- data/lib/clir/data_manager/Periode.rb +366 -0
- data/lib/clir/data_manager/PrecedencedList.rb +98 -0
- data/lib/clir/data_manager/Property.rb +438 -0
- data/lib/clir/data_manager/Validator.rb +157 -0
- data/lib/clir/data_manager/constants.rb +14 -0
- data/lib/clir/data_manager/errors_and_messages.rb +123 -0
- data/lib/clir/data_manager/module_constants.rb +13 -0
- data/lib/clir/data_manager/version.rb +5 -0
- data/lib/clir/data_manager.rb +21 -0
- metadata +114 -0
@@ -0,0 +1,366 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Class Periode
|
4
|
+
-------------
|
5
|
+
Pour gérer une période de temps, à partir d'une date (:from_date)
|
6
|
+
jusqu'à une autre date (:to_date)
|
7
|
+
|
8
|
+
Initialisation possible avec :
|
9
|
+
- des vraies dates (Date)
|
10
|
+
- des vrais temps (Time)
|
11
|
+
- des dates stings en "JJ/MM/AAAA" (ATTENTION ! Format FR)
|
12
|
+
- des dates strings inverses "AAAA/MM/JJ"
|
13
|
+
- des dates strings raccourcis, par exemple seulement l'année
|
14
|
+
ou le mois
|
15
|
+
- des timestamps/nombre de secondes
|
16
|
+
|
17
|
+
On obtient ensuite des méthodes pratiques et particulièrement la
|
18
|
+
méthode :human_period qui détecte si la date définit une année,
|
19
|
+
un trimestre, un mois, etc.
|
20
|
+
|
21
|
+
<Periode>#human_period ou #as_human
|
22
|
+
=> Période au format humain
|
23
|
+
("année xxxx", "1er trimestre 2014", "mois d’avril 2009",
|
24
|
+
"semaine 25 de l’année 2012",
|
25
|
+
"du 1er avril au 1er juin 2022")
|
26
|
+
|
27
|
+
<Periode>#du_au
|
28
|
+
=> "du <date départ> au <date fin>"
|
29
|
+
|
30
|
+
<Periode>#from
|
31
|
+
=> date de départ ({Time})
|
32
|
+
|
33
|
+
<Periode>#to
|
34
|
+
=> date {Time} de fin
|
35
|
+
|
36
|
+
<Periode>#time_in?(time)
|
37
|
+
=> true si le temps +time+ est dans la période
|
38
|
+
|
39
|
+
|
40
|
+
=end
|
41
|
+
require 'date'
|
42
|
+
# require_relative 'Date_utils'
|
43
|
+
|
44
|
+
class Periode
|
45
|
+
|
46
|
+
# Pour définir une période (une date de départ et de fin)
|
47
|
+
def self.choose(params = nil)
|
48
|
+
params ||= {}
|
49
|
+
params.merge!(from_date: params.delete(:default)) if params.key?(:default)
|
50
|
+
params.key?(:from_date) || params.merge!(from_date: nil)
|
51
|
+
params.key?(:to_date) || params.merge!(to_date: nil)
|
52
|
+
params[:from_date] = params[:from_date].to_s if params[:from_date].is_a?(Integer)
|
53
|
+
while true
|
54
|
+
date_default = params[:from_date] || Time.now
|
55
|
+
if not date_default.is_a?(String)
|
56
|
+
date_default = date_default.strftime('%d/%m/%Y')
|
57
|
+
end
|
58
|
+
params[:from_date] = ask_for_a_date("De la date", date_default)
|
59
|
+
if params[:from_date].is_a?(String)
|
60
|
+
if params[:from_date].match?(/^[0-9]{4}$/)
|
61
|
+
params.merge!({
|
62
|
+
from_date: "01/01/#{params[:from_date]}",
|
63
|
+
to_date: "01/01/#{params[:from_date].to_i + 1}"
|
64
|
+
})
|
65
|
+
break
|
66
|
+
elsif params[:from_date].match?(/^[0-9]{1,2}\/[0-9]{4}$/)
|
67
|
+
#
|
68
|
+
# Seulement le mois donné
|
69
|
+
#
|
70
|
+
mois, annee = params[:from_date].split('/').collect{|n|n.to_i}
|
71
|
+
fromdate, todate =
|
72
|
+
if mois == 12
|
73
|
+
["01/12/#{annee}", "01/01/#{annee + 1}"]
|
74
|
+
else
|
75
|
+
["01/#{mois.to_s.rjust(2,'0')}/#{annee}", "01/#{(mois + 1).to_s.rjust(2,'0')}/#{annee}"]
|
76
|
+
end
|
77
|
+
params.merge!(from_date:fromdate, to_date:todate)
|
78
|
+
break
|
79
|
+
end
|
80
|
+
else
|
81
|
+
params[:to_date] = ask_for_a_date("À la date", date_default)
|
82
|
+
# --- Vérifications ---
|
83
|
+
if params[:from_date] > params[:to_date]
|
84
|
+
erreur "La date de début ne peut être supérieure à la date de fin…"
|
85
|
+
else
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end #/boucle
|
90
|
+
|
91
|
+
return new(params[:from_date], params[:to_date], params[:options])
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
attr_reader :from_date, :to_date
|
96
|
+
|
97
|
+
def initialize(from_date, to_date = nil, options = nil)
|
98
|
+
from_date, to_date = defaultize(from_date, to_date)
|
99
|
+
@from_date = date_from(from_date)
|
100
|
+
@to_date = date_from(to_date)
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
@inspect ||= "#<Periode #{as_la_periode}>"
|
105
|
+
end
|
106
|
+
|
107
|
+
# @public
|
108
|
+
#
|
109
|
+
# @return [Boolean] Si le temps +time+ (qui peut être exprimé par
|
110
|
+
# une multitude de choses) se trouve dans la période courante
|
111
|
+
# @param [Any] time Le temps à tester, qui doit pouvoir être évalué par 'date_from'
|
112
|
+
# [String] "JJ/MM/AAAA"
|
113
|
+
# [Integer]
|
114
|
+
def time_in?(time)
|
115
|
+
return false if time.nil?
|
116
|
+
time = date_from(time)
|
117
|
+
return time >= from_date && time <= to_date
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Méthode qui reçoit les deux premiers arguments de l'initiation
|
122
|
+
# pour les interpréter au besoin. Par exemple, on peut fournir
|
123
|
+
# seulement une année (dans +from_date+) ou un mois (MM/AAAA)
|
124
|
+
#
|
125
|
+
# [N0001]
|
126
|
+
# Une année chiffrée (2022) peut avoir été donnée
|
127
|
+
#
|
128
|
+
REG_ANNEE = /^[0-9]{4}$/
|
129
|
+
REG_MOIS = /^[0-9]{1,2}\/[0-9]{4}$/
|
130
|
+
def defaultize(from_date, to_date)
|
131
|
+
return [from_date, to_date] if to_date.is_a?(Time) || to_date.is_a?(Date)
|
132
|
+
from_date = from_date.to_s # cf. [N0001]
|
133
|
+
if to_date.nil?
|
134
|
+
if from_date.match?(REG_ANNEE)
|
135
|
+
#
|
136
|
+
# Période d'une année
|
137
|
+
#
|
138
|
+
annee = from_date.to_i
|
139
|
+
from_date = "01/01/#{annee}"
|
140
|
+
to_date = "01/01/#{annee + 1}"
|
141
|
+
elsif from_date.match?(REG_MOIS)
|
142
|
+
#
|
143
|
+
# Période d'un mois
|
144
|
+
#
|
145
|
+
mois, annee = from_date.split('/')
|
146
|
+
from_date = "01/#{mois.rjust(2,'0')}/#{annee}"
|
147
|
+
if mois.to_i == 12
|
148
|
+
mois, annee = [0, annee.to_i + 1]
|
149
|
+
end
|
150
|
+
to_date = "01/#{(mois.to_i+1).to_s.rjust(2,'0')}/#{annee}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
return [from_date, to_date]
|
154
|
+
end
|
155
|
+
|
156
|
+
# - raccourcis -
|
157
|
+
def from; from_date end
|
158
|
+
def to; to_date end
|
159
|
+
|
160
|
+
# - clé pour table -
|
161
|
+
def as_hash_key
|
162
|
+
@as_hash_key ||= "#{from.to_i}-#{to.to_i}"
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# @return la durée en secondes de la période
|
167
|
+
def duration
|
168
|
+
@duration ||= to_timestamp - from_timestamp
|
169
|
+
end
|
170
|
+
|
171
|
+
def from_timestamp
|
172
|
+
@from_timestamp ||= from_date.to_i
|
173
|
+
end
|
174
|
+
def to_timestamp
|
175
|
+
@to_timestamp ||= to_date.to_i
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# @return "l'année ..." ou "Le trimestre", etc.
|
180
|
+
#
|
181
|
+
def as_la_periode
|
182
|
+
@as_la_periode ||= begin
|
183
|
+
if debut_et_fin_de_semaine?
|
184
|
+
"la #{as_human}"
|
185
|
+
elsif premier_jour_mois?
|
186
|
+
if debut_et_fin_de_mois?
|
187
|
+
"le mois d#{human_month.match?(/^[ao]/) ? '’' : 'e '}#{human_month} #{human_year}"
|
188
|
+
elsif debut_et_fin_trimestre?
|
189
|
+
"#{hindex_trimestre} trimestre #{human_year}"
|
190
|
+
elsif debut_et_fin_annee?
|
191
|
+
"l’année #{human_year}"
|
192
|
+
else
|
193
|
+
"le #{human_period_jours}"
|
194
|
+
end
|
195
|
+
else
|
196
|
+
"le #{human_period_jours}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
alias :to_s :as_la_periode
|
201
|
+
|
202
|
+
##
|
203
|
+
# @return un texte comme "de l'année 2021"
|
204
|
+
def as_du_au
|
205
|
+
@as_du_au ||= begin
|
206
|
+
if debut_et_fin_de_semaine?
|
207
|
+
"de la #{as_human}"
|
208
|
+
elsif premier_jour_mois?
|
209
|
+
if debut_et_fin_de_mois?
|
210
|
+
"du mois d#{human_month.match?(/^[ao]/) ? '’' : 'e '}#{human_month} #{human_year}"
|
211
|
+
elsif debut_et_fin_trimestre?
|
212
|
+
"#{hindex_trimestre} trimestre #{human_year}"
|
213
|
+
elsif debut_et_fin_annee?
|
214
|
+
"de l’année #{human_year}"
|
215
|
+
else
|
216
|
+
"du #{human_period_jours}"
|
217
|
+
end
|
218
|
+
else
|
219
|
+
"du #{human_period_jours}"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def human_period
|
225
|
+
@human_period ||= begin
|
226
|
+
# Est-ce une semaine ?
|
227
|
+
if debut_et_fin_de_semaine?
|
228
|
+
"semaine #{from_date.to_date.cweek} de l’année #{human_year}"
|
229
|
+
elsif premier_jour_mois?
|
230
|
+
if debut_et_fin_de_mois?
|
231
|
+
"mois d#{human_month.match?(/^[ao]/) ? '’' : 'e '}#{human_month} #{human_year}"
|
232
|
+
elsif debut_et_fin_trimestre?
|
233
|
+
"#{hindex_trimestre} trimestre #{human_year}"
|
234
|
+
elsif debut_et_fin_annee?
|
235
|
+
"Année #{human_year}"
|
236
|
+
else
|
237
|
+
human_period_jours
|
238
|
+
end
|
239
|
+
else
|
240
|
+
human_period_jours
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
alias :as_human :human_period
|
245
|
+
|
246
|
+
##
|
247
|
+
# @return la période simple en jour (lorsqu'aucun période humaine
|
248
|
+
# n'a été trouvée)
|
249
|
+
#
|
250
|
+
def human_period_jours
|
251
|
+
@human_period_jours ||= begin
|
252
|
+
formated_from = formate_date(from_date, **{no_time: true, update_format:true, verbal: true})
|
253
|
+
if from_date.year == to_date.year
|
254
|
+
# Les années sont identiques, on les rassemble à la fin
|
255
|
+
formated_from = formated_from[0..-6]
|
256
|
+
end
|
257
|
+
"#{formated_from} au #{formate_date(to_date, **{no_time: true, update_format:true, verbal: true})}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def as_human_jours
|
262
|
+
@as_human_jours ||= "du #{human_period_jours}"
|
263
|
+
end
|
264
|
+
alias :du_au :as_human_jours
|
265
|
+
|
266
|
+
##
|
267
|
+
# @return TRUE si la période correspond à un début et une fin de
|
268
|
+
# semaine
|
269
|
+
def debut_et_fin_de_semaine?
|
270
|
+
:TRUE == @is_debut_et_fin_de_semaine ||= begin
|
271
|
+
from_date.wday == 1 && duration.proche?(7.days) ? :TRUE : :FALSE
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# @return TRUE si la période commence le premier jour d'un mois
|
277
|
+
def premier_jour_mois?
|
278
|
+
from_date.day == 1
|
279
|
+
end
|
280
|
+
|
281
|
+
##
|
282
|
+
# @return TRUE si la période correspond à un mois
|
283
|
+
#
|
284
|
+
def debut_et_fin_de_mois?
|
285
|
+
from_date.day == 1 || return
|
286
|
+
:TRUE == @is_debut_et_fin_de_mois ||= begin
|
287
|
+
d = (from_date.to_date >> 1).to_time
|
288
|
+
to_date.proche?(d) ? :TRUE : :FALSE
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
# @return TRUE si la période correspond à un trimestre
|
294
|
+
#
|
295
|
+
def debut_et_fin_trimestre?
|
296
|
+
from_date.day == 1 || return
|
297
|
+
:TRUE == @is_debut_et_fin_trimestre ||= begin
|
298
|
+
d = from_date.to_date >> 3
|
299
|
+
d = Date.new(d.year, d.month, 1).to_time
|
300
|
+
to_date.proche?(d) ? :TRUE : :FALSE
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
##
|
305
|
+
# @return TRUE is la période correspond à une année
|
306
|
+
#
|
307
|
+
def debut_et_fin_annee?
|
308
|
+
from_date.day == 1 || return
|
309
|
+
from_date.month == 1 || return
|
310
|
+
:TRUE == @is_debut_et_fin_annee ||= begin
|
311
|
+
dcomp = Time.new(from_date.year + 1, 1, 1)
|
312
|
+
to_date.proche?(dcomp) ? :TRUE : :FALSE
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Mois humain (en prenant le mois de from_date)
|
317
|
+
def human_month
|
318
|
+
@human_month ||= MOIS[from_date.month][:long]
|
319
|
+
end
|
320
|
+
# Année (en prenant l'année de from_date)
|
321
|
+
def human_year
|
322
|
+
@human_year ||= from_date.year
|
323
|
+
end
|
324
|
+
|
325
|
+
##
|
326
|
+
# Index du trimestre (de from_date)
|
327
|
+
def index_trimestre
|
328
|
+
@index_trimestre ||= (from_date.month.to_f / 3).ceil
|
329
|
+
end
|
330
|
+
|
331
|
+
def hindex_trimestre
|
332
|
+
@hindex_trimestre ||= begin
|
333
|
+
"#{index_trimestre}e#{index_trimestre > 1 ? '' : 'r'}"
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
# dernier jour du mois (en prenant le mois de to_date)
|
338
|
+
def last_day_of_the_month
|
339
|
+
@last_day_of_the_month ||= Date.civil(to_date.year, to_date.month, -1).day
|
340
|
+
end
|
341
|
+
def from_mois
|
342
|
+
@from_mois ||= from_date.month
|
343
|
+
end
|
344
|
+
def to_mois
|
345
|
+
@to_mois ||= to_mois
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
class Time
|
350
|
+
def proche?(t)
|
351
|
+
self.to_i.proche?(t.to_i)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
class Integer
|
355
|
+
# Retourne true si le nombre (de secondes correspondant à une
|
356
|
+
# durée) est ± une demi-heure
|
357
|
+
def proche?(laps)
|
358
|
+
self.between?(laps - 1800, laps + 1800)
|
359
|
+
end
|
360
|
+
def days
|
361
|
+
self * 24 * 3600
|
362
|
+
end
|
363
|
+
alias :day :days
|
364
|
+
end
|
365
|
+
|
366
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Class Clir::PrecedencedList
|
4
|
+
=end
|
5
|
+
module Clir
|
6
|
+
class PrecedencedList < Array
|
7
|
+
|
8
|
+
##
|
9
|
+
# Instantiation
|
10
|
+
#
|
11
|
+
# @param liste {Array} La liste des items
|
12
|
+
# @param list_name {String} Un nom unique pour cette liste de
|
13
|
+
# valeur. Si ce n'est pas un nom unique, des comportements
|
14
|
+
# inattendus se produiront.
|
15
|
+
#
|
16
|
+
def initialize(liste, list_name)
|
17
|
+
super(liste)
|
18
|
+
liste.first.is_a?(Hash) || raise("Pour pouvoir gérer les précédences d'une liste, il faut que les éléments soit des dictionnaires (Hash).")
|
19
|
+
liste.first.key?(:value) || raise("Pour pouvoir gérer les précédences d'une liste, il faut que les éléments définissent l'attribut unique :value.")
|
20
|
+
@list_name = list_name
|
21
|
+
end
|
22
|
+
|
23
|
+
# = main =
|
24
|
+
#
|
25
|
+
# Méthode principale qui retourne les items gérés au niveau de
|
26
|
+
# la précédence.
|
27
|
+
#
|
28
|
+
def to_prec
|
29
|
+
if precedence_exist?
|
30
|
+
@table = nil # Pour forcer
|
31
|
+
liste = precedences.map do |value|
|
32
|
+
table.delete(value)
|
33
|
+
end.compact # les items supprimés
|
34
|
+
#
|
35
|
+
# On ajoute les items restants
|
36
|
+
#
|
37
|
+
table.each do |value, ditem|
|
38
|
+
liste << ditem
|
39
|
+
end
|
40
|
+
return liste
|
41
|
+
else
|
42
|
+
return self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @public
|
47
|
+
# = main =
|
48
|
+
#
|
49
|
+
# Méthode principale pour enregistrer le dernier item choisi
|
50
|
+
#
|
51
|
+
def set_last(value)
|
52
|
+
precedences.delete(value)
|
53
|
+
@precedences.unshift(value)
|
54
|
+
save
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def table
|
59
|
+
@table ||= begin
|
60
|
+
tbl = {}
|
61
|
+
self.each { |ditem| tbl.merge!(ditem[:value] => ditem) }
|
62
|
+
tbl
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def save
|
67
|
+
File.write(path, precedences.join("\n"))
|
68
|
+
end
|
69
|
+
|
70
|
+
def precedences
|
71
|
+
@precedences ||= begin
|
72
|
+
if precedence_exist?
|
73
|
+
# puts "Je lis le fichier de précédence : #{path}".orange
|
74
|
+
# sleep 4
|
75
|
+
File.read(path).split("\n")
|
76
|
+
else [] end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def precedence_exist?
|
81
|
+
File.exist?(path)
|
82
|
+
end
|
83
|
+
|
84
|
+
def path
|
85
|
+
@path ||= File.join(self.class.folder, "#{@list_name}.precedences")
|
86
|
+
end
|
87
|
+
|
88
|
+
# @note
|
89
|
+
# En mode test, il faut le refaire chaque fois
|
90
|
+
def self.folder
|
91
|
+
if test?
|
92
|
+
mkdir(File.join(Dir.home, 'TESTS', '.precedences'))
|
93
|
+
else
|
94
|
+
@@folder ||= mkdir(File.join(Dir.home, '.precedences'))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end #/class PrecedencedList
|
98
|
+
end #/module Clir
|