From 2bf006c448685fc43595c395cf1b3d43af33ebd5 Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Tue, 5 May 2026 14:18:39 +0100 Subject: [PATCH 1/5] initial pass at fixing sonarqube tests --- infrastructure/bootstrap/hub.bicep | 12 +++-- .../bootstrap/modules/computeGallery.bicep | 2 +- .../bootstrap/modules/privateEndpoint.bicep | 4 +- .../forms/agree_terms_of_use_form.py | 7 +-- .../questions/migrations/0001_initial.py | 51 ++++++++++--------- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/infrastructure/bootstrap/hub.bicep b/infrastructure/bootstrap/hub.bicep index b17eaa81..25a967fc 100644 --- a/infrastructure/bootstrap/hub.bicep +++ b/infrastructure/bootstrap/hub.bicep @@ -44,6 +44,8 @@ var privateEndpointSubnetName = 'sn-hub-${hubType}-${regionShortName}-private-en var storageAccountName = 'sa${appShortName}${hubType}${regionShortName}state' var computeGalleryName = '${appShortName}_hub_compute_gallery' +var roleDefinitions = 'Microsoft.Authorization/roleDefinitions' + var miADOtoAZname = 'mi-${appShortName}-${hubType}-adotoaz-${regionShortName}' var miGHtoADOname = 'mi-${appShortName}-${hubType}-ghtoado-${regionShortName}' @@ -163,7 +165,7 @@ module managedIdentiyADOtoAZ 'modules/managedIdentity.bicep' = { resource networkContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().subscriptionId, hubType, 'networkContributor') properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleID.networkContributor) + roleDefinitionId: subscriptionResourceId(roleDefinitions, roleID.networkContributor) principalId: managedIdentiyADOtoAZ.outputs.miPrincipalID description: '${miADOtoAZname} Network Contributor access to subscription' } @@ -186,7 +188,7 @@ resource userAccessAdministratorAssignment 'Microsoft.Authorization/roleAssignme resource CDNContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().subscriptionId, hubType, 'CDNContributor') properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleID.CDNContributor) + roleDefinitionId: subscriptionResourceId(roleDefinitions, roleID.CDNContributor) principalId: managedIdentiyADOtoAZ.outputs.miPrincipalID description: '${miADOtoAZname} CDN Contributor access to subscription' } @@ -196,7 +198,7 @@ resource CDNContributorAssignment 'Microsoft.Authorization/roleAssignments@2022- resource TerraformContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().subscriptionId, hubType, 'TerraformContributor') properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleID.contributor) + roleDefinitionId: subscriptionResourceId(roleDefinitions, roleID.contributor) principalId: managedIdentiyADOtoAZ.outputs.miPrincipalID description: '${miADOtoAZname} Terraform Contributor access to subscription' } @@ -206,7 +208,7 @@ resource TerraformContributorAssignment 'Microsoft.Authorization/roleAssignments resource StorageAccountBlobContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().subscriptionId, hubType, 'StorageAccountBlobContributorAssignment') properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleID.storageBlobDataContributor) + roleDefinitionId: subscriptionResourceId(roleDefinitions, roleID.storageBlobDataContributor) principalId: managedIdentiyADOtoAZ.outputs.miPrincipalID description: '${miADOtoAZname} Storage Account Blob Contributor access to subscription' } @@ -230,7 +232,7 @@ module managedIdentiyGHtoADO 'modules/managedIdentity.bicep' = { resource readerAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().subscriptionId, hubType, 'reader') properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleID.reader) + roleDefinitionId: subscriptionResourceId(roleDefinitions, roleID.reader) principalId: managedIdentiyGHtoADO.outputs.miPrincipalID description: '${miGHtoADOname} Reader access to subscription' } diff --git a/infrastructure/bootstrap/modules/computeGallery.bicep b/infrastructure/bootstrap/modules/computeGallery.bicep index 3f92e798..3db6e002 100644 --- a/infrastructure/bootstrap/modules/computeGallery.bicep +++ b/infrastructure/bootstrap/modules/computeGallery.bicep @@ -10,7 +10,7 @@ resource computeGallery 'Microsoft.Compute/galleries@2023-07-03' = { name: galleryName location: location properties: { - description: '' + description: 'Compute Gallery for sharing images across subscriptions and regions' softDeletePolicy: { isSoftDeleteEnabled: false } diff --git a/infrastructure/bootstrap/modules/privateEndpoint.bicep b/infrastructure/bootstrap/modules/privateEndpoint.bicep index df58f87b..a24c8a12 100644 --- a/infrastructure/bootstrap/modules/privateEndpoint.bicep +++ b/infrastructure/bootstrap/modules/privateEndpoint.bicep @@ -17,14 +17,14 @@ var groupID = { // Retrieve the existing vnet resource group resource vnetRG 'Microsoft.Resources/resourceGroups@2024-11-01' existing = { - name: RGName scope: subscription() + name: RGName } // Retrieve the existing vnet resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = { - name: vnetName scope: vnetRG + name: vnetName } resource privateEndpointSubnet 'Microsoft.Network/virtualNetworks/subnets@2025-01-01' = { diff --git a/lung_cancer_screening/questions/forms/agree_terms_of_use_form.py b/lung_cancer_screening/questions/forms/agree_terms_of_use_form.py index e574b4ba..a765fb32 100644 --- a/lung_cancer_screening/questions/forms/agree_terms_of_use_form.py +++ b/lung_cancer_screening/questions/forms/agree_terms_of_use_form.py @@ -8,15 +8,16 @@ class TermsOfUseForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + terms_of_use = "Agree to the terms of use to continue" self.fields["value"] = MultipleChoiceField( choices=[(True, 'I agree')], widget=forms.CheckboxSelectMultiple, label="Accept terms of use", label_classes="nhsuk-u-visually-hidden", error_messages={ - "required": "Agree to the terms of use to continue", - "invalid_choice": "Agree to the terms of use to continue", - "invalid_list": "Agree to the terms of use to continue" + "required": terms_of_use, + "invalid_choice": terms_of_use, + "invalid_list": terms_of_use } ) class Meta: diff --git a/lung_cancer_screening/questions/migrations/0001_initial.py b/lung_cancer_screening/questions/migrations/0001_initial.py index 2b392bc0..b47f5b5c 100644 --- a/lung_cancer_screening/questions/migrations/0001_initial.py +++ b/lung_cancer_screening/questions/migrations/0001_initial.py @@ -11,6 +11,9 @@ class Migration(migrations.Migration): initial = True + QUESTIONS_RESPONSESET = 'questions.responseset' + PREFER_NOT_TO_SAY = 'Prefer not to say' + QUESTIONS_TOBACCOMOKINGSHISTORY = 'questions.tobaccosmokinghistory' dependencies = [ ] @@ -52,7 +55,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('P', 'Pneumonia'), ('E', 'Emphysema'), ('B', 'Bronchitis'), ('T', 'Tuberculosis (TB)'), ('C', 'Chronic obstructive pulmonary disease (COPD)'), ('N', 'No, I have not had any of these respiratory conditions')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='respiratory_conditions_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='respiratory_conditions_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -65,7 +68,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('Y', 'Yes, they were younger than 60'), ('N', 'No, they were 60 or older'), ('U', 'I do not know')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='relatives_age_when_diagnosed', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='relatives_age_when_diagnosed', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -79,7 +82,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), ('duration_years', models.IntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1, message='The number of years you stopped smoking for must be at least 1')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='periods_when_you_stopped_smoking_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='periods_when_you_stopped_smoking_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -93,7 +96,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True)), ('metric', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1397, message='Height must be between 139.7cm and 243.8 cm'), django.core.validators.MaxValueValidator(2438, message='Height must be between 139.7cm and 243.8 cm')])), ('imperial', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(55, message='Height must be between 4 feet 7 inches and 8 feet'), django.core.validators.MaxValueValidator(96, message='Height must be between 4 feet 7 inches and 8 feet')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='height_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='height_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -106,7 +109,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.IntegerField(choices=[(0, 'Yes, I currently smoke'), (1, 'Yes, I used to smoke'), (2, 'Yes, but I have smoked fewer than 100 cigarettes in my lifetime'), (3, 'No, I have never smoked')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='have_you_ever_smoked_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='have_you_ever_smoked_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -118,8 +121,8 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('value', models.CharField(choices=[('F', 'Female'), ('M', 'Male'), ('N', 'Non-binary'), ('P', 'Prefer not to say'), ('G', 'How I describe myself may not match my GP record')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='gender_response', to='questions.responseset')), + ('value', models.CharField(choices=[('F', 'Female'), ('M', 'Male'), ('N', 'Non-binary'), ('P', PREFER_NOT_TO_SAY), ('G', 'How I describe myself may not match my GP record')], max_length=1)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='gender_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -132,7 +135,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('Y', 'Yes'), ('N', 'No'), ('U', 'I do not know')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='family_history_lung_cancer', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='family_history_lung_cancer', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -144,8 +147,8 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('value', models.CharField(choices=[('A', 'Asian or Asian British'), ('B', 'Black, African, Caribbean or Black British'), ('M', 'Mixed or multiple ethnic groups'), ('W', 'White'), ('O', 'Other ethnic group'), ('N', 'Prefer not to say')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='ethnicity_response', to='questions.responseset')), + ('value', models.CharField(choices=[('A', 'Asian or Asian British'), ('B', 'Black, African, Caribbean or Black British'), ('M', 'Mixed or multiple ethnic groups'), ('W', 'White'), ('O', 'Other ethnic group'), ('N', PREFER_NOT_TO_SAY)], max_length=1)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='ethnicity_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -157,8 +160,8 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('X', 'I finished school before the age of 15'), ('G', 'GCSEs'), ('A', 'A-levels'), ('F', 'Further education'), ('B', "Bachelor's degree"), ('P', 'Postgraduate degree'), ('N', 'Prefer not to say')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='education_response', to='questions.responseset')), + ('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('X', 'I finished school before the age of 15'), ('G', 'GCSEs'), ('A', 'A-levels'), ('F', 'Further education'), ('B', "Bachelor's degree"), ('P', 'Postgraduate degree'), ('N', PREFER_NOT_TO_SAY)], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='education_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -171,7 +174,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.DateField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='date_of_birth_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='date_of_birth_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -184,7 +187,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='check_need_appointment_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='check_need_appointment_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -197,7 +200,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='cancer_diagnosis_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='cancer_diagnosis_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -210,7 +213,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='asbestos_exposure_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='asbestos_exposure_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -223,7 +226,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1, message='The age you started smoking must be between 1 and your current age')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='age_when_started_smoking_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='age_when_started_smoking_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -236,7 +239,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('F', 'Female'), ('M', 'Male'), ('I', 'Intersex')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sex_at_birth_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sex_at_birth_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, @@ -249,7 +252,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('type', models.CharField(choices=[('Cigarettes', 'Cigarettes'), ('RolledCigarettes', 'Rolled cigarettes, or roll-ups'), ('Pipe', 'Pipe'), ('Cigars', 'Cigars'), ('Cigarillos', 'Cigarillos'), ('Shisha', 'Shisha')])), - ('response_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tobacco_smoking_history', to='questions.responseset')), + ('response_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tobacco_smoking_history', to=QUESTIONS_RESPONSESET)), ], ), migrations.CreateModel( @@ -259,7 +262,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('D', 'Daily'), ('W', 'Weekly'), ('M', 'Monthly')], max_length=1)), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_frequency_response', to='questions.tobaccosmokinghistory')), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_frequency_response', to=QUESTIONS_TOBACCOMOKINGSHISTORY)), ], options={ 'abstract': False, @@ -272,7 +275,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_current_response', to='questions.tobaccosmokinghistory')), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_current_response', to=QUESTIONS_TOBACCOMOKINGSHISTORY)), ], options={ 'abstract': False, @@ -285,7 +288,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.IntegerField(validators=[django.core.validators.MinValueValidator(1, message='The number of years you smoked cigarettes must be at least 1')])), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_total_years_response', to='questions.tobaccosmokinghistory')), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_total_years_response', to='QUESTIONS_TOBACCOMOKINGSHISTORY')), ], options={ 'abstract': False, @@ -298,7 +301,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.IntegerField(validators=[django.core.validators.MinValueValidator(1)])), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_amount_response', to='questions.tobaccosmokinghistory')), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_amount_response', to='QUESTIONS_TOBACCOMOKINGSHISTORY')), ], options={ 'abstract': False, @@ -312,7 +315,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True)), ('metric', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(254, message='Weight must be between 25.4kg and 317.5kg'), django.core.validators.MaxValueValidator(3175, message='Weight must be between 25.4kg and 317.5kg')])), ('imperial', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(56, message='Weight must be between 4 stone and 50 stone'), django.core.validators.MaxValueValidator(700, message='Weight must be between 4 stone and 50 stone')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='weight_response', to='questions.responseset')), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='weight_response', to=QUESTIONS_RESPONSESET)), ], options={ 'abstract': False, From 4433e4e7a1d5fb34a5b3addc54aa800874996095 Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Tue, 5 May 2026 15:14:57 +0100 Subject: [PATCH 2/5] fixing sonarqube code smells --- .github/actions/lint-terraform/action.yaml | 5 +++-- scripts/bash/container_app_smoke_test.sh | 8 ++++---- scripts/bash/resource_group_init.sh | 2 +- scripts/bash/run_container_app_job.sh | 2 +- scripts/docker/dgoss.sh | 6 +++--- scripts/docker/docker.lib.sh | 18 +++++++++--------- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/actions/lint-terraform/action.yaml b/.github/actions/lint-terraform/action.yaml index d5dfe35d..8e0de7d0 100644 --- a/.github/actions/lint-terraform/action.yaml +++ b/.github/actions/lint-terraform/action.yaml @@ -13,8 +13,9 @@ runs: check_only=true scripts/githooks/check-terraform-format.sh - name: "Validate Terraform" shell: bash + env: + STACKS: ${{ inputs.root-modules }} run: | - stacks=${{ inputs.root-modules }} - for dir in $(find infrastructure/environments -maxdepth 1 -mindepth 1 -type d; echo ${stacks//,/$'\n'}); do + for dir in $(find infrastructure/environments -maxdepth 1 -mindepth 1 -type d; echo ${STACKS//,/$'\n'}); do dir=$dir make terraform-validate done diff --git a/scripts/bash/container_app_smoke_test.sh b/scripts/bash/container_app_smoke_test.sh index fc4d15aa..f1479338 100755 --- a/scripts/bash/container_app_smoke_test.sh +++ b/scripts/bash/container_app_smoke_test.sh @@ -8,9 +8,9 @@ DNS_ZONE_NAME=$3 PR_NUMBER=${4:-} USE_APEX_DOMAIN=${5:-false} -if [ -z "$PR_NUMBER" ]; then +if [[ -z "$PR_NUMBER" ]]; then # Permanent environments - if [ "$USE_APEX_DOMAIN" = "true" ]; then + if [[ "$USE_APEX_DOMAIN" = "true" ]]; then # For production with apex domain ENDPOINT="https://${DNS_ZONE_NAME}/sha" else @@ -33,7 +33,7 @@ while true; do # It should fail initially until front door presents the right certificate. if ACTUAL_SHA=$(curl -fsS "$ENDPOINT" 2>/dev/null); then echo "Endpoint responded: $ACTUAL_SHA" - if [ "$ACTUAL_SHA" = "$EXPECTED_SHA" ]; then + if [[ "$ACTUAL_SHA" = "$EXPECTED_SHA" ]]; then echo "✅ SHA matches expected commit: $EXPECTED_SHA" exit 0 else @@ -43,7 +43,7 @@ while true; do now=$(date +%s) elapsed=$((now - start_time)) - if [ $elapsed -ge $TIMEOUT ]; then + if [[ $elapsed -ge $TIMEOUT ]]; then echo "❌ Timeout: Endpoint did not become ready within ${TIMEOUT}s" exit 1 fi diff --git a/scripts/bash/resource_group_init.sh b/scripts/bash/resource_group_init.sh index 1c431ded..36b02fdf 100755 --- a/scripts/bash/resource_group_init.sh +++ b/scripts/bash/resource_group_init.sh @@ -15,7 +15,7 @@ userGroupName="screening_${APP_SHORT_NAME}_${ENV_CONFIG}" echo "Fetching object id for group: $userGroupName" userGroupPrincipalID=$(az ad group show --group "$userGroupName" --query id -o tsv) -if [ -z "$userGroupPrincipalID" ]; then +if [[ -z "$userGroupPrincipalID" ]]; then echo "Error: Group '$userGroupName' not found in Entra ID" exit 1 fi diff --git a/scripts/bash/run_container_app_job.sh b/scripts/bash/run_container_app_job.sh index bcf1852a..ba72bf10 100755 --- a/scripts/bash/run_container_app_job.sh +++ b/scripts/bash/run_container_app_job.sh @@ -6,7 +6,7 @@ ENV_CONFIG=$1 JOB_SHORT_NAME=$2 PR_NUMBER=${3:-} -if [ -z "$PR_NUMBER" ]; then +if [[ -z "$PR_NUMBER" ]]; then # On permanent environments, the environment name is the environment config name, i.e. "production" ENV=${ENV_CONFIG} else diff --git a/scripts/docker/dgoss.sh b/scripts/docker/dgoss.sh index e573a48b..3db6d745 100644 --- a/scripts/docker/dgoss.sh +++ b/scripts/docker/dgoss.sh @@ -24,7 +24,7 @@ error() { cleanup() { set +e { kill "$log_pid" && wait "$log_pid"; } 2> /dev/null - if [ -n "$CONTAINER_LOG_OUTPUT" ]; then + if [[ -n "$CONTAINER_LOG_OUTPUT" ]]; then cp "$tmp_dir/docker_output.log" "$CONTAINER_LOG_OUTPUT" fi rm -rf "$tmp_dir" @@ -47,7 +47,7 @@ run(){ case "$GOSS_FILES_STRATEGY" in mount) info "Starting $CONTAINER_RUNTIME container" - if [ "$CONTAINER_RUNTIME" == "podman" -a $# == 2 ]; then + if [[ "$CONTAINER_RUNTIME" == "podman" -a $# == 2 ]]; then id=$($CONTAINER_RUNTIME run -d -v "$tmp_dir:/goss:z" "${@:2}" sleep infinity) else id=$($CONTAINER_RUNTIME run -d -v "$tmp_dir:/goss:z" "${@:2}") @@ -113,7 +113,7 @@ case "$1" in fi [[ $GOSS_SLEEP ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; } info "Container health" - if [ "true" != "$($CONTAINER_RUNTIME inspect -f '{{.State.Running}}' "$id")" ]; then + if [[ "true" != "$($CONTAINER_RUNTIME inspect -f '{{.State.Running}}' "$id")" ]]; then $CONTAINER_RUNTIME logs "$id" >&2 error "the container failed to start" fi diff --git a/scripts/docker/docker.lib.sh b/scripts/docker/docker.lib.sh index d52d651c..fb64cea0 100644 --- a/scripts/docker/docker.lib.sh +++ b/scripts/docker/docker.lib.sh @@ -49,7 +49,7 @@ function docker-build() { # Tag the image with all the stated versions, see the documentation for more details for version in $(_get-all-effective-versions) latest; do - if [ ! -z "$version" ]; then + if [[ ! -z "$version" ]]; then docker tag "${tag}" "${DOCKER_IMAGE}:${version}" fi done @@ -188,7 +188,7 @@ function docker-get-image-version-and-pull() { # match it by name and version regex, if given. local versions_file="${TOOL_VERSIONS:=$(git rev-parse --show-toplevel)/.tool-versions}" local version="latest" - if [ -f "$versions_file" ]; then + if [[ -f "$versions_file" ]]; then line=$(grep "docker/${name} " "$versions_file" | sed "s/^#\s*//; s/\s*#.*$//" | grep "${match_version:-".*"}") [ -n "$line" ] && version=$(echo "$line" | awk '{print $2}') fi @@ -199,7 +199,7 @@ function docker-get-image-version-and-pull() { # Check if the image exists locally already if ! docker images | awk '{ print $1 ":" $2 }' | grep -q "^${name}:${tag}$"; then - if [ "$digest" != "latest" ]; then + if [[ "$digest" != "latest" ]]; then # Pull image by the digest sha256 and tag it docker pull \ --platform linux/amd64 \ @@ -232,7 +232,7 @@ function _create-effective-dockerfile() { # Dockerfile.effective file, otherwise docker won't use it. # See https://docs.docker.com/build/building/context/#filename-and-location # If using podman, this requires v5.0.0 or later. - if [ -f "${dir}/Dockerfile.dockerignore" ]; then + if [[ -f "${dir}/Dockerfile.dockerignore" ]]; then cp "${dir}/Dockerfile.dockerignore" "${dir}/Dockerfile.effective.dockerignore" fi cp "${dir}/Dockerfile" "${dir}/Dockerfile.effective" @@ -250,7 +250,7 @@ function _replace-image-latest-by-specific-version() { local dockerfile="${dir}/Dockerfile.effective" local build_datetime=${BUILD_DATETIME:-$(date -u +'%Y-%m-%dT%H:%M:%S%z')} - if [ -f "$versions_file" ]; then + if [[ -f "$versions_file" ]]; then # First, list the entries specific for Docker to take precedence, then the rest but exclude comments content=$(grep " docker/" "$versions_file"; grep -v " docker/" "$versions_file" ||: | grep -v "^#") echo "$content" | while IFS= read -r line; do @@ -262,7 +262,7 @@ function _replace-image-latest-by-specific-version() { done fi - if [ -f "$dockerfile" ]; then + if [[ -f "$dockerfile" ]]; then # shellcheck disable=SC2002 cat "$dockerfile" | \ sed "s/\(\${yyyy}\|\$yyyy\)/$(date --date="${build_datetime}" -u +"%Y")/g" | \ @@ -312,7 +312,7 @@ function _get-effective-tag() { local tag=$DOCKER_IMAGE version=$(_get-effective-version) - if [ ! -z "$version" ]; then + if [[ ! -z "$version" ]]; then tag="${tag}:${version}" fi echo "$tag" @@ -334,9 +334,9 @@ function _get-git-branch-name() { local branch_name=$(git rev-parse --abbrev-ref HEAD) - if [ -n "${GITHUB_HEAD_REF:-}" ]; then + if [[ -n "${GITHUB_HEAD_REF:-}" ]]; then branch_name=$GITHUB_HEAD_REF - elif [ -n "${GITHUB_REF:-}" ]; then + elif [[ -n "${GITHUB_REF:-}" ]]; then # shellcheck disable=SC2001 branch_name=$(echo "$GITHUB_REF" | sed "s#refs/heads/##") fi From 41badd222daccc0858b89faa8ef2e5befa4d706e Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Tue, 5 May 2026 15:27:09 +0100 Subject: [PATCH 3/5] further fixes --- infrastructure/bootstrap/core.bicep | 4 ---- scripts/bash/resource_group_init.sh | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/infrastructure/bootstrap/core.bicep b/infrastructure/bootstrap/core.bicep index 9412f6e8..96a6de47 100644 --- a/infrastructure/bootstrap/core.bicep +++ b/infrastructure/bootstrap/core.bicep @@ -6,10 +6,6 @@ param miPrincipalId string @minLength(1) param miName string -param userGroupPrincipalID string - -param userGroupName string - // See: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles var roleID = { contributor: 'b24988ac-6180-42a0-ab88-20f7382dd24c' diff --git a/scripts/bash/resource_group_init.sh b/scripts/bash/resource_group_init.sh index 36b02fdf..7379f43a 100755 --- a/scripts/bash/resource_group_init.sh +++ b/scripts/bash/resource_group_init.sh @@ -47,5 +47,4 @@ miPrincipalID=$(echo "$output" | jq -r '.properties.outputs.miPrincipalID.value' echo "Deploy to core subscription $ARM_SUBSCRIPTION_ID..." az deployment sub create --location "$REGION" --template-file infrastructure/bootstrap/core.bicep \ --subscription "$ARM_SUBSCRIPTION_ID" \ - --parameters miName="$miName" miPrincipalId="$miPrincipalID" \ - userGroupPrincipalID="$userGroupPrincipalID" userGroupName="$userGroupName" --confirm-with-what-if + --parameters miName="$miName" miPrincipalId="$miPrincipalID" --confirm-with-what-if From 9b230403120343066ed187edd4eadf058e83f576 Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Tue, 5 May 2026 16:09:20 +0100 Subject: [PATCH 4/5] revert --- .../questions/migrations/0001_initial.py | 51 +++++++++---------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/lung_cancer_screening/questions/migrations/0001_initial.py b/lung_cancer_screening/questions/migrations/0001_initial.py index b47f5b5c..2b392bc0 100644 --- a/lung_cancer_screening/questions/migrations/0001_initial.py +++ b/lung_cancer_screening/questions/migrations/0001_initial.py @@ -11,9 +11,6 @@ class Migration(migrations.Migration): initial = True - QUESTIONS_RESPONSESET = 'questions.responseset' - PREFER_NOT_TO_SAY = 'Prefer not to say' - QUESTIONS_TOBACCOMOKINGSHISTORY = 'questions.tobaccosmokinghistory' dependencies = [ ] @@ -55,7 +52,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('P', 'Pneumonia'), ('E', 'Emphysema'), ('B', 'Bronchitis'), ('T', 'Tuberculosis (TB)'), ('C', 'Chronic obstructive pulmonary disease (COPD)'), ('N', 'No, I have not had any of these respiratory conditions')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='respiratory_conditions_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='respiratory_conditions_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -68,7 +65,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('Y', 'Yes, they were younger than 60'), ('N', 'No, they were 60 or older'), ('U', 'I do not know')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='relatives_age_when_diagnosed', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='relatives_age_when_diagnosed', to='questions.responseset')), ], options={ 'abstract': False, @@ -82,7 +79,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), ('duration_years', models.IntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1, message='The number of years you stopped smoking for must be at least 1')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='periods_when_you_stopped_smoking_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='periods_when_you_stopped_smoking_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -96,7 +93,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True)), ('metric', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1397, message='Height must be between 139.7cm and 243.8 cm'), django.core.validators.MaxValueValidator(2438, message='Height must be between 139.7cm and 243.8 cm')])), ('imperial', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(55, message='Height must be between 4 feet 7 inches and 8 feet'), django.core.validators.MaxValueValidator(96, message='Height must be between 4 feet 7 inches and 8 feet')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='height_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='height_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -109,7 +106,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.IntegerField(choices=[(0, 'Yes, I currently smoke'), (1, 'Yes, I used to smoke'), (2, 'Yes, but I have smoked fewer than 100 cigarettes in my lifetime'), (3, 'No, I have never smoked')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='have_you_ever_smoked_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='have_you_ever_smoked_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -121,8 +118,8 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('value', models.CharField(choices=[('F', 'Female'), ('M', 'Male'), ('N', 'Non-binary'), ('P', PREFER_NOT_TO_SAY), ('G', 'How I describe myself may not match my GP record')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='gender_response', to=QUESTIONS_RESPONSESET)), + ('value', models.CharField(choices=[('F', 'Female'), ('M', 'Male'), ('N', 'Non-binary'), ('P', 'Prefer not to say'), ('G', 'How I describe myself may not match my GP record')], max_length=1)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='gender_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -135,7 +132,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('Y', 'Yes'), ('N', 'No'), ('U', 'I do not know')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='family_history_lung_cancer', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='family_history_lung_cancer', to='questions.responseset')), ], options={ 'abstract': False, @@ -147,8 +144,8 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('value', models.CharField(choices=[('A', 'Asian or Asian British'), ('B', 'Black, African, Caribbean or Black British'), ('M', 'Mixed or multiple ethnic groups'), ('W', 'White'), ('O', 'Other ethnic group'), ('N', PREFER_NOT_TO_SAY)], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='ethnicity_response', to=QUESTIONS_RESPONSESET)), + ('value', models.CharField(choices=[('A', 'Asian or Asian British'), ('B', 'Black, African, Caribbean or Black British'), ('M', 'Mixed or multiple ethnic groups'), ('W', 'White'), ('O', 'Other ethnic group'), ('N', 'Prefer not to say')], max_length=1)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='ethnicity_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -160,8 +157,8 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('X', 'I finished school before the age of 15'), ('G', 'GCSEs'), ('A', 'A-levels'), ('F', 'Further education'), ('B', "Bachelor's degree"), ('P', 'Postgraduate degree'), ('N', PREFER_NOT_TO_SAY)], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='education_response', to=QUESTIONS_RESPONSESET)), + ('value', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('X', 'I finished school before the age of 15'), ('G', 'GCSEs'), ('A', 'A-levels'), ('F', 'Further education'), ('B', "Bachelor's degree"), ('P', 'Postgraduate degree'), ('N', 'Prefer not to say')], max_length=1), size=None, validators=[lung_cancer_screening.questions.models.validators.singleton_option.validate_singleton_option])), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='education_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -174,7 +171,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.DateField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='date_of_birth_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='date_of_birth_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -187,7 +184,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='check_need_appointment_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='check_need_appointment_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -200,7 +197,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='cancer_diagnosis_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='cancer_diagnosis_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -213,7 +210,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='asbestos_exposure_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='asbestos_exposure_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -226,7 +223,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1, message='The age you started smoking must be between 1 and your current age')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='age_when_started_smoking_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='age_when_started_smoking_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -239,7 +236,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('F', 'Female'), ('M', 'Male'), ('I', 'Intersex')], max_length=1)), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sex_at_birth_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sex_at_birth_response', to='questions.responseset')), ], options={ 'abstract': False, @@ -252,7 +249,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('type', models.CharField(choices=[('Cigarettes', 'Cigarettes'), ('RolledCigarettes', 'Rolled cigarettes, or roll-ups'), ('Pipe', 'Pipe'), ('Cigars', 'Cigars'), ('Cigarillos', 'Cigarillos'), ('Shisha', 'Shisha')])), - ('response_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tobacco_smoking_history', to=QUESTIONS_RESPONSESET)), + ('response_set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tobacco_smoking_history', to='questions.responseset')), ], ), migrations.CreateModel( @@ -262,7 +259,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.CharField(choices=[('D', 'Daily'), ('W', 'Weekly'), ('M', 'Monthly')], max_length=1)), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_frequency_response', to=QUESTIONS_TOBACCOMOKINGSHISTORY)), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_frequency_response', to='questions.tobaccosmokinghistory')), ], options={ 'abstract': False, @@ -275,7 +272,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.BooleanField()), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_current_response', to=QUESTIONS_TOBACCOMOKINGSHISTORY)), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoking_current_response', to='questions.tobaccosmokinghistory')), ], options={ 'abstract': False, @@ -288,7 +285,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.IntegerField(validators=[django.core.validators.MinValueValidator(1, message='The number of years you smoked cigarettes must be at least 1')])), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_total_years_response', to='QUESTIONS_TOBACCOMOKINGSHISTORY')), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_total_years_response', to='questions.tobaccosmokinghistory')), ], options={ 'abstract': False, @@ -301,7 +298,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('value', models.IntegerField(validators=[django.core.validators.MinValueValidator(1)])), - ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_amount_response', to='QUESTIONS_TOBACCOMOKINGSHISTORY')), + ('tobacco_smoking_history', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='smoked_amount_response', to='questions.tobaccosmokinghistory')), ], options={ 'abstract': False, @@ -315,7 +312,7 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(auto_now=True)), ('metric', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(254, message='Weight must be between 25.4kg and 317.5kg'), django.core.validators.MaxValueValidator(3175, message='Weight must be between 25.4kg and 317.5kg')])), ('imperial', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(56, message='Weight must be between 4 stone and 50 stone'), django.core.validators.MaxValueValidator(700, message='Weight must be between 4 stone and 50 stone')])), - ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='weight_response', to=QUESTIONS_RESPONSESET)), + ('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='weight_response', to='questions.responseset')), ], options={ 'abstract': False, From 86c0b85d6b0cc439185b58b401b7993ecc0e074a Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Tue, 5 May 2026 16:39:11 +0100 Subject: [PATCH 5/5] reordering variables --- infrastructure/bootstrap/modules/dns.bicep | 2 +- .../bootstrap/modules/privateEndpoint-spoke.bicep | 4 ++-- infrastructure/bootstrap/modules/storage.bicep | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/infrastructure/bootstrap/modules/dns.bicep b/infrastructure/bootstrap/modules/dns.bicep index 3a602711..2900fa99 100644 --- a/infrastructure/bootstrap/modules/dns.bicep +++ b/infrastructure/bootstrap/modules/dns.bicep @@ -19,9 +19,9 @@ resource privateDNSZone 'Microsoft.Network/privateDnsZones@2024-06-01' = { } resource vnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = { + location: 'global' name: '${last(split(vnetId, '/'))}-link' parent: privateDNSZone - location: 'global' properties: { virtualNetwork: { id: vnetId diff --git a/infrastructure/bootstrap/modules/privateEndpoint-spoke.bicep b/infrastructure/bootstrap/modules/privateEndpoint-spoke.bicep index 877ce0c8..827187e0 100644 --- a/infrastructure/bootstrap/modules/privateEndpoint-spoke.bicep +++ b/infrastructure/bootstrap/modules/privateEndpoint-spoke.bicep @@ -16,14 +16,14 @@ var groupID = { // Retrieve the existing vnet resource group resource vnetRG 'Microsoft.Resources/resourceGroups@2024-11-01' existing = { - name: RGName scope: subscription() + name: RGName } // Retrieve the existing vnet resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = { - name: vnetName scope: vnetRG + name: vnetName } // Retrieve the existing Subnet within the vnet diff --git a/infrastructure/bootstrap/modules/storage.bicep b/infrastructure/bootstrap/modules/storage.bicep index 32701636..ea7380a4 100644 --- a/infrastructure/bootstrap/modules/storage.bicep +++ b/infrastructure/bootstrap/modules/storage.bicep @@ -61,11 +61,6 @@ resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/container } } -// See: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles -var roleID = { - blobContributor: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' -} - // Define role assignments array var roleAssignments = [ { @@ -75,6 +70,11 @@ var roleAssignments = [ } ] +// See: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles +var roleID = { + blobContributor: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' +} + // Let the managed identity edit the terraform state resource blobContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().subscriptionId, miPrincipalID, 'blobContributor')