checkout formats may be undefined, avoid error in that scenario
[sitka/overdrive-evergreen-opac.git] / src / od_data.coffee
CommitLineData
9669700c
SC
1define [
2 'lodash'
3 'moment'
4], (
5 _
6 M
7) ->
8
9 # A base class defining utilitarian methods
10 class U
11 constructor: (x) ->
12 return unless x
13 t = @
14 t extends x
15 return
16
17 # Mutate an ISO 8601 date string into a Moment object. If the argument is
18 # just a date value, then it specifies an absolute date in ISO 8601 format.
19 # If the argument is a pair, then it specifies a date relative to now. For
20 # an ISO 8601 date, we correct for what seems to be an error in time zone,
21 # Zulu time is really East Coast time.
22 momentize: (date, unit) ->
23 switch arguments.length
24 when 1
25 if date then M(date.replace /Z$/, '-0400') else M()
26 when 2
27 if date then M().add date, unit else M()
28 else M()
29
508513a1
SC
30 # The URL endpoint is converted to its reverse proxy version,
31 # because we are using the Evergreen server as a reverse proxy to
32 # the Overdrive server.
33 proxy: (x) ->
34 return unless x
35 y = x
36 y = y.replace 'https://', '//'
37 y = y.replace 'http://' , '//'
38 y = y.replace '//oauth-patron.overdrive.com', '/od/oauth-patron'
39 y = y.replace '//oauth.overdrive.com', '/od/oauth'
40 y = y.replace '//patron.api.overdrive.com', '/od/api-patron'
41 y = y.replace '//api.overdrive.com', '/od/api'
42 y = y.replace '//images.contentreserve.com', '/od/images'
43 y = y.replace '//fulfill.contentreserve.com', '/od/fulfill'
44 #log "proxy #{x} -> #{y}"
45 y
46 proxies: (x) ->
47 (v.href = @proxy l) for n, v of x when l = v.href
48 return x
49
9669700c
SC
50
51 class Metadata extends U
52 constructor: (x) ->
53 super x
54
55 # Convert ID to upper case to match same case found in EG catalogue
56 @id = @id.toUpperCase()
57 # Provide a simplified notion of author: first name in creators
58 # list having a role of author
59 @author = (v.name for v in @creators when v.role is 'Author')[0] or ''
508513a1
SC
60 # Convert image links to use reverse proxy
61 @proxies @images
9669700c
SC
62
63 return
64
65
66 class Availability extends U
67 constructor: (x, email_address) ->
68 super x
69
70 @zero()
71 @hold email_address if @actions?.hold
fe5e191d 72 @proxies @actions if @actions?
e07737bf 73 @action_formats()
9669700c
SC
74
75 return @
76
77 # Add zero values
78 zero: ->
79 @copiesOwned = 0 unless @copiesOwned
80 @copiesAvailable = 0 unless @copiesAvailable
81 @numberOfHolds = 0 unless @numberOfHolds
82 return @
83
84 hold: (email_address) ->
85 # The reserve ID is empty in the actions.hold.fields; we have to fill it ourselves.
86 _.where(@actions.hold.fields, name: 'reserveId')[0].value = @id
87 # We jam the email address from the prefs page into the fields object from the server
88 # so that the new form will display it.
89 if email_address
90 _.where(@actions.hold.fields, name: 'emailAddress')[0].value = email_address
91 return @
92
e07737bf
SC
93 # Surface the format options list that might be buried in an actions object
94 action_formats: ->
95 xs = @actions?.checkout?.fields
96 return @ unless xs?.length > 0
97 break for x in xs when x.name is 'formatType'
98 return @ unless x.options.length > 0
99 @formats = ( { id: v, name: '' } for v in x.options )
100 return @
101
9669700c
SC
102
103 class Holds extends U
104 constructor: (x) ->
105 super x
106
107 @add()
108 .remove()
508513a1 109 .proxy_urls()
9669700c
SC
110 .moments()
111 .count()
112 .sort()
113
114 return
115
116 # Ensure there is always a holds list, even if it's empty
117 add: ->
118 @holds = [] if @holds is undefined
119 return @
120
121 # Delete action to release a suspension if a hold is not
122 # suspended, because such actions are redundant
123 remove: ->
124 delete x.actions.releaseSuspension for x in @holds when not x.holdSuspension
125 return @
126
508513a1
SC
127 proxy_urls: ->
128 (@proxies v.actions) for v, n in @holds
129 return @
130
9669700c
SC
131 # For each hold, convert any ISO 8601 date strings into a
132 # Moment object (at local time zone)
133 moments: ->
134 for x in @holds
135 x.holdPlacedDate = @momentize x.holdPlacedDate
136 x.holdExpires = @momentize x.holdExpires
137 if x.holdSuspension
138 x.holdSuspension.numberOfDays = @momentize x.holdSuspension.numberOfDays, 'days'
139 return @
140
141 # Count the number of holds that can be checked out now
142 count: ->
143 @ready = _.countBy @holds, (x) -> if x.actions.checkout then 'forCheckout' else 'other'
144 @ready.forCheckout = 0 unless @ready.forCheckout
145 return @
146
147 # Sort the holds list by position and placed date
148 # and sort ready holds first
149 sort: ->
150 @holds = _(@holds)
151 .sortBy ['holdListPosition', 'holdPlacedDate']
152 .sortBy (x) -> x.actions.checkout
153 .value()
154 return @
155
156
157 class Checkouts extends U
158 constructor: (x) ->
159 super x
160
161 @add()
508513a1 162 .proxy_urls()
9669700c
SC
163 .moments()
164 .sort()
165
166 return
167
168 # Ensure there is always a checkouts list, even if it's empty
169 add: ->
170 @checkouts = [] if @checkouts is undefined
171 return @
172
47c4b112
SC
173 proxy_urls: ->
174 for x in @checkouts
175 @proxies x.actions
6bca3365 176 x.formats = [] if x.formats is undefined
47c4b112 177 @proxies y.linkTemplates for y in x.formats
508513a1
SC
178 return @
179
9669700c
SC
180 # For each checkout, convert any ISO 8601 date strings into a
181 # Moment object (at local time zone)
182 moments: ->
183 for x in @checkouts
184 x.expires = @momentize x.expires
185 return @
186
187 # Sort the checkout list by expiration date
188 sort: ->
189 @checkouts = _.sortBy @checkouts, 'expires'
190 return @
191
192
193 class Interests
194 constructor: (h, c) ->
195 return {
196 nHolds: h.totalItems
197 nHoldsReady: h.ready.forCheckout
198 nCheckouts: c.totalItems
199 nCheckoutsReady: c.totalCheckouts
200 ofHolds: h.holds
201 ofCheckouts: c.checkouts
202 # The following property is a map from product ID to a hold or
203 # a checkout object, eg, interests.byID(124)
204 byID: do (hs = h.holds, cs = c.checkouts) ->
205 byID = {}
206 for v, n in hs
207 v.type = 'hold'
208 byID[v.reserveId] = v
209 for v, n in cs
210 v.type = 'checkout'
211 byID[v.reserveId] = v
212 return byID
213 }
214
215 return {
216 Metadata: Metadata
217 Availability: Availability
218 Holds: Holds
219 Checkouts: Checkouts
220 Interests: Interests
221 }