5
5
from functools import cached_property
6
6
import importlib
7
7
import logging
8
+ from pathlib import Path
8
9
import uuid
9
10
11
+ from django import template
10
12
from django .conf import settings
11
13
from django .core .exceptions import ValidationError
12
14
from django .contrib .auth .models import Group , User
24
26
logger = logging .getLogger (__name__ )
25
27
26
28
29
+ def template_path (template_name : str ) -> Path :
30
+ """Get a `pathlib.Path` for the named template, or None if it can't be found.
31
+
32
+ A `template_name` is the app-local name, e.g. `enrollment/success.html`.
33
+
34
+ Adapted from https://stackoverflow.com/a/75863472.
35
+ """
36
+ for engine in template .engines .all ():
37
+ for loader in engine .engine .template_loaders :
38
+ for origin in loader .get_template_sources (template_name ):
39
+ path = Path (origin .name )
40
+ if path .exists () and path .is_file ():
41
+ return path
42
+ return None
43
+
44
+
27
45
class SecretNameField (models .SlugField ):
28
46
"""Field that stores the name of a secret held in a secret store.
29
47
@@ -264,6 +282,30 @@ def transit_processor_client_secret(self):
264
282
def enrollment_flows (self ):
265
283
return self .enrollmentflow_set
266
284
285
+ def clean (self ):
286
+ if self .active :
287
+ errors = {}
288
+ message = "This field is required for active transit agencies."
289
+ needed = dict (
290
+ short_name = self .short_name ,
291
+ long_name = self .long_name ,
292
+ phone = self .phone ,
293
+ info_url = self .info_url ,
294
+ )
295
+ for k , v in needed .items ():
296
+ if not v :
297
+ errors [k ] = ValidationError (message )
298
+
299
+ if not template_path (self .index_template ):
300
+ errors ["index_template" ] = ValidationError (f"Template not found: { self .index_template } " )
301
+ if not template_path (self .eligibility_index_template ):
302
+ errors ["eligibility_index_template" ] = ValidationError (
303
+ f"Template not found: { self .eligibility_index_template } "
304
+ )
305
+
306
+ if errors :
307
+ raise ValidationError (errors )
308
+
267
309
@staticmethod
268
310
def by_id (id ):
269
311
"""Get a TransitAgency instance by its ID."""
@@ -493,6 +535,17 @@ def uses_claims_verification(self):
493
535
"""True if this flow verifies via the claims provider and has a scope and claim. False otherwise."""
494
536
return self .claims_provider is not None and bool (self .claims_scope ) and bool (self .claims_eligibility_claim )
495
537
538
+ @property
539
+ def claims_scheme (self ):
540
+ return self .claims_scheme_override or self .claims_provider .scheme
541
+
542
+ @property
543
+ def claims_all_claims (self ):
544
+ claims = [self .claims_eligibility_claim ]
545
+ if self .claims_extra_claims is not None :
546
+ claims .extend (self .claims_extra_claims .split ())
547
+ return claims
548
+
496
549
@property
497
550
def eligibility_verifier (self ):
498
551
"""A str representing the entity that verifies eligibility for this flow.
@@ -520,23 +573,6 @@ def enrollment_success_template(self):
520
573
else :
521
574
return self .enrollment_success_template_override or f"{ prefix } --{ self .agency_card_name } .html"
522
575
523
- def eligibility_form_instance (self , * args , ** kwargs ):
524
- """Return an instance of this flow's EligibilityForm, or None."""
525
- if not bool (self .eligibility_form_class ):
526
- return None
527
-
528
- # inspired by https://stackoverflow.com/a/30941292
529
- module_name , class_name = self .eligibility_form_class .rsplit ("." , 1 )
530
- FormClass = getattr (importlib .import_module (module_name ), class_name )
531
-
532
- return FormClass (* args , ** kwargs )
533
-
534
- @staticmethod
535
- def by_id (id ):
536
- """Get an EnrollmentFlow instance by its ID."""
537
- logger .debug (f"Get { EnrollmentFlow .__name__ } by id: { id } " )
538
- return EnrollmentFlow .objects .get (id = id )
539
-
540
576
def clean (self ):
541
577
supports_expiration = self .supports_expiration
542
578
expiration_days = self .expiration_days
@@ -556,18 +592,22 @@ def clean(self):
556
592
if errors :
557
593
raise ValidationError (errors )
558
594
559
- @property
560
- def claims_scheme (self ):
561
- if not self .claims_scheme_override :
562
- return self .claims_provider .scheme
563
- return self .claims_scheme_override
595
+ def eligibility_form_instance (self , * args , ** kwargs ):
596
+ """Return an instance of this flow's EligibilityForm, or None."""
597
+ if not bool (self .eligibility_form_class ):
598
+ return None
564
599
565
- @property
566
- def claims_all_claims (self ):
567
- claims = [self .claims_eligibility_claim ]
568
- if self .claims_extra_claims is not None :
569
- claims .extend (self .claims_extra_claims .split ())
570
- return claims
600
+ # inspired by https://stackoverflow.com/a/30941292
601
+ module_name , class_name = self .eligibility_form_class .rsplit ("." , 1 )
602
+ FormClass = getattr (importlib .import_module (module_name ), class_name )
603
+
604
+ return FormClass (* args , ** kwargs )
605
+
606
+ @staticmethod
607
+ def by_id (id ):
608
+ """Get an EnrollmentFlow instance by its ID."""
609
+ logger .debug (f"Get { EnrollmentFlow .__name__ } by id: { id } " )
610
+ return EnrollmentFlow .objects .get (id = id )
571
611
572
612
573
613
class EnrollmentEvent (models .Model ):
0 commit comments