• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ministryofjustice / cla_public / 21210

18 Apr 2024 09:48AM UTC coverage: 73.867% (-0.1%) from 74.006%
21210

Pull #1256

circleci

said-moj
Add field description
Pull Request #1256: WIP LGA-3027 - Add hlpas page after user fails means

2 of 8 new or added lines in 3 files covered. (25.0%)

1 existing line in 1 file now uncovered.

2216 of 3000 relevant lines covered (73.87%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.85
/cla_public/apps/checker/views.py
1
# coding: utf-8
2
"Checker views"
1✔
3
import logging
1✔
4

5
from cla_common.constants import ELIGIBILITY_REASONS
1✔
6
from flask import abort, render_template, redirect, session, url_for, views, request, current_app
1✔
7
from flask.ext.babel import lazy_gettext as _
1✔
8
from wtforms.validators import StopValidation
1✔
9

10
from cla_public.apps.checker import checker
1✔
11
from cla_public.apps.checker.api import get_organisation_list
1✔
12
from cla_public.apps.checker.forms import FindLegalAdviserForm
1✔
13
from cla_public.apps.checker.utils import category_option_from_name
1✔
14
from cla_public.apps.contact.forms import ContactForm
1✔
15
from cla_public.apps.checker.constants import (
1✔
16
    ORGANISATION_CATEGORY_MAPPING,
17
    NO_CALLBACK_CATEGORIES,
18
    LAALAA_PROVIDER_CATEGORIES_MAP,
19
    CATEGORIES,
20
)
21
from cla_public.apps.checker.forms import (
1✔
22
    AboutYouForm,
23
    YourBenefitsForm,
24
    PropertiesForm,
25
    SavingsForm,
26
    OutgoingsForm,
27
    IncomeForm,
28
    ReviewForm,
29
    AdditionalBenefitsForm,
30
)
31
from cla_public.apps.checker.constants import CATEGORY_ID_MAPPING
1✔
32
from cla_public.apps.checker.means_test import MeansTest, MeansTestError
1✔
33
from cla_public.apps.checker.validators import IgnoreIf
1✔
34
from cla_public.apps.checker import filters  # noqa: F401
1✔
35
from cla_public.libs.utils import override_locale, category_id_to_name
1✔
36
from cla_public.libs.views import AllowSessionOverride, FormWizard, FormWizardStep, RequiresSession, HasFormMixin
1✔
37
from cla_public.libs import laalaa, honeypot
1✔
38
from cla_public.apps.checker.cait_intervention import get_cait_params
1✔
39
import math
1✔
40

41
log = logging.getLogger(__name__)
1✔
42

43

44
@checker.after_request
1✔
45
def add_header(response):
46
    """
47
    Add no-cache headers
48
    """
49
    response.headers["Cache-Control"] = "no-cache, no-store, max-age=0"
1✔
50
    response.headers["Pragma"] = "no-cache"
1✔
51
    return response
1✔
52

53

54
class UpdatesMeansTest(object):
1✔
55
    def on_valid_submit(self):
1✔
56
        means_test = session.checker.get("means_test", MeansTest())
1✔
57
        means_test.update_from_session()
1✔
58
        try:
1✔
59
            means_test.save()
1✔
60
        except MeansTestError:
1✔
61
            self.form.errors["timeout"] = _(
1✔
62
                u"There was an error submitting your data. " u"Please check and try again."
63
            )
64
            return self.return_form_errors(step=self.name)
1✔
65
        else:
66
            return super(UpdatesMeansTest, self).on_valid_submit()
×
67

68

69
def is_null(field):
1✔
70
    if field.data is None:
1✔
71
        return True
1✔
72

73
    if hasattr(field, "_money_interval_field"):
1✔
74
        if field.data["per_interval_value"] is None:
×
75
            return True
×
76

77
    if isinstance(field.data, list):
1✔
78
        if len(field.data) == 0:
1✔
79
            return True
×
80

81
    if hasattr(field, "form"):
1✔
82
        if all(map(is_null, field.form._fields.values())):
×
83
            return True
×
84

85
    return False
1✔
86

87

88
class CheckerStep(UpdatesMeansTest, FormWizardStep):
1✔
89
    def completed_fields(self):
1✔
90
        session_data = session.checker.get(self.form_class.__name__, {})
1✔
91
        form = self.form_class(**session_data)
1✔
92

93
        def user_completed(field):
1✔
94
            name, field = field
1✔
95
            if name in ["csrf_token", honeypot.FIELD_NAME]:
1✔
96
                return False
1✔
97

98
            def should_ignore(field):
1✔
99
                ignore_validators = filter(lambda v: isinstance(v, IgnoreIf), field.validators)
1✔
100

101
                def triggered(v):
1✔
102
                    try:
1✔
103
                        v(form, field)
1✔
104
                    except StopValidation:
1✔
105
                        return True
1✔
106
                    return False
×
107

108
                return any(map(triggered, ignore_validators))
1✔
109

110
            if should_ignore(field):
1✔
111
                return False
1✔
112

113
            return not is_null(field)
1✔
114

115
        fields = filter(user_completed, form._fields.items())
1✔
116
        fields = map(lambda (name, field): (field), fields)
1✔
117
        return fields
1✔
118

119
    @property
1✔
120
    def is_completed(self):
121
        return session.checker.get(self.form_class.__name__, {}).get("is_completed", False)
1✔
122

123
    @property
1✔
124
    def is_current(self):
125
        if request.view_args:
1✔
126
            return self.name == request.view_args["step"]
1✔
127
        else:
128
            return False
×
129

130
    @property
1✔
131
    def count(self):
132
        steps = CheckerWizard("").relevant_steps[:-1]
1✔
133
        for index, item in enumerate(steps):
1✔
134
            if item.name == self.name:
1✔
135
                return index + 1
1✔
136
        return None
×
137

138
    def render(self, *args, **kwargs):
1✔
139
        steps = CheckerWizard("").relevant_steps[:-1]
1✔
140
        current_step = None
1✔
141
        if self.count:
1✔
142
            current_step = steps[self.count - 1]
1✔
143

144
        return render_template(self.template, steps=steps, current_step=current_step, form=self.form)
1✔
145

146

147
class ReviewStep(CheckerStep):
1✔
148
    @property
1✔
149
    def count(self):
150
        steps = CheckerWizard("").relevant_steps[:-1]
1✔
151
        return len(steps) + 1
1✔
152

153
    def render(self, *args, **kwargs):
1✔
154
        review_steps = CheckerWizard("").review_steps
1✔
155
        current_step = self
1✔
156
        return render_template(
1✔
157
            self.template, steps=review_steps, review_steps=review_steps, current_step=current_step, form=self.form
158
        )
159

160

161
class CheckerWizard(AllowSessionOverride, FormWizard):
1✔
162
    steps = [
1✔
163
        ("about", CheckerStep(AboutYouForm, "checker/about.html")),
164
        ("benefits", CheckerStep(YourBenefitsForm, "checker/benefits.html")),
165
        ("additional-benefits", CheckerStep(AdditionalBenefitsForm, "checker/additional-benefits.html")),
166
        ("property", CheckerStep(PropertiesForm, "checker/property.html")),
167
        ("savings", CheckerStep(SavingsForm, "checker/savings.html")),
168
        ("income", CheckerStep(IncomeForm, "checker/income.html")),
169
        ("outgoings", CheckerStep(OutgoingsForm, "checker/outgoings.html")),
170
        ("review", ReviewStep(ReviewForm, "checker/review.html")),
171
    ]
172

173
    @property
1✔
174
    def relevant_steps(self):
175
        return filter(lambda s: not self.skip(s), self.steps)
1✔
176

177
    @property
1✔
178
    def review_steps(self):
179
        return filter(lambda s: not self.skip_on_review(s), self.steps)
1✔
180

181
    def complete(self):
1✔
182
        # TODO: Is this still used now that scope diagnosis is taking care of F2F redirects for certain categories?
183
        if session.checker.needs_face_to_face:
×
184
            return self.redirect(url_for(".face-to-face", category=session.checker.category))
×
185

186
        if session.checker.ineligible:
×
187
            session.store(
×
188
                {
189
                    "ineligible_reasons": session.checker.ineligible_reasons,
190
                    "outcome": "referred/help-organisations/means",
191
                }
192
            )
NEW
193
            is_hlpas = session.stored.get("hlpas", "")
×
NEW
194
            if is_hlpas.lower() == "yes":
×
NEW
195
                return self.redirect(url_for("checker.hlpas_page"))
×
196

UNCOV
197
            return self.redirect(url_for(".help_organisations", category_name=session.checker.category_slug))
×
198

199
        if session.checker.need_more_info:
×
200
            session.store({"outcome": "provisional"})
×
201
            return self.redirect(url_for(".provisional"))
×
202

203
        session.store({"outcome": "eligible"})
×
204
        return self.redirect(url_for(".eligible"))
×
205

206
    def skip(self, step, for_review_page=False):
1✔
207

208
        if session.checker.needs_face_to_face:
1✔
209
            return True
×
210

211
        if step.name == "review":
1✔
212
            return False
1✔
213

214
        if not for_review_page and step.name not in ("about", "benefits") and session.checker.ineligible:
1✔
215
            return True
×
216

217
        if step.name == "benefits":
1✔
218
            return not session.checker.is_on_benefits
1✔
219

220
        if step.name == "additional-benefits":
1✔
221
            return not session.checker.is_on_other_benefits
1✔
222

223
        if step.name == "property":
1✔
224
            return not session.checker.owns_property
1✔
225

226
        if step.name == "savings":
1✔
227
            return not session.checker.has_savings_or_valuables
1✔
228

229
        if session.checker.is_on_passported_benefits and step.name not in ("about", "benefits"):
1✔
230
            return True
1✔
231

232
        return False
1✔
233

234
    def skip_on_review(self, step):
1✔
235
        if self.skip(step, for_review_page=True) or not step.is_completed:
1✔
236
            return True
1✔
237
        return False
1✔
238

239

240
checker.add_url_rule("/<step>", view_func=CheckerWizard.as_view("wizard"), methods=("GET", "POST"))
1✔
241

242

243
class LaaLaaView(views.MethodView):
1✔
244
    """
245
    Find a legal adviser view
246
    Requires no session so is directly accessible
247
    """
248

249
    template = "laalaa.html"
1✔
250
    view_clears_session = False
1✔
251

252
    @classmethod
1✔
253
    def handle_find_legal_adviser_form(cls, form, args):
254
        data = {}
×
255

256
        items_per_page = 10
×
257
        max_pages = 9
×
258

259
        page = 1
×
260
        if "postcode" in args:
×
261
            if form.validate():
×
262
                if "page" in args and args["page"].isdigit():
×
263
                    page = args["page"]
×
264
                try:
×
265
                    categories = LAALAA_PROVIDER_CATEGORIES_MAP.get(args.get("category"))
×
266
                    data = laalaa.find(args["postcode"], categories, page)
×
267
                    if "error" in data:
×
268
                        form.postcode.errors.append(data["error"])
×
269
                except laalaa.LaaLaaError:
×
270
                    form.postcode.errors.append(
×
271
                        u"%s %s" % (_("Error looking up legal advisers."), _("Please try again later."))
272
                    )
273

274
        data["num_pages"] = 1
×
275
        if "count" in data:
×
276
            data["num_pages"] = min(math.ceil(data["count"] / items_per_page), max_pages)
×
277

278
        data["current_page"] = page
×
279
        return data
×
280

281
    def get(self):
1✔
282
        if self.view_clears_session:
×
283
            session.clear_checker()
×
284
        category = request.args.get("category")
×
285
        if category == "other":
×
286
            category = ""
×
287
        category_name = None
×
288
        if category:
×
289
            category_name = category_id_to_name(category)
×
290

291
        return self.render(
×
292
            category=category,
293
            category_name=category_name,
294
            extra_context=self.get_extra_postcode_context(request.args.get("postcode")),
295
        )
296

297
    def render(self, category, category_name, extra_context={}):
1✔
298
        form = FindLegalAdviserForm(request.args, csrf_enabled=False)
×
299
        data = self.handle_find_legal_adviser_form(form, request.args)
×
300

301
        return render_template(
×
302
            self.template, category=category, category_name=category_name, data=data, form=form, **extra_context
303
        )
304

305
    @classmethod
1✔
306
    def get_extra_postcode_context(cls, postcode):
307
        prefix = postcode[:2].upper() if postcode else ""
1✔
308
        scottish_postcode_prefix = cls.get_scottish_postcode_prefixes()
1✔
309
        return {
1✔
310
            "postcode_info": {
311
                "is_scottish_postcode": prefix in scottish_postcode_prefix,
312
                "is_ni_postcode": prefix == "BT",
313
                "is_mann_postcode": prefix == "IM",
314
                "is_jersey_postcode": prefix == "JE",
315
                "is_guernsey_postcode": prefix == "GY",
316
            }
317
        }
318

319
    @classmethod
1✔
320
    def get_scottish_postcode_prefixes(cls):
321
        return [
1✔
322
            "AB",
323
            "DD",
324
            "DG",
325
            "EH",
326
            "FK",
327
            "G1",
328
            "G2",
329
            "G3",
330
            "G4",
331
            "G5",
332
            "G6",
333
            "G7",
334
            "G8",
335
            "G9",
336
            "G0",
337
            "HS",
338
            "IV",
339
            "KA",
340
            "KW",
341
            "KY",
342
            "ML",
343
            "PA",
344
            "PH",
345
            "TD",
346
            "ZE",
347
        ]
348

349

350
checker.add_url_rule("/find-a-legal-adviser", view_func=LaaLaaView.as_view("laalaa"))
1✔
351

352

353
class FaceToFace(LaaLaaView):
1✔
354
    template = "checker/result/face-to-face.html"
1✔
355
    view_clears_session = True
1✔
356

357

358
checker.add_url_rule("/scope/refer/legal-adviser", view_func=FaceToFace.as_view("face-to-face"))
1✔
359

360

361
class EligibleFaceToFace(LaaLaaView):
1✔
362
    template = "checker/result/eligible-f2f.html"
1✔
363
    view_clears_session = True
1✔
364

365

366
checker.add_url_rule("/result/refer/legal-adviser", view_func=EligibleFaceToFace.as_view("find-legal-adviser"))
1✔
367

368

369
class Eligible(HasFormMixin, RequiresSession, views.MethodView, object):
1✔
370
    form_class = ContactForm
1✔
371

372
    def get(self):
1✔
373
        steps = CheckerWizard("").relevant_steps[:-1]
×
374
        if session.checker.category in NO_CALLBACK_CATEGORIES:
×
375
            session.store({"outcome": "referred/f2f/means"})
×
376
            return redirect(url_for(".find-legal-adviser", category=session.checker.category))
×
377

378
        current_step = {"count": len(steps) + 1, "is_current": True, "is_completed": False}
×
379

380
        return render_template(
×
381
            "checker/result/eligible.html",
382
            current_step=current_step,
383
            steps=steps,
384
            form=self.form,
385
            family_issue_flag=current_app.config["FAMILY_ISSUE_FEATURE_FLAG"],
386
        )
387

388

389
checker.add_url_rule("/result/eligible", view_func=Eligible.as_view("eligible"), methods=("GET", "POST"))
1✔
390

391
checker.add_url_rule("/result/provisional", view_func=Eligible.as_view("provisional"), methods=("GET", "POST"))
1✔
392

393

394
class HelpOrganisations(views.MethodView):
1✔
395
    _template = "checker/result/ineligible.html"
1✔
396

397
    def get_context(self, category_name, diagnosis_previous_choices):
1✔
398
        category_name = category_name.replace("-", " ").capitalize()
1✔
399

400
        # force english as knowledge base languages are in english
401
        with override_locale("en"):
1✔
402
            category, name, desc = category_option_from_name(category_name)
1✔
403

404
            if category is None:
1✔
405
                abort(404)
×
406

407
            name = unicode(name)
1✔
408

409
            category_name = ORGANISATION_CATEGORY_MAPPING.get(name, name)
1✔
410
        trans_category_name = next(c[1] for c in CATEGORIES if c[0] == category)
1✔
411

412
        ineligible_reasons = session.stored.get("ineligible_reasons", [])
1✔
413

414
        organisations = get_organisation_list(article_category__name=category_name)
1✔
415

416
        params = {
1✔
417
            "organisations": organisations,
418
            "category": category,
419
            "category_name": trans_category_name,
420
            "ELIGIBILITY_REASONS": ELIGIBILITY_REASONS,
421
            "ineligible_reasons": ineligible_reasons,
422
            "truncate": 5,
423
        }
424
        params.update(
1✔
425
            get_cait_params(category_name, organisations, diagnosis_previous_choices, truncate=params["truncate"])
426
        )
427
        return params
1✔
428

429
    def clear_session(self):
1✔
430
        if session.checker:
1✔
431
            session.clear_checker()
×
432

433
    def get(self, category_name):
1✔
434
        choices = session.checker.get("diagnosis_previous_choices", [])
1✔
435

436
        self.clear_session()
1✔
437

438
        return render_template(self._template, **self.get_context(category_name, choices))
1✔
439

440

441
checker.add_url_rule(
1✔
442
    "/result/refer/<category_name>", view_func=HelpOrganisations.as_view("help_organisations"), methods=["GET"]
443
)
444

445

446
@checker.route("/result/hlpas")
1✔
447
def hlpas_page():
NEW
448
    context = {"url": url_for(".face-to-face", category="hlpas", hlpas="yes")}
×
NEW
449
    return render_template("hlpas.html", **context)
×
450

451

452
@checker.route("/legal-aid-available")
1✔
453
def interstitial():
454
    """
455
    Interstitial page after passing scope test
456
    """
457
    if not session or not session.is_current or not session.checker.category:
×
458
        return redirect(url_for("base.session_expired"))
×
459

460
    category = CATEGORY_ID_MAPPING.get(session.checker.category, session.checker.category)
×
461
    category_name = session.checker.category_name
×
462
    with override_locale("en"):
×
463
        category_name_english = unicode(session.checker.category_name)
×
464

465
    organisations = get_organisation_list(article_category__name=category_name_english)
×
466
    is_hlpas = request.args.get("hlpas", "no").lower() == "yes"
×
467
    context = {
×
468
        "category": category,
469
        "category_name": category_name,
470
        "organisations": organisations,
471
        "is_hlpas": is_hlpas,
472
    }
473
    return render_template("interstitial.html", **context)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc