1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2015 Alexandre Terrasa <alexandre@moz-code.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # JSON handling for `refund`.
24 redef class RefundProcessor
26 redef fun process
(input_file
, output_file
) do
27 self.output_file
= output_file
28 var json
= load_input
(input_file
)
29 var sheet
= new ReclamationSheet.from_json
(self, json
)
30 var res
= process_refunds
(sheet
)
31 write_output
(res
.to_pretty_json
, output_file
)
34 # Computes allowed refunds for a given `ReclamationSheet`.
35 fun process_refunds
(sheet
: ReclamationSheet): JsonObject do
37 var stats
= load_stats
38 stats
.inc
("total_treatments")
41 var json
= new JsonObject
42 json
["dossier"] = sheet
.file
.to_s
43 json
["mois"] = sheet
.month
.to_s
44 var arr
= new JsonArray
45 var sum
= 0.0.to_dollar
46 for recl
in sheet
.recls
do
47 var refund
= process_refund
(sheet
, recl
)
48 var obj
= new JsonObject
49 obj
["soin"] = recl
.care_id
50 obj
["date"] = recl
.date
.to_s
51 obj
["montant"] = refund
.to_s
54 # update stats for care
55 stats
.inc
("total_{recl.care_id}")
58 json
["remboursements"] = arr
59 json
["total"] = sum
.to_s
63 # Loads the input string and returns its content as a JsonObject.
65 # Dies if the file cannot be read or does not contain a valid JSONObject.
66 fun load_input
(file
: String): JsonObject do
67 if not file
.file_exists
then
68 die
("File `{file}` not found.")
71 var ptr
= new FileReader.open
(file
)
72 var json
= ptr
.read_all
.parse_json
73 if json
isa JsonParseError then
74 die
("Wrong input file ({json.message})")
76 else if json
== null then
77 die
("Unable to parse input file as json (got null)")
79 else if not json
isa JsonObject then
80 die
("Wrong input type (expected JsonObject got {json.class_name})")
87 # Writes `str` in path specified by `file`.
89 # Used to produce output and stats.
90 fun write_output
(str
: String, file
: String) do
91 var ofs
= new FileWriter.open
(file
)
99 # Does `json` contains `key`? Dies otherwise.
100 private fun check_key
(json
: JsonObject, key
: String) do
101 if json
.has_key
(key
) then return
102 die
("Malformed input (missing key {key})")
105 # Does `str` match the regex `re`.
106 private fun check_format
(str
, re
: String): Bool do
107 return str
.has
(re
.to_re
)
110 redef fun die
(msg
) do
112 var obj
= new JsonObject
114 write_output
(obj
.to_pretty_json
, output_file
)
116 var stats
= load_stats
117 stats
.inc
("total_reject")
123 redef fun show_stats
do print load_stats
.to_json_object
.to_pretty_json
125 redef fun load_stats
do
126 # If no stats found, return a new object
127 if not stats_file
.file_exists
then return new RefundStats
128 # Try to read from file
129 var ifs
= new FileReader.open
(stats_file
)
130 var content
= ifs
.read_all
.parse_json
132 # If file is corrupted, return a new object
133 if not content
isa JsonObject then return new RefundStats
134 # Return file contained stats
135 return new RefundStats.from_json
(content
)
138 redef fun save_stats
(stats
) do
139 write_output
(stats
.to_json_object
.to_pretty_json
, stats_file
)
143 redef class RefundStats
145 # Inits `self` from the content of a JsonObject
146 init from_json
(json
: JsonObject) do
147 for k
, v
in json
do self[k
] = v
.as(Int)
150 # Outputs `self` as a JSON string.
151 fun to_json_object
: JsonObject do
152 var obj
= new JsonObject
153 for k
, v
in self do obj
[k
] = v
158 redef class ReclamationSheet
160 # Inits `self` from the content of a `JsonObject`.
161 init from_json
(proc
: RefundProcessor, json
: JsonObject) do
162 file
= new ReclFile.from_json
(proc
, json
)
163 month
= new ReclMonth.from_json
(proc
, json
)
164 recls
= parse_recls
(proc
, json
)
168 # Parses and checks the given `json` then returns an array of `Reclamation` instances.
169 private fun parse_recls
(proc
: RefundProcessor, json
: JsonObject): Array[Reclamation] do
170 proc
.check_key
(json
, "reclamations")
171 var res
= new Array[Reclamation]
172 var recls
= json
["reclamations"]
173 if recls
== null then
174 proc
.die
("Wrong type for `number` (expected JsonArray got null)")
176 else if not recls
isa JsonArray then
177 proc
.die
("Wrong type for `number` (expected JsonArray got {recls.class_name})")
183 proc
.die
("Wrong type for `reclamations#{i}` (expected JsonObject got null)")
185 else if not obj
isa JsonObject then
186 proc
.die
("Wrong type for `reclamations#{i}` " +
187 "(expected JsonObject got {obj.class_name})")
190 var recl
= new Reclamation.from_json
(proc
, obj
)
191 if not month
.has
(recl
.date
) then
192 proc
.die
("Wrong `mois` for `soin` with id `{recl.care_id}`")
195 if file
.contract
.care_by_id
(recl
.care_id
) == null then
196 proc
.die
("Unknown `soin` with id `{recl.care_id}`")
207 # Inits `self` from the content of a JsonObject.
208 init from_json
(proc
: RefundProcessor, json
: JsonObject) do
209 proc
.check_key
(json
, "dossier")
210 var id
= json
["dossier"]
212 proc
.die
("Wrong type for `dossier` (expected String got null)")
214 else if not id
isa String then
215 proc
.die
("Wrong type for `dossier` (expected String got {id.class_name})")
219 parse_contract
(proc
, id
)
220 parse_client
(proc
, id
)
224 # Tries to parse the contract from `file_id` string.
225 private fun parse_contract
(proc
: RefundProcessor, file_id
: String) do
226 var kind
= file_id
.first
.to_s
227 if not proc
.check_format
(kind
, "^[A-E]\{1\}$") then
228 proc
.die
("Wrong contract (expected A, B, C, D or E got {kind})")
230 contract
= contract_factory
(proc
, kind
)
233 # Tries to parse the client number from the `file_id` string.
234 private fun parse_client
(proc
: RefundProcessor, file_id
: String) do
235 var num
= file_id
.substring_from
(1)
236 if not proc
.check_format
(num
, "^[0-9]\{6\}$") then
237 proc
.die
("Wrong format for `number` (expected XXXXXX got {num})")
240 client
= new Client(num
)
244 redef class ReclMonth
245 # Inits `self` from a `JsonObject`.
246 init from_json
(proc
: RefundProcessor, json
: JsonObject) do
247 proc
.check_key
(json
, "mois")
248 var month
= json
["mois"]
249 if month
== null then
250 proc
.die
("Wrong type for `mois` (expected String got null)")
252 else if not month
isa String then
253 proc
.die
("Wrong type for `mois` (expected String got {month.class_name})")
256 if not proc
.check_format
(month
, "^[0-9]\{4\}-[0-9]\{2\}$") then
257 proc
.die
("Wrong format for `mois` (expected AAAA-MM got {month})")
260 from_string
(proc
, month
)
263 # Inits `self` from a string representation formatted as `AAAA-MM`.
264 init from_string
(proc
: RefundProcessor, str
: String) do
265 var parts
= str
.split
("-")
266 var year
= parts
[0].to_i
267 var month
= parts
[1].to_i
268 if month
< 1 or month
> 12 then
269 proc
.die
("Wrong format for `mois` (expected AAAA-MM got {str})")
272 date
= new ReclDate(year
, month
, 1)
278 # Inits `self` from a `JsonObject`.
280 # Dies if the `json` input is invalid.
281 init from_json
(proc
: RefundProcessor, json
: JsonObject) do
282 proc
.check_key
(json
, "date")
283 var date
= json
["date"]
285 proc
.die
("Wrong type for `date` (expected String got null)")
287 else if not date
isa String then
288 proc
.die
("Wrong type for `date` (expected String got {date.class_name})")
291 if not proc
.check_format
(date
, "^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}$") then
292 proc
.die
("Wrong format for `date` (expected AAAA-MM-DD got {date})")
295 from_string
(proc
, date
)
298 # Inits `self` from its string representation formatted as `AAAA-MM`.
299 init from_string
(proc
: RefundProcessor, str
: String) do
300 var parts
= str
.split
("-")
302 month
= parts
[1].to_i
304 if month
< 1 or month
> 12 or day
< 1 or day
> 31 then
305 proc
.die
("Wrong format for `mois` (expected AAAA-MM got {str})")
308 init(year
, month
, day
)
312 redef class Reclamation
313 # Inits `self` from a `JsonObject`.
314 init from_json
(proc
: RefundProcessor, json
: JsonObject) do
315 care_id
= parse_care_id
(proc
, json
)
316 date
= new ReclDate.from_json
(proc
, json
)
317 fees
= parse_fees
(proc
, json
)
318 init(care_id
, date
, fees
)
321 # Inits `self` from its string representation formatted as `Int`.
322 private fun parse_care_id
(proc
: RefundProcessor, json
: JsonObject): Int do
323 proc
.check_key
(json
, "soin")
324 var id
= json
["soin"]
326 proc
.die
("Wrong type for `soin` (expected Int got null)")
328 else if not id
isa Int then
329 proc
.die
("Wrong type for `soin` (expected Int got {id.class_name})")
335 # Inits `self` from its string representation formatted as `0.00$`.
336 private fun parse_fees
(proc
: RefundProcessor, json
: JsonObject): Dollar do
337 proc
.check_key
(json
, "montant")
338 var fees
= json
["montant"]
340 proc
.die
("Wrong type for `fees` (expected String got null)")
342 else if not fees
isa String then
343 proc
.die
("Wrong type for `fees` (expected String got {fees.class_name})")
346 if not proc
.check_format
(fees
, "^[0-9]+((\\.|\\,)[0-9]+)?\\$$") then
347 proc
.die
("Wrong format for `montant` (expected XX.XX$ got {fees})")
350 return new Dollar.from_float
(fees
.basename
("$").to_f
)