pillboxr 0.0.3 → 0.6.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/Gemfile +10 -3
- data/Gemfile.lock +52 -0
- data/README.md +116 -0
- data/Rakefile +29 -6
- data/lib/pillboxr.rb +35 -532
- data/lib/pillboxr/attributes.rb +267 -0
- data/lib/pillboxr/constants.rb +61 -0
- data/lib/pillboxr/extensions.rb +11 -0
- data/lib/pillboxr/pages.rb +120 -0
- data/lib/pillboxr/params.rb +36 -0
- data/lib/pillboxr/pill.rb +74 -0
- data/lib/pillboxr/request.rb +59 -0
- data/lib/pillboxr/response.rb +8 -0
- data/lib/pillboxr/result.rb +79 -0
- data/lib/pillboxr/version.rb +4 -0
- data/pillboxr.gemspec +18 -7
- data/test/pillboxr/attributes_test.rb +15 -0
- data/test/pillboxr/params_test.rb +28 -0
- data/test/pillboxr/pill_test.rb +20 -0
- data/test/pillboxr/pillboxr_test.rb +167 -0
- data/test/pillboxr/test_helper.rb +10 -0
- metadata +116 -90
- data/.gitignore +0 -6
- data/README.rdoc +0 -46
- data/lib/activeresource_patch.rb +0 -27
- data/lib/compatibility.rb +0 -15
- data/lib/pillbox_resource_noko.rb +0 -40
- data/lib/version.rb +0 -1
- data/test/fixtures/all_meds.yml +0 -8445
- data/test/fixtures/image_meds.yml +0 -4223
- data/test/fixtures/meds.xml +0 -73
- data/test/fixtures/no_image_meds.yml +0 -4223
- data/test/pillboxr_test.rb +0 -106
- data/test/test_helper.rb +0 -16
@@ -0,0 +1,267 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require_relative 'constants'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Pillboxr
|
6
|
+
module Attributes
|
7
|
+
def attributes
|
8
|
+
{ :color => :splcolor,
|
9
|
+
:shape => :splshape,
|
10
|
+
:product_code => :product_code,
|
11
|
+
:schedule => :dea_schedule_code,
|
12
|
+
:ingredients => :ingredients,
|
13
|
+
:imprint => :splimprint,
|
14
|
+
:id => :spl_id,
|
15
|
+
:ndc9 => :ndc9,
|
16
|
+
:size => :splsize,
|
17
|
+
:score => :splscore,
|
18
|
+
:rxcui => :rxcui,
|
19
|
+
:rxtty => :rxtty,
|
20
|
+
:rxstring => :rxstring,
|
21
|
+
:image => :has_image,
|
22
|
+
:image_id => :image_id,
|
23
|
+
:setid => :setid,
|
24
|
+
:author => :author,
|
25
|
+
:inactive_ingredients => :spl_inactive_ing,
|
26
|
+
:lower_limit => :lower_limit }
|
27
|
+
end
|
28
|
+
|
29
|
+
def api_attributes
|
30
|
+
attributes.invert
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a series of class methods on Pillboxr::Attributes module
|
34
|
+
# that allow easy access to all the possible colors, shapes, dea codes, etc..
|
35
|
+
# Example:
|
36
|
+
#
|
37
|
+
# Pillboxr::Attributes.colors
|
38
|
+
# => [:bullet,:capsule,:clover,:diamond,:double_circle,:freeform,:gear,:heptagon,
|
39
|
+
# :hexagon,:octagon,:oval,:pentagon,:rectangle,:round,:semi_circle,:square,
|
40
|
+
# :tear,:trapezoid,:triangle]
|
41
|
+
#
|
42
|
+
# Available methods are colors, color_codes, shapes, shape_codes, dea_codes,
|
43
|
+
# and schedule_codes.
|
44
|
+
self.constants.each do |const|
|
45
|
+
self.module_eval("def self.#{const.downcase}; #{const}.keys; end")
|
46
|
+
end
|
47
|
+
|
48
|
+
class Color
|
49
|
+
attr_accessor :color
|
50
|
+
|
51
|
+
def initialize(color_arg)
|
52
|
+
# puts "argument to method = #{color_arg}"
|
53
|
+
@color = case color_arg
|
54
|
+
when NilClass; raise ColorError
|
55
|
+
when Array; color_arg.size > 1 ? Pill::Attributes::Colors.new(color_arg) : COLORS[color_arg[0]]
|
56
|
+
when Symbol; COLORS.fetch(color_arg, color_arg)
|
57
|
+
when String; COLORS.fetch(color_arg.to_sym, color_arg.to_sym)
|
58
|
+
else raise "invalid arguments."
|
59
|
+
end
|
60
|
+
return self
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_param
|
64
|
+
"&color=" + String(@color)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Shape
|
69
|
+
attr_accessor :shape
|
70
|
+
|
71
|
+
def initialize(shape_arg)
|
72
|
+
# puts "argument to method = #{shape_arg}"
|
73
|
+
@shape = case shape_arg
|
74
|
+
when NilClass; raise ShapeError
|
75
|
+
when Array; shape_arg.size > 1 ? Pill::Attributes::Shapes.new(color_arg) : SHAPES[shape_arg[0]]
|
76
|
+
when /^([Cc]{1}\d{5})+/; shape_arg # valid hex
|
77
|
+
when Symbol; SHAPES.fetch(shape_arg, shape_arg)
|
78
|
+
when String; SHAPES.fetch(shape_arg.to_sym, shape_arg.to_sym)
|
79
|
+
else raise "invalid arguments."
|
80
|
+
end
|
81
|
+
return self
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_param
|
85
|
+
"&shape=" + String(@shape)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Productcode
|
90
|
+
attr_accessor :product_code
|
91
|
+
|
92
|
+
def initialize(product_code_arg)
|
93
|
+
# puts "argument to method = #{product_code_arg}"
|
94
|
+
@code = case product_code_arg
|
95
|
+
when NilClass; raise ProductcodeError
|
96
|
+
when Array; raise "product_code must be unique."
|
97
|
+
when /^\d+-\d+/; product_code_arg # valid hex
|
98
|
+
when Symbol; raise "product_code cannot be a symbol."
|
99
|
+
else raise "invalid arguments."
|
100
|
+
end
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_param
|
105
|
+
"&prodcode=" + String(@code)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class Schedule
|
110
|
+
attr_accessor :schedule
|
111
|
+
|
112
|
+
def initialize(schedule_code)
|
113
|
+
# puts "argument to method = #{schedule_code}"
|
114
|
+
@schedule = case schedule_code
|
115
|
+
when NilClass; raise ScheduleError
|
116
|
+
when Array; schedule_code.size > 1 ? Pill::Attributes::Schedules.new(color_arg) : DEA_CODES.fetch(schedule_code[0])
|
117
|
+
when /^([Cc]{1}\d{5})+/; schedule_code # valid hex
|
118
|
+
when /\AI{1,3}\z|\AIV\z|\AV\z/; DEA_CODES.fetch(schedule_code, schedule_code)
|
119
|
+
else raise "invalid arguments."
|
120
|
+
end
|
121
|
+
return self
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_param
|
125
|
+
"&dea=" + String(@schedule)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class Ingredients
|
130
|
+
attr_accessor :ingredients
|
131
|
+
|
132
|
+
def initialize(ingredient_arg)
|
133
|
+
# puts "argument to method = #{ingredient_arg}"
|
134
|
+
@ingredients = case ingredient_arg
|
135
|
+
when NilClass; raise IngredientError
|
136
|
+
when Array; raise ArgumentError, "can only search for one active ingredient at this time."
|
137
|
+
when String; ingredient_arg
|
138
|
+
when Symbol; String(ingredient_arg)
|
139
|
+
else raise ArgumentError, "invalid arguments."
|
140
|
+
end
|
141
|
+
return self
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_param
|
145
|
+
"&ingredient=" + String(@ingredients)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# class Imprint # Broken currently
|
150
|
+
# attr_accessor :imprint
|
151
|
+
|
152
|
+
# def initialize(imprint_arg)
|
153
|
+
# # puts "argument to method = #{ingredient_arg}"
|
154
|
+
# @imprint = case imprint_arg
|
155
|
+
# when NilClass; raise ImprintError
|
156
|
+
# when Array; raise ArgumentError, "can only search for one imprint string at this time."
|
157
|
+
# when String; imprint_arg
|
158
|
+
# when Symbol; imprint_arg
|
159
|
+
# when Integer; imprint_arg
|
160
|
+
# else raise ArgumentError, "invalid arguments."
|
161
|
+
# end
|
162
|
+
# return self
|
163
|
+
# end
|
164
|
+
|
165
|
+
# def to_param
|
166
|
+
# "&imprint=" + String(@imprint)
|
167
|
+
# end
|
168
|
+
# end
|
169
|
+
|
170
|
+
class Size
|
171
|
+
attr_accessor :size
|
172
|
+
|
173
|
+
def initialize(size_arg)
|
174
|
+
@size = case size_arg
|
175
|
+
when NilClass; raise SizeError
|
176
|
+
when Integer; size_arg
|
177
|
+
when Float; Integer(size_arg)
|
178
|
+
when String; Integer(size_arg)
|
179
|
+
when Symbol; Integer(size_arg)
|
180
|
+
else raise ArgumentError, "invalid arguments."
|
181
|
+
end
|
182
|
+
return self
|
183
|
+
end
|
184
|
+
|
185
|
+
def to_param
|
186
|
+
"&size=" + String(@size)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
class Score
|
193
|
+
attr_accessor :score
|
194
|
+
|
195
|
+
def initialize(score_arg)
|
196
|
+
# puts "argument to method = #{score_arg}"
|
197
|
+
@score = case score_arg
|
198
|
+
when NilClass; raise ScoreError
|
199
|
+
when TrueClass; 2
|
200
|
+
when FalseClass; 1
|
201
|
+
when Integer; score_arg
|
202
|
+
when Array; raise ArgumentError, "Must be true or false."
|
203
|
+
else raise ArgumentError, "invalid arguments."
|
204
|
+
end
|
205
|
+
return self
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_param
|
209
|
+
"&score=" + String(@score)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class Image
|
214
|
+
attr_accessor :image
|
215
|
+
|
216
|
+
def initialize(image_arg)
|
217
|
+
# puts "argument to method = #{image_arg}"
|
218
|
+
@image = case image_arg
|
219
|
+
when NilClass; raise ImageError
|
220
|
+
when TrueClass; 1
|
221
|
+
when FalseClass; 0
|
222
|
+
when Array; raise ArgumentError, "Must be true or false."
|
223
|
+
else raise ArgumentError, "Invalid argument. Must be true or false."
|
224
|
+
end
|
225
|
+
return self
|
226
|
+
end
|
227
|
+
|
228
|
+
def to_param
|
229
|
+
"&has_image=" + String(@image)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class Author
|
234
|
+
attr_accessor :author
|
235
|
+
|
236
|
+
def initialize(author_arg)
|
237
|
+
@author = case author_arg
|
238
|
+
when String; author_arg
|
239
|
+
when Array; raise ArgumentError, "Author can only take a single string."
|
240
|
+
else raise ArgumentError, "invalid arguments."
|
241
|
+
end
|
242
|
+
return self
|
243
|
+
end
|
244
|
+
|
245
|
+
def to_param
|
246
|
+
"&author=" + ERB::Util.url_encode(@author)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
class Lowerlimit
|
251
|
+
attr_accessor :lower_limit
|
252
|
+
|
253
|
+
def initialize(limit = DEFAULT_LOWER_LIMIT)
|
254
|
+
@lower_limit = case limit
|
255
|
+
when NilClass; raise LowerLimitError
|
256
|
+
when Integer; limit
|
257
|
+
else raise ArgumentError, "Lower limit must be an integer."
|
258
|
+
end
|
259
|
+
return self
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_param
|
263
|
+
"&lower_limit=" + String(@lower_limit)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Pillboxr
|
3
|
+
RECORDS_PER_PAGE = 201
|
4
|
+
DEFAULT_LOWER_LIMIT = 1
|
5
|
+
BASE_URI = 'pillbox.nlm.nih.gov'
|
6
|
+
NO_RECORDS_ERROR_MESSAGE = "The document \"No records found\" does not have a valid root"
|
7
|
+
API_KEY_ERROR_MESSAGE = "The document \"Key does not match, you may not access this service\" does not have a valid root"
|
8
|
+
|
9
|
+
module Attributes
|
10
|
+
SHAPES = {
|
11
|
+
:bullet=> :C48335,
|
12
|
+
:capsule=> :C48336,
|
13
|
+
:clover=> :C48337,
|
14
|
+
:diamond=> :C48338,
|
15
|
+
:double_circle=> :C48339,
|
16
|
+
:freeform=> :C48340,
|
17
|
+
:gear=> :C48341,
|
18
|
+
:heptagon=> :C48342,
|
19
|
+
:hexagon=> :C48343,
|
20
|
+
:octagon=> :C48344,
|
21
|
+
:oval=> :C48345,
|
22
|
+
:pentagon=> :C48346,
|
23
|
+
:rectangle=> :C48347,
|
24
|
+
:round=> :C48348,
|
25
|
+
:semi_circle=> :C48349,
|
26
|
+
:square=> :C48350,
|
27
|
+
:tear=> :C48351,
|
28
|
+
:trapezoid=> :C48352,
|
29
|
+
:triangle=> :C48353
|
30
|
+
}
|
31
|
+
|
32
|
+
SHAPE_CODES = SHAPES.invert
|
33
|
+
|
34
|
+
COLORS = {
|
35
|
+
:black => :C48323,
|
36
|
+
:blue => :C48333,
|
37
|
+
:brown => :C48332,
|
38
|
+
:gray => :C48324,
|
39
|
+
:green => :C48329,
|
40
|
+
:orange => :C48331,
|
41
|
+
:pink => :C48328,
|
42
|
+
:purple => :C48327,
|
43
|
+
:red => :C48326,
|
44
|
+
:turquoise => :C48334,
|
45
|
+
:white => :C48325,
|
46
|
+
:yellow => :C48330
|
47
|
+
}
|
48
|
+
|
49
|
+
COLOR_CODES = COLORS.invert
|
50
|
+
|
51
|
+
DEA_CODES = {
|
52
|
+
:I => :C48672,
|
53
|
+
:II => :C48675,
|
54
|
+
:III => :C48676,
|
55
|
+
:IV => :C48677,
|
56
|
+
:V => :C48679
|
57
|
+
}
|
58
|
+
|
59
|
+
SCHEDULE_CODES = DEA_CODES.invert
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Pillboxr
|
5
|
+
class Pages
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@data, :<<, :size, :each, :include?, :empty?, :count,
|
8
|
+
:join, :first, :last, :[], :[]=, :all?, :any?, :inject
|
9
|
+
|
10
|
+
def initialize(size = 0, obj = nil, &block)
|
11
|
+
@data = Array.new(size, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
string = "#<Pillboxr::Result::Pages:#{object_id} ["
|
16
|
+
@data.each do |page|
|
17
|
+
string << String(page)
|
18
|
+
string << ", "
|
19
|
+
end
|
20
|
+
string << "], size = #{self.size}>"
|
21
|
+
return string
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :to_s, :inspect
|
25
|
+
|
26
|
+
def start
|
27
|
+
self.current = @data[0]
|
28
|
+
end
|
29
|
+
|
30
|
+
def start?
|
31
|
+
self.current == @data[0]
|
32
|
+
end
|
33
|
+
|
34
|
+
def end
|
35
|
+
self.current = @data[-1]
|
36
|
+
end
|
37
|
+
|
38
|
+
def end?
|
39
|
+
self.current == @data[-1]
|
40
|
+
end
|
41
|
+
|
42
|
+
def advance(slots = 1)
|
43
|
+
slots.times { self.current = self.next }
|
44
|
+
return self.current
|
45
|
+
end
|
46
|
+
|
47
|
+
def retreat(slots = 1)
|
48
|
+
slots.times { self.current = self.previous }
|
49
|
+
return self.current
|
50
|
+
end
|
51
|
+
|
52
|
+
def next(slots = 1)
|
53
|
+
if slots == 1
|
54
|
+
self.end? ? @data[0] : @data[current_index + 1]
|
55
|
+
else
|
56
|
+
if slots <= (@data.size - (current_index + 1))
|
57
|
+
@data[(current_index + 1), slots]
|
58
|
+
else
|
59
|
+
temporary_array = @data[(current_index + 1)..-1].push(@data[0..(current_index - 1)]).flatten
|
60
|
+
return temporary_array[0..(slots - 1)]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def previous(slots = 1)
|
66
|
+
if slots == 1
|
67
|
+
self.start? ? @data[-1] : @data[current_index - 1]
|
68
|
+
else
|
69
|
+
if slots <= current_index
|
70
|
+
@data[(current_index - slots)..(current_index - 1)]
|
71
|
+
else
|
72
|
+
(@data[0..(current_index - 1)].reverse.push(@data[(current_index - slots)..-1].reverse)).flatten
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def current
|
78
|
+
@data[current_index]
|
79
|
+
end
|
80
|
+
|
81
|
+
def current=(page)
|
82
|
+
unless page.current?
|
83
|
+
self.current.send(:current=, false)
|
84
|
+
page.send(:current=, true)
|
85
|
+
end
|
86
|
+
return page
|
87
|
+
end
|
88
|
+
|
89
|
+
def current_index
|
90
|
+
@data.index { |page| page.current }
|
91
|
+
end
|
92
|
+
|
93
|
+
private :current_index
|
94
|
+
end
|
95
|
+
|
96
|
+
Page = Struct.new(:current, :retrieved, :number, :pills, :params) do
|
97
|
+
def inspect
|
98
|
+
"<Page: current: #{current}, retrieved: #{retrieved}, number: #{number}, params: #{params}, #{pills.size} pills>"
|
99
|
+
end
|
100
|
+
|
101
|
+
def current?
|
102
|
+
self.current == true
|
103
|
+
end
|
104
|
+
|
105
|
+
def retrieved?
|
106
|
+
self.retrieved == true
|
107
|
+
end
|
108
|
+
|
109
|
+
def get
|
110
|
+
unless self.retrieved
|
111
|
+
self.pills = Result.subsequent(Request.new(self.params).perform)
|
112
|
+
puts "#{self.pills.size} records retrieved."
|
113
|
+
self.retrieved = true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
alias_method :to_s, :inspect
|
118
|
+
private :current=, :retrieved=, :number=, :pills=, :params=
|
119
|
+
end
|
120
|
+
end
|