Calculation code comments Calculation controller
On GitHub: app/controllers/calculator_controller.rb
2 class CalculatorController < ApplicationController
Include code from each of the modules for the different styles of calculation.
5 include Calculations::BicameralBothHousesSitting
6 include Calculations::BicameralSiEitherHouseSitting
7 include Calculations::CommonsOnlySi
8 include Calculations::CommonsOnlySittingDays
9 include Calculations::Pnsi
10 include Calculations::Treaty
11 include Calculations::BicameralBothHousesSittingReverse
12 include Calculations::BicameralSiEitherHouseSittingReverse
13 include Calculations::CommonsOnlySiReverse
14 include Calculations::CommonsOnlySittingDaysReverse
15 include Calculations::PnsiReverse
16 include Calculations::TreatyReverse
This is the code to provide a list of calculators.
19 def index
We set the meta information for the page.
22 @page_title = "Calculators"
23 @description = "Calculators made available by #{$SITE_TITLE}."
24 @crumb << { label: 'Calculators', url: nil }
25 @section = 'calculators'
26 end
This is the code to provide information for the form that users can fill in to calculate the end date of a scrutiny period by procedure.
29 def scrutiny_period
We find all the active procedures in display order - to populate the procedure radio buttons on the form.
32 @procedures = Procedure.all.where( 'active is true' ).order( 'display_order asc' )
We set the meta information for the page.
35 @page_title = "Scrutiny end date calculator"
36 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny end date</span>".html_safe
37 @description = "A calculator to determine the estimated end date of scrutiny for instruments before Parliament."
38 @crumb << { label: 'Calculators', url: calculator_list_url }
39 @crumb << { label: 'Scrutiny end date', url: nil }
40 @section = 'calculators'
41 @subsection = 'scrutiny-calculator'
42 end
This is the code to provide information for the form that users can fill in to calculate the start date of a scrutiny period by procedure.
45 def reverse_scrutiny_period
We find all the active procedures in display order - to populate the procedure radio buttons on the form.
48 @procedures = Procedure.all.where( 'active is true' ).order( 'display_order asc' )
We set the meta information for the page.
51 @page_title = "Scrutiny start date calculator"
52 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny start date</span>".html_safe
53 @description = "A calculator to determine the estimated start date of scrutiny for instruments before Parliament."
54 @crumb << { label: 'Calculators', url: calculator_list_url }
55 @crumb << { label: 'Scrutiny start date', url: nil }
56 @section = 'calculators'
57 @subsection = 'scrutiny-calculator-reverse'
58 end
This is the code to provide information for the form that users wishing to run a specific calculation style can fill in.
61 def style
We get the calculation style if it's been passed as a parameter.
64 calculation_style = params['calculation-style']
65 @calculation_style = calculation_style.to_i if calculation_style
We set the meta information for the page.
68 @page_title = "Scrutiny end date calculator for a calculation style"
69 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny end date for a calculation style</span>".html_safe
70 @description = "A calculator to determine the estimated end date of scrutiny for instruments before Parliament for a given calculation style."
71 @crumb << { label: 'About', url: meta_list_url }
72 @crumb << { label: 'Librarian tools', url: meta_librarian_tools_url }
73 @crumb << { label: 'Scrutiny period calculator by calculation style', url: nil }
74 end
This code runs the scrutiny period calculation.
77 def calculate
In order to calculate the anticipated end date of the scrutiny period for a forward calculation or the start date of the scrutiny period for a reverse calculation, we need:
- the start date, for example: "2020-05-06"
Note that this is the start date of the calculation. For a reverse calculation, this will be the end date of the scrutiny period.
83 start_date = params['start-date']
- the day count and
86 day_count = params['day-count']
- either the type of the procedure, which we refer to by a number
89 procedure = params['procedure']
- or the calculation style, which we also refer to by a number
92 calculation_style = params['calculation-style']
Optionally, we may have been passed a direction parameter.
This is populated with 'reverse' if we intend the calculation to return an anticipated start date given an end date.
96 direction = params['direction']
97 @direction = direction
Calling this method also sets instance variables for start date, day count, procedure and calculation style.
If we don't have enough information to proceed with the calculation ...
101 unless calculation_can_proceed?( start_date, day_count, procedure, calculation_style )
... we call the insufficient information method.
104 insufficient_information
Otherwise, if we do have enough information to proceed with the calculation ...
107 else
... if the day count has not been provided or the day count is invalid ...
110 if !@day_count or is_day_count_invalid?( @day_count )
We set generic meta information for both forward and reverse calculations.
113 @crumb << { label: 'Calculators', url: calculator_list_url }
114 @section = 'calculators'
If the direction of the calculation is reverse ...
117 if @direction == 'reverse'
... we set the meta information for the page ...
120 @page_title = "Scrutiny start date - number of days to count required"
121 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny start date - number of days to count required</span>".html_safe
122 @description = "Number of days to count required for a calculation to determine the estimated start date of scrutiny for instruments before Parliament."
123 @crumb << { label: 'Scrutiny start date', url: reverse_calculator_form_url }
124 @subsection = 'scrutiny-calculator-reverse'
Otherwise, if the direction of calculation is not reverse ...
127 else
... we set the meta information for the page ...
130 @page_title = "Scrutiny end date - number of days to count required"
131 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny end date - number of days to count required</span>".html_safe
132 @description = "Number of days to count required for a calculation to determine the estimated end date of scrutiny for instruments before Parliament."
133 @crumb << { label: 'Scrutiny end date', url: calculator_form_url }
134 @crumb << { label: 'Number of days to count required', url: nil }
135 @subsection = 'scrutiny-calculator'
136 end
137 @crumb << { label: 'Number of days to count required', url: nil }
... and we render the day count form.
140 render :template => 'calculator/day_count_form'
Otherwise, if the day count has been provided and the day count is not invalid ...
143 else
If the procedure has been passed as a parameter ...
146 if @procedure
... to calculate the anticipated end date, we select the calculation based on the type of procedure:
149 case @procedure.id
- Legislative Reform Orders, Public Body Orders, Localism Orders and enhanced affirmatives under the Investigatory Powers Act 2016
152 when 1, 17, 18, 19, 2, 4, 21, 22, 23
154 if @direction == 'reverse'
155 @scrutiny_end_date = bicameral_calculation_both_houses_sitting_reverse( @start_date, @day_count )
156 @message = "In order to meet the target end date, the instrument must be <em>laid on or before</em> the anticipated start date of the scrutiny period."
157 else
158 @start_date_type = "laying date"
159 @scrutiny_end_date = bicameral_calculation_both_houses_sitting( @start_date, @day_count )
160 end
- Proposed Statutory Instruments (PNSIs)
163 when 3
165 if @direction == 'reverse'
166 @scrutiny_end_date = pnsi_calculation_reverse( @start_date, @day_count )
167 @message = "In order to meet the target end date, the instrument must be <em>laid earlier than</em> the anticipated start date of the scrutiny period."
168 else
169 @scrutiny_end_date = pnsi_calculation( @start_date, @day_count )
170 @start_date_type = "laying date"
171 end
- Commons only negative Statutory Instruments
174 when 5
176 if @direction == 'reverse'
177 @scrutiny_end_date = commons_only_si_calculation_reverse( @start_date, @day_count )
178 @message = "In order to meet the target end date, the instrument must be <em>laid on or before</em> the anticipated start date of the scrutiny period."
179 else
180 @scrutiny_end_date = commons_only_si_calculation( @start_date, @day_count )
181 @start_date_type = "laying date"
182 end
- Commons and Lords negative Statutory Instruments, proposed and draft affirmative remedial orders
185 when 6, 13, 14
187 if @direction == 'reverse'
188 @scrutiny_end_date = bicameral_si_either_house_sitting_calculation_reverse( @start_date, @day_count )
189 @message = "In order to meet the target end date, the instrument must be <em>laid on or before</em> the anticipated start date of the scrutiny period."
190 else
191 @start_date_type = "laying date"
192 @scrutiny_end_date = bicameral_si_either_house_sitting_calculation( @start_date, @day_count )
193 end
- Some Commons only made affirmative Statutory Instruments
196 when 7
198 if @direction == 'reverse'
199 @scrutiny_end_date = commons_only_si_calculation_reverse( @start_date, @day_count )
200 @message = "In order to meet the target end date, the instrument must be <em>made on or before</em> the anticipated start date of the scrutiny period."
201 else
202 @scrutiny_end_date = commons_only_si_calculation( @start_date, @day_count )
203 @start_date_type = "making date"
204 end
- Commons and Lords made affirmative Statutory Instruments where both Houses are sitting
207 when 8
209 if @direction == 'reverse'
210 @scrutiny_end_date = bicameral_calculation_both_houses_sitting_reverse( @start_date, @day_count )
211 @message = "In order to meet the target end date, the instrument must be <em>made on or before</em> the anticipated start date of the scrutiny period."
212 else
213 @start_date_type = "making date"
214 @scrutiny_end_date = bicameral_calculation_both_houses_sitting( @start_date, @day_count )
215 end
- Commons and Lords made affirmative Statutory Instruments where either House is sitting and made affirmative remedial orders
218 when 9, 15, 16
220 if @direction == 'reverse'
221 @scrutiny_end_date = bicameral_si_either_house_sitting_calculation_reverse( @start_date, @day_count )
222 @message = "In order to meet the target end date, the instrument must be <em>made on or before</em> the anticipated start date of the scrutiny period."
223 else
224 @start_date_type = "making date"
225 @scrutiny_end_date = bicameral_si_either_house_sitting_calculation( @start_date, @day_count )
226 end
- Treaty period A
229 when 10
231 if @direction == 'reverse'
232 @scrutiny_end_date = treaty_calculation_reverse( @start_date, @day_count )
233 @message = "In order to meet the target end date, the treaty must be <em>laid earlier than</em> the anticipated start date of the scrutiny period."
234 else
235 @scrutiny_end_date = treaty_calculation( @start_date, @day_count )
236 @start_date_type = "laying date"
237 end
- Treaty period B
240 when 11
242 if @direction == 'reverse'
243 @scrutiny_end_date = treaty_calculation_reverse( @start_date, @day_count )
244 @message = "In order to meet the target end date, the ministerial statement must be <em>made earlier than</em> the anticipated start date of the scrutiny period."
245 else
246 @scrutiny_end_date = treaty_calculation( @start_date, @day_count )
247 @start_date_type = "date of Ministerial statement"
248 end
- Published drafts under the European Union (Withdrawal) Act 2018
251 when 12
253 if @direction == 'reverse'
254 @scrutiny_end_date = bicameral_calculation_both_houses_sitting_reverse( @start_date, @day_count )
255 @message = "In order to meet the target end date, the draft must be <em>published on or before</em> the anticipated start date of the scrutiny period."
256 else
257 @start_date_type = "date of publication"
258 @scrutiny_end_date = bicameral_calculation_both_houses_sitting( @start_date, @day_count )
259 end
- National Policy Statements.
262 when 20
264 if @direction == 'reverse'
265 @scrutiny_end_date = commons_only_sitting_days_reverse( @start_date, @day_count )
266 @message = "In order to meet the target end date, the national policy statement must be <em>laid earlier than</em> the anticipated start date of the scrutiny period."
267 else
268 @scrutiny_end_date = commons_only_sitting_days( @start_date, @day_count )
269 @start_date_type = "laying date"
270 end
271 end
Otherwise, if the calculation style has been selected ...
274 elsif @calculation_style
... to calculate the anticipated end date, we select the calculation based on the calculation style:
277 case @calculation_style
- Calculation style 1
280 when 1
282 @scrutiny_end_date = bicameral_calculation_both_houses_sitting( @start_date, @day_count )
- Calculation style 2
285 when 2
287 @scrutiny_end_date = bicameral_si_either_house_sitting_calculation( @start_date, @day_count )
- Calculation style 3
290 when 3
292 @scrutiny_end_date = commons_only_si_calculation( @start_date, @day_count )
- Calculation style 4
295 when 4
297 @scrutiny_end_date = pnsi_calculation( @start_date, @day_count )
- Calculation style 5
300 when 5
302 @scrutiny_end_date = treaty_calculation( @start_date, @day_count )
- Calculation style 6
305 when 6
307 @scrutiny_end_date = commons_only_sitting_days( @start_date, @day_count )
308 else
... we add a reason to the missing information array ...
311 @missing_information << 'a valid calculation style'
... and call the insufficient information method.
314 insufficient_information
315 end
316 end
We set the generic meta information.
318 @json_url = request.original_fullpath.sub '?', '.json?'
319 @crumb << { label: 'Calculators', url: calculator_list_url }
320 @section = 'calculators'
If the direction of the calculation is reverse ...
323 if @direction == 'reverse'
... we set the meta information for the page.
326 @page_title = "Scrutiny start date calculation"
327 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny start date calculation</span>".html_safe
328 @description = "A calculation to determine the estimated start date of scrutiny for instruments before Parliament."
329 @calendar_links << ['Anticipated start date of the scrutiny period', request.original_fullpath.sub( '?', '.ics?' )]
330 @crumb << { label: 'Scrutiny start date', url: reverse_calculator_form_url }
331 @crumb << { label: 'Calculation', url: nil }
332 @subsection = 'scrutiny-calculator-reverse'
334 render :template => 'calculator/scrutiny_reverse'
Otherwise, if the direction of the calculation is not reverse ...
337 else
.. we set the meta information for the page.
340 @page_title = "Scrutiny end date calculation"
341 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny end date calculation</span>".html_safe
342 @description = "A calculation to determine the anticipated end date of scrutiny for instruments before Parliament."
343 @json_url = request.original_fullpath.sub '?', '.json?'
344 @calendar_links << ['Anticipated end date of the scrutiny period', request.original_fullpath.sub( '?', '.ics?' )]
345 @crumb << { label: 'Scrutiny end date', url: calculator_form_url }
346 @crumb << { label: 'Calculation', url: nil }
347 @subsection = 'scrutiny-calculator'
348 end
349 end
350 end
351 end
A method to check if the scrutiny period calculation can proceed.
This method also creates the start date, day count, procedure and calculation style as instance variables.
355 def calculation_can_proceed?( start_date, day_count, procedure, calculation_style )
We create a variable to hold a boolean, determining if the scrutiny period calculation can proceed.
358 calculation_can_proceed = true
We create an array to hold any errors we find as a result of missing information.
361 @missing_information = []
We check for the presence of a valid start date.
If the start date is present ...
365 if start_date
... we attempt to convert the start date string into a date, storing the result as a instance variable.
368 begin
369 @start_date = Date.parse( start_date )
If the start date string cannot be converted into a date ...
372 rescue ArgumentError
... we flag that the calculation cannot proceed ...
375 calculation_can_proceed = false
... and add a reason to the missing information array.
378 @missing_information << invalid_date_type_label
379 end
Otherwise, if the start date is not present ...
382 else
... we flag that the calculation cannot proceed ...
385 calculation_can_proceed = false
... and add a reason to the missing information array.
388 @missing_information << missing_date_type_label
389 end
We check for the presence of a valid day count.
If a day count has been passed ...
393 if day_count
... we convert the day count to an integer, storing the result as a instance variable.
396 @day_count = day_count.to_i
If the day count is an invalid day count ...
399 if is_day_count_invalid?( @day_count )
... we do not flag that the calculation cannot proceed because this will be picked up by the day count form.
We add a reason to the missing information array.
404 @missing_information << 'a valid day count'
405 end
Otherwise, if a day count has not been passed ...
408 else
... we do not flag that the calculation cannot proceed because this will be picked up by the day count form.
We add a reason to the missing information array.
413 @missing_information << 'a day count'
414 end
We check for the presence of a valid procedure or a valid calculation style.
If the calculation has been passed neither a procedure, nor a calculation style ...
418 if !procedure and !calculation_style
... we flag that the calculation cannot proceed ...
421 calculation_can_proceed = false
... and add a reason to the missing information array.
424 @missing_information << 'a procedure or calculation style'
Otherwise, if the calculation has been passed a procedure ...
427 elsif procedure
... we attempt to find the procedure, storing the result as a instance variable.
430 @procedure = Procedure.find_by_id( procedure )
If we fail to find the procedure ...
433 unless @procedure
... we flag that the calculation cannot proceed ...
436 calculation_can_proceed = false
... and add a reason to the missing information array.
439 @missing_information << 'a valid procedure'
440 end
Otherwise, if the calculation has been passed a calculation style ...
443 elsif calculation_style
... we convert the calculation style ID to an integer, storing the result as a instance variable.
446 @calculation_style = calculation_style.to_i
If the calculation style is an invalid calculation style ...
449 if is_calculation_style_invalid?( @calculation_style )
... we flag that the calculation cannot proceed ...
452 calculation_can_proceed = false
... and add a reason to the missing information array.
455 @missing_information << 'a valid calculation style'
456 end
457 end
We return the calculation can proceed boolean.
460 calculation_can_proceed
461 end
A method to check if the day count is valid.
464 def is_day_count_valid?( day_count )
We create a boolean to hold the validity of the day count.
467 is_day_count_valid = true
If the day count is zero or a negative number ...
470 if day_count == 0 or day_count.negative?
... we set the is day count valid boolean to false.
473 is_day_count_valid = false
474 end
We return the is day count valid boolean.
477 is_day_count_valid
478 end
A method to check if the day count is invalid.
481 def is_day_count_invalid?( day_count )
We flip the boolean returned by the is day count valid method.
484 !is_day_count_valid?( day_count )
485 end
A method to check if the calculation style is valid.
488 def is_calculation_style_valid?( calculation_style )
We create a boolean to hold the validity of the calculation style.
491 is_calculation_style_valid = true
If the calculation style is zero or a negative number ...
494 if calculation_style == 0 or calculation_style.negative?
... we set the is calculation style valid boolean to false.
497 is_calculation_style_valid = false
498 end
We return the is calculation style valid boolean.
501 is_calculation_style_valid
502 end
A method to check if the calculation style is invalid.
505 def is_calculation_style_invalid?( calculation_style )
We flip the boolean returned by the is calculation style valid method.
508 !is_calculation_style_valid?( calculation_style )
509 end
A method to report that we have insufficient information for the scrutiny period calculation to proceed.
512 def insufficient_information
We set the generic meta information.
515 @crumb << { label: 'Calculators', url: calculator_list_url }
516 @section = 'calculators'
If the calculation is a reverse calculation ...
519 if @direction == 'reverse'
... we set the meta information for the page ...
522 @page_title = "Scrutiny start date - more information required"
523 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny start date - more information required</span>".html_safe
524 @description = "More information required for a calculation to determine the estimated start date of scrutiny for instruments before Parliament."
525 @crumb << { label: 'Scrutiny start date', url: reverse_calculator_form_url }
526 @subsection = 'scrutiny-calculator-reverse'
Otherwise, if the calculation is not a reverse calculation ...
529 else
... we set the meta information for the page ...
532 @page_title = "Scrutiny end date - more information required"
533 @multiline_page_title = "Calculators <span class='subhead'>Scrutiny end date - more information required</span>".html_safe
534 @description = "More information required for a calculation to determine the estimated end date of scrutiny for instruments before Parliament."
535 @crumb << { label: 'Scrutiny end date', url: calculator_form_url }
536 @subsection = 'scrutiny-calculator'
537 end
We set more generic meta information.
540 @crumb << { label: 'More information required', url: nil }
We display the not enough information message.
543 render :template => 'calculator/not_enough_information'
544 end
A method to return the label of any missing date type.
For the end date scrutiny period calculator, this will be the start date.
For the start date scrutiny period calculator, this will be the end date.
549 def missing_date_type_label
If the calculation is a reverse calculation, calculating a start date ...
552 if @direction == 'reverse'
... the missing date parameter is an end date.
555 'an end date'
Otherwise, if the calculation is a forward calculation, calculating an end date ...
558 else
... the missing date parameter is a start date.
561 'a start date'
562 end
563 end
A method to return the label of any invalid date type.
For the end date scrutiny period calculator, this will be the start date.
For the start date scrutiny period calculator, this will be the end date.
568 def invalid_date_type_label
If the calculation is a reverse calculation, calculating a start date ...
571 if @direction == 'reverse'
... the missing date parameter is an end date.
574 'a valid end date'
Otherwise, if the calculation is a forward calculation, calculating an end date ...
577 else
... the missing date parameter is a start date.
580 'a valid start date'
581 end
582 end
583 end