pueri 0.7.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.
data/lib/pueri.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pueri/age'
4
+ require 'pueri/neuro'
5
+ require 'pueri/vax'
6
+ require 'pueri/version'
7
+
8
+ require 'pastel'
9
+ require 'tty-table'
10
+
11
+ # This module is a support for physicians and nurses who work on Primary Care
12
+ # with children age 12 and younger. It calculates the child's age and outputs
13
+ # an optimal vaccination scheme for such age, based on Brazil's National Program
14
+ # of Immunization (PNI), using the 2019 uptade.
15
+ module Pueri; end
data/lib/pueri/age.rb ADDED
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pueri
4
+ # Parses a given date string into and Array of age. The array contains the
5
+ # years, months and days of life of the one who was/would be born on the given
6
+ # day.
7
+ class Age
8
+ attr_reader :days_month, :age
9
+
10
+ # Creates an Age instance.
11
+ #
12
+ # @param date [String] A date current or on the past - NOT on the future.
13
+ def initialize(date)
14
+ @days_month = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
15
+ @age = parse_age(date)
16
+ end
17
+
18
+ # Retrieves the age on a readable format.
19
+ #
20
+ # @param short [Booleann] Whether to output a short-format age or not.
21
+ # Defaults to long format.
22
+ # @return [String] The age in a readable format, either short or long.
23
+ def to_s(short = false)
24
+ if short
25
+ short_string
26
+ else
27
+ long_string
28
+ end
29
+ end
30
+
31
+ # Retrieves the age in days.
32
+ #
33
+ # @return [Float] The age in days, rounded to the second decimal place.
34
+ def to_f
35
+ a = age[0].to_f * 365.25
36
+ a += age[1].to_f * 30.0
37
+ a += age[2].to_f
38
+ a.round(2)
39
+ end
40
+
41
+ private
42
+
43
+ def short_string
44
+ p = Pastel.new
45
+ "#{p.bold(age[0])}a#{p.bold(age[1])}m#{p.bold(age[2])}d"
46
+ end
47
+
48
+ def long_string
49
+ p = Pastel.new
50
+ "Idade: #{p.cyan(age[0])} anos, #{p.cyan(age[1])} meses, "\
51
+ "#{p.cyan(age[2])} dias."
52
+ end
53
+
54
+ # This method takes a date in the string format "DD/MM/YYYY" and returns
55
+ # a Time object.
56
+ def birthday_timestamp_time(date_read)
57
+ date = date_read.chomp
58
+ if date =~ %r{/}
59
+ birthday = date.split('/')
60
+ birthday[2] = year(birthday[2])
61
+ else
62
+ birthday = parse_numonly_date(date)
63
+ end
64
+ Time.new(birthday[2], birthday[1], birthday[0])
65
+ end
66
+
67
+ def parse_numonly_date(date)
68
+ if date.size == 6
69
+ [date[0..1], date[2..3], year(date[4..5])]
70
+ else
71
+ [date[0..1], date[2..3], date[4..7]]
72
+ end
73
+ end
74
+
75
+ def year(year)
76
+ if year.chomp.size == 2
77
+ if year.to_i < 30
78
+ year.prepend('20')
79
+ else
80
+ year.prepend('19')
81
+ end
82
+ end
83
+
84
+ year
85
+ end
86
+
87
+ def future_day?(day, month, birth_date, current_date, borrowed_month)
88
+ if day.negative?
89
+ # subtract month, get positive # for day
90
+ day = @days_month[birth_date.month] - birth_date.day + current_date.day
91
+ month -= 1
92
+ borrowed_month = true
93
+ end
94
+
95
+ [day, month, borrowed_month]
96
+ end
97
+
98
+ def future_month?(month, year, birth_date, current_date, borrowed_month)
99
+ if month.negative?
100
+ # subtract year, get positive # for month
101
+ month = 12 - birth_date.month + current_date.month
102
+ month -= 1 if borrowed_month == true
103
+ year -= 1
104
+ end
105
+
106
+ [month, year]
107
+ end
108
+
109
+ def future_year?(year, month, day)
110
+ # Error-handling for future date
111
+ if year.negative?
112
+ year = 0
113
+ month = 0
114
+ day = 0
115
+ end
116
+
117
+ [year, month, day]
118
+ end
119
+
120
+ def check_future(day, month, year, dates, borrowed_month)
121
+ birth_date, current_date = dates
122
+ day, month, borrowed_month = future_day?(
123
+ day, month, birth_date, current_date, borrowed_month
124
+ )
125
+ month, year = future_month?(
126
+ month, year, birth_date, current_date, borrowed_month
127
+ )
128
+ year, month, day = future_year?(year, month, day)
129
+
130
+ [day, month, year]
131
+ end
132
+
133
+ def check_present(current_date, birth_date)
134
+ day = current_date.day - birth_date.day
135
+ month = current_date.month - birth_date.month
136
+ year = current_date.year - birth_date.year
137
+
138
+ [day, month, year]
139
+ end
140
+
141
+ # This method takes a birthday in the string format "DD/MM/YYYY" and returns
142
+ # the person's age.
143
+ def parse_age(date)
144
+ borrowed_month = false
145
+
146
+ current_date = Time.new
147
+ birth_date = birthday_timestamp_time(date)
148
+
149
+ # Get days for this year
150
+ @days_month[2] = 29 if current_date.to_date.leap?
151
+
152
+ day, month, year = check_present(current_date, birth_date)
153
+
154
+ day, month, year = check_future(
155
+ day, month, year, [birth_date, current_date], borrowed_month
156
+ )
157
+
158
+ [year, month, day]
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pueri
4
+ # Class description here
5
+ class Neuro
6
+ attr_reader :range, :header, :social, :motor, :lang, :table
7
+
8
+ def initialize(norm_age)
9
+ @range = calc_range(norm_age)
10
+ init_tables
11
+ mount_table
12
+ end
13
+
14
+ private
15
+
16
+ def mount_table
17
+ adjust_length
18
+ table_obj = TTY::Table.new @header, make_lines
19
+ @table = table_obj.render :unicode
20
+ end
21
+
22
+ def adjust_length
23
+ max_length = num_lines
24
+ adjust_length_social(max_length)
25
+ adjust_length_motor(max_length)
26
+ adjust_length_lang(max_length)
27
+ end
28
+
29
+ def adjust_length_social(max_length)
30
+ return unless @social.size < max_length
31
+
32
+ (max_length - @social.size).times do
33
+ @social.append ''
34
+ end
35
+ end
36
+
37
+ def adjust_length_motor(max_length)
38
+ return unless @motor.size < max_length
39
+
40
+ (max_length - @motor.size).times do
41
+ @motor.append ''
42
+ end
43
+ end
44
+
45
+ def adjust_length_lang(max_length)
46
+ return unless @lang.size < max_length
47
+
48
+ (max_length - @lang.size).times do
49
+ @lang.append ''
50
+ end
51
+ end
52
+
53
+ def make_lines
54
+ [@social, @motor, @lang].transpose
55
+ end
56
+
57
+ def num_lines
58
+ n = @social.size
59
+ n = @motor.size if @motor.size > n
60
+ n = @lang.size if @lang.size > n
61
+ n
62
+ end
63
+
64
+ def init_tables
65
+ init_header
66
+ init_social
67
+ init_motor
68
+ init_lang
69
+ end
70
+
71
+ def init_header
72
+ @header = %w[
73
+ Social
74
+ Motor
75
+ Linguagem
76
+ ]
77
+ end
78
+
79
+ def init_social
80
+ social = [
81
+ 'Mamar, evacuar e dormir.',
82
+ 'Fixa o olhar com o examinador',
83
+ 'Sorriso social',
84
+ 'Leva a mão a objetos', '', '',
85
+ 'Estranhamento social', '',
86
+ 'Dá tchau e bate palma',
87
+ 'Imita atividades da casa', '', ''
88
+ ]
89
+ @social = social[0..@range].reject(&:empty?)
90
+ end
91
+
92
+ def init_motor
93
+ motor = [
94
+ '', '', '',
95
+ 'Fixa polo cefálico',
96
+ 'Senta com apoio',
97
+ 'Senta sem apoio, pinça superior',
98
+ 'Fica em pé com ajuda', '', '',
99
+ 'Primeiros passos', '', ''
100
+ ]
101
+ @motor = motor[0..@range].reject(&:empty?)
102
+ end
103
+
104
+ def init_lang
105
+ lang = [
106
+ '', '', '', '',
107
+ 'Lalação', '', '',
108
+ 'Primeiras palavras', '',
109
+ 'Palavra frase',
110
+ 'Junta duas palavras',
111
+ 'Fases gramaticais'
112
+ ]
113
+ @lang = lang[0..@range].reject(&:empty?)
114
+ end
115
+
116
+ def out_of_range
117
+ m = 'The given age is out of reality bounds'
118
+ raise ArgumentError, m
119
+ end
120
+
121
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
122
+ # Disabled rubocop for this method for breaking this switch apart will make
123
+ # it less readable than keeping it as a whole logic.
124
+ def calc_range(age)
125
+ r = case age
126
+ when 0.01..59.99 then 0
127
+ when 60.0..89.99 then 1
128
+ when 90.0..119.99 then 2
129
+ when 120.0..179.99 then 3
130
+ when 180.0..269.99 then 4
131
+ when 270.0..299.99 then 5
132
+ when 300.0..365.24 then 6
133
+ when 365.25..455.24 then 7
134
+ when 455.25..545.24 then 8
135
+ when 545.25..730.49 then 9
136
+ when 730.5..1095.74 then 10
137
+ when 1095.75..50_000.0 then 11
138
+ else out_of_range
139
+ end
140
+ r
141
+ end
142
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
143
+ end
144
+ end
data/lib/pueri/vax.rb ADDED
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pueri
4
+ # Gets input from child vaccination calendar for children (PNI) and an age in
5
+ # days. With this, process which vaccines are due by that age.
6
+ class Vax
7
+ attr_reader :header, :calendar, :notes
8
+ attr_reader :range, :table
9
+
10
+ def initialize(norm_age)
11
+ init_base
12
+ @range = calc_range(norm_age)
13
+ parse_calendar
14
+ end
15
+
16
+ # Retrieves the notes for the vaccination calendar, prettified.
17
+ #
18
+ # @return [String] The notes for the vaccines' calendar.
19
+ def parse_notes
20
+ r = ''
21
+ max = @notes.size
22
+ p = Pastel.new
23
+
24
+ @notes.each_with_index do |line, k|
25
+ pre = ' ' * (max - (k + 1))
26
+ pre += '*' * (k.to_i + 1)
27
+ r += "\n#{p.cyan(pre)} #{line}"
28
+ end
29
+
30
+ r
31
+ end
32
+
33
+ private
34
+
35
+ def init_base
36
+ init_header
37
+ init_calendar
38
+ init_notes
39
+ end
40
+
41
+ def init_header
42
+ @header = ['BCB', 'HepB', 'Penta', 'Polio', 'Pneumo 10V', 'Rota',
43
+ 'MeningoC', 'FA', 'HepA', 'SCR', 'Tetra*', 'Varicela',
44
+ 'HPV', 'Influenza']
45
+ end
46
+
47
+ def init_calendar
48
+ part1 = init_calendar1
49
+ part2 = init_calendar2
50
+ @calendar = part1.concat part2
51
+ end
52
+
53
+ def init_calendar1
54
+ [
55
+ ['DU', '1ª', '', '', '', '', '', '', '', '', '', '', '', ''],
56
+ ['', '', '1ª', '1ª VIP', '1ª', '1ª', '', '', '', '', '', '', '', ''],
57
+ ['', '', '', '', '', '', '1ª', '', '', '', '', '', '', ''],
58
+ ['', '', '2ª', '2ª VIP', '2ª', '2ª', '', '', '', '', '', '', '', ''],
59
+ ['', '', '', '', '', '', '2ª', '', '', '', '', '', '', ''],
60
+ ['', '', '3ª', '3ª VIP', '', '', '', '', '', '', '', '', '', '**']
61
+ ]
62
+ end
63
+
64
+ def init_calendar2
65
+ [
66
+ ['', '', '', '', '', '', '', 'DU', '', '', '', '', '', '**'],
67
+ ['', '', '', '', 'Ref', '', 'Ref', '', '', '1ª', '', '', '', '**'],
68
+ ['', '', 'DTP', '1ª VOP', '', '', '', '', 'DU', '', 'DU', '', '', '**'],
69
+ ['', '', 'DTP', '2ª VOP', '', '', '', '', '', '', '', 'DU', '', '**'],
70
+ ['', '', '', '', '', '', '', '', '', '', '', '', '***', '']
71
+ ]
72
+ end
73
+
74
+ def init_notes
75
+ @notes = [
76
+ 'A vacina tetra viral corresponde à segunda dose de SCR e a uma dose '\
77
+ 'de Varicela.',
78
+ 'A partir dos 6 meses aos 6 anos incompletos, uma dose por ano, exceto'\
79
+ ' no primeiro ano de vida, quase se oferta duas doses.',
80
+ 'Meninas de 9 a 14 anos. Meninos de 11 a 14 anos. Duas doses com '\
81
+ 'intervalo de 6 meses.'
82
+ ]
83
+ end
84
+
85
+ def parse_calendar
86
+ table_obj = TTY::Table.new(header: @header) do |table_line|
87
+ @calendar[0..@range].each do |line|
88
+ table_line << line
89
+ end
90
+ end
91
+ @table = table_obj.render(:unicode, alignment: :center)
92
+ end
93
+
94
+ def out_of_range
95
+ m = 'The given age is out of reality bounds'
96
+ raise ArgumentError, m
97
+ end
98
+
99
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
100
+ # Disabled rubocop for this method for breaking this switch apart will make
101
+ # it less readable than keeping it as a whole logic.
102
+ def calc_range(norm_age)
103
+ r = case norm_age
104
+ when 0.01..59.99 then 0
105
+ when 60.0..89.99 then 1
106
+ when 90.0..119.99 then 2
107
+ when 120.0..149.99 then 3
108
+ when 150.0..179.99 then 4
109
+ when 180.0..269.99 then 5
110
+ when 270.0..365.24 then 6
111
+ when 365.25..455.24 then 7
112
+ when 455.25..1460.99 then 8
113
+ when 1461.0..3287.24 then 9
114
+ when 3287.25..50_000.0 then 10
115
+ else out_of_range
116
+ end
117
+ r
118
+ end
119
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
120
+ end
121
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module is a support for physicians and nurses who work on Primary Care
4
+ # with children age 12 and younger. It calculates the child's age and outputs
5
+ # an optimal vaccination scheme for such age, based on Brazil's National Program
6
+ # of Immunization (PNI), using the 2019 uptade.
7
+ module Pueri
8
+ # Sets the current version of the gem
9
+ @version = '0.7.0'
10
+
11
+ # Returns the Gem's current version.
12
+ #
13
+ # @return [String] The Gem's current version.
14
+ def self.version
15
+ @version
16
+ end
17
+ end