@@ -569,7 +569,7 @@ export default {
this.conservation_status_obj.processing_status == "With Assessor";
},
listing_and_review_due_date_disabled: function () {
- return this.isReadOnly || this.conservation_status_obj.processing_status != "With Assessor"
+ return this.isReadOnly || !["With Assessor", "Unlocked"].includes(this.conservation_status_obj.processing_status)
},
approval_level_disabled: function () {
return this.isReadOnly || !['With Assessor', 'With Referral'].includes(this.conservation_status_obj.processing_status);
From 1c94c1ab987363dee2f1bb2db21881c87487acdb Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Tue, 17 Sep 2024 09:24:32 +0800
Subject: [PATCH 028/185] Modify has_assessor_mode to allow assessor to propose
to delist the CS without having to assign to themselves.
---
boranga/components/conservation_status/models.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/boranga/components/conservation_status/models.py b/boranga/components/conservation_status/models.py
index 3bd2abbf..ff33d767 100644
--- a/boranga/components/conservation_status/models.py
+++ b/boranga/components/conservation_status/models.py
@@ -965,7 +965,6 @@ def can_user_assign_to_self(self, request):
def has_assessor_mode(self, request):
status_without_assessor = [
- ConservationStatus.PROCESSING_STATUS_APPROVED,
ConservationStatus.PROCESSING_STATUS_WITH_APPROVER,
ConservationStatus.PROCESSING_STATUS_CLOSED,
ConservationStatus.PROCESSING_STATUS_DECLINED,
@@ -975,6 +974,12 @@ def has_assessor_mode(self, request):
if self.processing_status in status_without_assessor:
return False
+ if self.processing_status == ConservationStatus.PROCESSING_STATUS_APPROVED:
+ # Edge case that allows assessors to propose to delist without being assigned
+ # to the conservation status. This is due to the fact we only show either
+ # the assigned to dropdown for the approver or assessor and not both.
+ return is_conservation_status_assessor(request)
+
elif self.processing_status == ConservationStatus.PROCESSING_STATUS_UNLOCKED:
return is_conservation_status_approver(request)
else:
From 1d80e00c43a0ca3ee6b39268c4945f449979cf2a Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Tue, 17 Sep 2024 09:27:01 +0800
Subject: [PATCH 029/185] Modify component to integrate back to assessor modal
action for approver. Add confirmation prompt when proposing CS ready for
agenda. Correct code that was not showing appropriate actions to users when
CS is ready for agenda.
---
.../conservation_status.vue | 79 ++++++++++---------
1 file changed, 40 insertions(+), 39 deletions(-)
diff --git a/boranga/frontend/boranga/src/components/internal/conservation_status/conservation_status.vue b/boranga/frontend/boranga/src/components/internal/conservation_status/conservation_status.vue
index 316857b0..4a1a9e78 100644
--- a/boranga/frontend/boranga/src/components/internal/conservation_status/conservation_status.vue
+++ b/boranga/frontend/boranga/src/components/internal/conservation_status/conservation_status.vue
@@ -27,7 +27,8 @@
-
-
@@ -272,34 +272,12 @@
-
-
-
- Decline
-
-
-
-
- Approve
-
-
-
-
-
-
-
-
-
+ v-if="conservation_status_obj.processing_status == 'Ready For Agenda' && conservation_status_obj.approval_level == 'minister'">
Back To
+ @click.prevent="backToAssessor">Back To
Assessor
@@ -422,6 +400,8 @@
+
@@ -441,6 +421,7 @@ import datatable from '@vue-utils/datatable.vue'
import CommsLogs from '@common-utils/comms_logs.vue'
import Submission from '@common-utils/submission.vue'
import AmendmentRequest from './amendment_request.vue'
+import BackToAssessor from './back_to_assessor.vue'
import ProposedDecline from './proposal_proposed_decline'
import ProposeDelist from './proposal_propose_delist'
import ProposedApproval from './proposed_issuance.vue'
@@ -490,6 +471,7 @@ export default {
Submission,
ProposalConservationStatus,
AmendmentRequest,
+ BackToAssessor,
CSMoreReferrals,
ProposedDecline,
ProposeDelist,
@@ -724,18 +706,34 @@ export default {
},
proposedReadyForAgenda: function () {
let vm = this;
- if (!this.validateConservationStatus()) {
- return;
- }
- vm.proposeReadyForAgenda = true;
- vm.$http.post(helpers.add_endpoint_json(api_endpoints.conservation_status, vm.conservation_status_obj.id + '/proposed_ready_for_agenda')).then((response) => {
- vm.proposeReadyForAgenda = false;
- vm.$router.push({ path: '/internal/conservation-status/' }); //Navigate to dashboard page after Propose issue.
- }, (error) => {
- vm.errors = true;
- vm.proposeReadyForAgenda = false;
- vm.errorString = helpers.apiVueResourceError(error);
+ swal.fire({
+ title: `Propose Conservation Status ${this.conservation_status_obj.conservation_status_number} Ready For Agenda`,
+ text: "Are you sure you want to propose this conservation status ready for agenda?",
+ icon: "question",
+ showCancelButton: true,
+ confirmButtonText: 'Propose Ready For Agenda',
+ reverseButtons: true,
+ customClass: {
+ confirmButton: 'btn btn-primary',
+ cancelButton: 'btn btn-secondary'
+ }
+ }).then((result) => {
+ if (result.isConfirmed) {
+ if (!this.validateConservationStatus()) {
+ return;
+ }
+ vm.proposeReadyForAgenda = true;
+ vm.$http.post(helpers.add_endpoint_json(api_endpoints.conservation_status, vm.conservation_status_obj.id + '/proposed_ready_for_agenda')).then((response) => {
+ vm.proposeReadyForAgenda = false;
+ vm.$router.push({ path: '/internal/conservation-status/' }); //Navigate to dashboard page after Propose issue.
+ }, (error) => {
+ vm.errors = true;
+ vm.proposeReadyForAgenda = false;
+ vm.errorString = helpers.apiVueResourceError(error);
+ });
+ }
});
+
},
proposeDelist: function () {
this.$refs.propose_delist.isModalOpen = true;
@@ -1035,8 +1033,8 @@ export default {
vm.original_conservation_status_obj = helpers.copyObject(response.body);
vm.conservation_status_obj = helpers.copyObject(response.body);
vm.$nextTick(() => {
- vm.initialiseAssignedOfficerSelect(true);
- vm.updateAssignedOfficerSelect();
+ vm.initialisedSelects = false;
+ vm.initialiseSelects();
});
},
assignTo: function () {
@@ -1426,6 +1424,9 @@ export default {
});
});
},
+ backToAssessor: function () {
+ this.$refs.back_to_assessor_modal.isModalOpen = true;
+ },
switchStatus: function (status) {
let vm = this;
if (vm.conservation_status_obj.processing_status == 'With Approver' && status == 'with_assessor') {
From 1ca9a0daf004b8873c2dd3ef8bd4afc81e982505 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Tue, 17 Sep 2024 09:44:24 +0800
Subject: [PATCH 030/185] Don't allow users to refer to themselves. Don't allow
users to refer to the submitter of the related object (CS/OCR).
---
.../components/conservation_status/models.py | 36 +++++++++++++------
boranga/components/occurrence/models.py | 8 +++++
2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/boranga/components/conservation_status/models.py b/boranga/components/conservation_status/models.py
index ff33d767..eb40a4c6 100644
--- a/boranga/components/conservation_status/models.py
+++ b/boranga/components/conservation_status/models.py
@@ -1142,39 +1142,47 @@ def send_referral(self, request, referral_email, referral_text):
]:
raise exceptions.ConservationStatusReferralCannotBeSent()
- self.processing_status = ConservationStatus.PROCESSING_STATUS_WITH_REFERRAL
- self.save()
- referral = None
-
# Check if the user is in ledger
try:
- user = EmailUser.objects.get(email__icontains=referral_email)
+ referee = EmailUser.objects.get(email__icontains=referral_email)
except EmailUser.DoesNotExist:
raise ValidationError(
f"There is no user with email {referral_email} in the ledger system. "
"Please check the email and try again."
)
+ # Don't allow users to refer to themselves
+ if request.user.id == referee.id:
+ raise ValidationError("You cannot refer to yourself")
+
+ # Don't allow users to refer to the submitter
+ if request.user.id == self.submitter:
+ raise ValidationError("You cannot refer to the submitter")
+
+ referral = None
try:
ConservationStatusReferral.objects.get(
- referral=user.id, conservation_status=self
+ referral=referee.id, conservation_status=self
)
raise ValidationError("A referral has already been sent to this user")
except ConservationStatusReferral.DoesNotExist:
referral = ConservationStatusReferral.objects.create(
conservation_status=self,
- referral=user.id,
+ referral=referee.id,
sent_by=request.user.id,
text=referral_text,
assigned_officer=request.user.id,
)
+ self.processing_status = ConservationStatus.PROCESSING_STATUS_WITH_REFERRAL
+ self.save()
+
# Create a log entry for the proposal
self.log_user_action(
ConservationStatusUserAction.ACTION_SEND_REFERRAL_TO.format(
referral.id,
self.conservation_status_number,
- f"{user.get_full_name()}({user.email})",
+ f"{referee.get_full_name()}({referee.email})",
),
request,
)
@@ -1184,7 +1192,7 @@ def send_referral(self, request, referral_email, referral_text):
ConservationStatusUserAction.ACTION_SEND_REFERRAL_TO.format(
referral.id,
self.conservation_status_number,
- f"{user.get_full_name()}({user.email})",
+ f"{referee.get_full_name()}({referee.email})",
),
request,
)
@@ -2346,8 +2354,14 @@ def send_referral(self, request, referral_email, referral_text):
):
raise exceptions.ConservationStatusReferralCannotBeSent()
- if request.user.id != self.referral:
- raise exceptions.ReferralNotAuthorized()
+ # Don't allow users to refer to themselves
+ if request.user.id == self.referral:
+ raise ValidationError("You cannot refer to yourself")
+
+ # Don't allow users to refer to the submitter
+ if request.user.id == self.conservation_status.submitter:
+ raise ValidationError("You cannot refer to the submitter")
+
if self.sent_from != 1:
raise exceptions.ReferralCanNotSend()
self.conservation_status.processing_status = (
diff --git a/boranga/components/occurrence/models.py b/boranga/components/occurrence/models.py
index 4548e064..2fd26590 100644
--- a/boranga/components/occurrence/models.py
+++ b/boranga/components/occurrence/models.py
@@ -1093,6 +1093,14 @@ def send_referral(self, request, referral_email, referral_text):
"The user you want to send the referral to does not exist in the ledger database"
)
+ # Don't allow the user to refer to themselves
+ if referee.id == request.user.id:
+ raise ValidationError("You cannot refer to yourself")
+
+ # Don't allow the user to refer to the submitter
+ if referee.id == self.submitter:
+ raise ValidationError("You cannot refer to the submitter")
+
# Check if the referral has already been sent to this user
if OccurrenceReportReferral.objects.filter(
referral=referee.id, occurrence_report=self
From 384ae3a035aed10005c6aaba1c4135fe353d6e61 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Tue, 17 Sep 2024 09:50:47 +0800
Subject: [PATCH 031/185] Tweak error message when attempts to refer to
oneself.
---
boranga/components/conservation_status/serializers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/boranga/components/conservation_status/serializers.py b/boranga/components/conservation_status/serializers.py
index 1fb55e94..5ad221c7 100644
--- a/boranga/components/conservation_status/serializers.py
+++ b/boranga/components/conservation_status/serializers.py
@@ -1331,7 +1331,7 @@ def validate(self, data):
request = self.context.get("request")
if request.user.email == data["email"]:
- non_field_errors.append("You cannot send referral to yourself.")
+ non_field_errors.append("You cannot refer to yourself.")
elif not data["email"]:
non_field_errors.append("Referral not found.")
From 8c9af586877b1ac1fdebbe92e13f0cfd4e1b8bd1 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Tue, 17 Sep 2024 09:51:35 +0800
Subject: [PATCH 032/185] Bug fixes in send_referral on ConservationStatus
model.
---
boranga/components/conservation_status/models.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/boranga/components/conservation_status/models.py b/boranga/components/conservation_status/models.py
index eb40a4c6..143348bb 100644
--- a/boranga/components/conservation_status/models.py
+++ b/boranga/components/conservation_status/models.py
@@ -1144,7 +1144,7 @@ def send_referral(self, request, referral_email, referral_text):
# Check if the user is in ledger
try:
- referee = EmailUser.objects.get(email__icontains=referral_email)
+ referee = EmailUser.objects.get(email__iexact=referral_email.strip())
except EmailUser.DoesNotExist:
raise ValidationError(
f"There is no user with email {referral_email} in the ledger system. "
@@ -1152,11 +1152,11 @@ def send_referral(self, request, referral_email, referral_text):
)
# Don't allow users to refer to themselves
- if request.user.id == referee.id:
+ if referee.id == request.user.id:
raise ValidationError("You cannot refer to yourself")
# Don't allow users to refer to the submitter
- if request.user.id == self.submitter:
+ if referee.id == self.submitter:
raise ValidationError("You cannot refer to the submitter")
referral = None
From 52708a7ee758e9946f857c536a82c4fb1bfb5c34 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Tue, 17 Sep 2024 10:59:56 +0800
Subject: [PATCH 033/185] Remove unused component.
---
.../src/components/common/blank_template.vue | 93 -------------------
1 file changed, 93 deletions(-)
delete mode 100755 boranga/frontend/boranga/src/components/common/blank_template.vue
diff --git a/boranga/frontend/boranga/src/components/common/blank_template.vue b/boranga/frontend/boranga/src/components/common/blank_template.vue
deleted file mode 100755
index 0190575b..00000000
--- a/boranga/frontend/boranga/src/components/common/blank_template.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
Step 3:
+ Select the .zip file containing any associated documents (Optional)
+
+
+
+
+
+ Step 4:
Select the bulk import file (.xlsx)
@@ -347,6 +357,10 @@ export default {
const file = event.target.files[0];
const formData = new FormData();
formData.append('_file', file);
+ const associated_files = this.$refs['bulk-import-associated-files-zip'].files;
+ if(associated_files.length > 0) {
+ formData.append('_associated_files_zip', associated_files[0]);
+ }
formData.append('schema_id', this.selected_schema_version.id);
this.$http.post(api_endpoints.occurrence_report_bulk_imports, formData).then((response) => {
@@ -355,6 +369,7 @@ export default {
this.importFileErrors = null;
this.form.classList.remove('was-validated');
this.$refs['bulk-import-file'].value = '';
+ this.$refs['bulk-import-associated-files-zip'].value = '';
swal.fire({
title: 'Bulk Import Added to Queue',
text: 'The bulk import of occurrence reports has been added to the queue for processing',
@@ -368,12 +383,14 @@ export default {
} else {
this.importFileErrors = response.body;
event.target.value = '';
+ this.$refs['bulk-import-associated-files-zip'].value = '';
this.$refs['bulk-import-file'].setCustomValidity('Invalid field');
this.form.classList.add('was-validated');
}
}, (error) => {
this.importFileErrors = error.body;
event.target.value = '';
+ this.$refs['bulk-import-associated-files-zip'].value = '';
this.$refs['bulk-import-file'].setCustomValidity('Invalid field');
this.form.classList.add('was-validated');
console.log(error.body);
From e5269c9d8821abffd5a8d1358db9ad9b858a3081 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 10:01:49 +0800
Subject: [PATCH 169/185] Move check file functionality to it's own function
(so it can be called independently of a model.
---
boranga/components/main/models.py | 23 ++--------------
boranga/helpers.py | 45 ++++++++++++++++++++++++-------
2 files changed, 37 insertions(+), 31 deletions(-)
diff --git a/boranga/components/main/models.py b/boranga/components/main/models.py
index 2c89c1de..6eedf3d7 100644
--- a/boranga/components/main/models.py
+++ b/boranga/components/main/models.py
@@ -4,12 +4,11 @@
from django.apps import apps
from django.conf import settings
from django.core.cache import cache
-from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.db import models
from reversion.models import Version
-from boranga.helpers import compressed_content_valid, file_extension_valid
+from boranga.helpers import check_file
private_storage = FileSystemStorage(
location=settings.BASE_DIR + "/private-media/", base_url="/private-media/"
@@ -183,25 +182,7 @@ def __str__(self):
return self.name or self.filename
def check_file(self, file):
- # check if extension in whitelist
- cache_key = settings.CACHE_KEY_FILE_EXTENSION_WHITELIST
- whitelist = cache.get(cache_key)
- if whitelist is None:
- whitelist = FileExtensionWhitelist.objects.all()
- cache.set(cache_key, whitelist, settings.CACHE_TIMEOUT_2_HOURS)
-
- valid, compression = file_extension_valid(
- str(file), whitelist, self._meta.model_name
- )
-
- if not valid:
- raise ValidationError("File type/extension not supported")
-
- if compression:
- # supported compression check
- valid = compressed_content_valid(file, whitelist, self._meta.model_name)
- if not valid:
- raise ValidationError("Unsupported type/extension in compressed file")
+ return check_file(file, self._meta.model_name)
# @python_2_unicode_compatible
diff --git a/boranga/helpers.py b/boranga/helpers.py
index aa8e26eb..9af22726 100755
--- a/boranga/helpers.py
+++ b/boranga/helpers.py
@@ -29,6 +29,28 @@
logger = logging.getLogger(__name__)
+def check_file(file, model_name):
+ from boranga.components.main.models import FileExtensionWhitelist
+
+ # check if extension in whitelist
+ cache_key = settings.CACHE_KEY_FILE_EXTENSION_WHITELIST
+ whitelist = cache.get(cache_key)
+ if whitelist is None:
+ whitelist = FileExtensionWhitelist.objects.all()
+ cache.set(cache_key, whitelist, settings.CACHE_TIMEOUT_2_HOURS)
+
+ valid, compression = file_extension_valid(str(file), whitelist, model_name)
+
+ if not valid:
+ raise ValidationError("File type/extension not supported")
+
+ if compression:
+ # supported compression check
+ valid = compressed_content_valid(file, whitelist, model_name)
+ if not valid:
+ raise ValidationError("Unsupported type/extension in compressed file")
+
+
def file_extension_valid(file, whitelist, model):
logger.info("Uploaded File: " + file + " For Model: " + model)
@@ -100,16 +122,19 @@ def zip_content_valid(file, whitelist, model):
for i in zipFile.filelist:
valid, compression = file_extension_valid(i.filename, whitelist, model)
if compression:
- logger.warning(
- "Uploaded File: "
- + str(file)
- + " For Model: "
- + model
- + " to be Rejected"
- )
- raise ValidationError(
- "Compressed files not supported within compressed files"
- )
+ if not i.filename.endswith(".zip"):
+ logger.warning(
+ "Uploaded File: "
+ + str(file)
+ + " For Model: "
+ + model
+ + " to be Rejected"
+ )
+ raise ValidationError(
+ "The only compressed format allowed in a .zip file is .zip"
+ )
+ valid = zip_content_valid(zipFile.open(i.filename), whitelist, model)
+
if not valid:
return False
From 20c361614be7917d6ccb872c6049e11d149b001a Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 10:02:41 +0800
Subject: [PATCH 170/185] Add file security validation for the bulk import file
and the associated files zip.
---
boranga/components/occurrence/serializers.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/boranga/components/occurrence/serializers.py b/boranga/components/occurrence/serializers.py
index 1660c339..16f682c8 100644
--- a/boranga/components/occurrence/serializers.py
+++ b/boranga/components/occurrence/serializers.py
@@ -74,6 +74,7 @@
)
from boranga.components.users.serializers import SubmitterInformationSerializer
from boranga.helpers import (
+ check_file,
is_conservation_status_approver,
is_conservation_status_assessor,
is_contributor,
@@ -4063,6 +4064,14 @@ class Meta:
def validate(self, attrs):
_file = attrs["_file"]
+ check_file(_file, OccurrenceReportBulkImportTask._meta.model_name)
+
+ _associated_files_zip = attrs.get("_associated_files_zip", None)
+ if _associated_files_zip:
+ check_file(
+ _associated_files_zip, OccurrenceReportBulkImportTask._meta.model_name
+ )
+
try:
schema = OccurrenceReportBulkImportSchema.objects.get(id=attrs["schema_id"])
except OccurrenceReportBulkImportSchema.DoesNotExist:
From a3f2e713026de2bbc37224ab66fc4cee93e54a2c Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 10:03:47 +0800
Subject: [PATCH 171/185] Update validation messages for ocr bulk import file
and associated files.
---
boranga/components/occurrence/models.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/boranga/components/occurrence/models.py b/boranga/components/occurrence/models.py
index fb519f3c..68b3ef64 100644
--- a/boranga/components/occurrence/models.py
+++ b/boranga/components/occurrence/models.py
@@ -5278,18 +5278,14 @@ def validate_bulk_import_file_extension(value):
ext = os.path.splitext(value.name)[1]
valid_extensions = [".xlsx"]
if ext not in valid_extensions:
- raise ValidationError(
- "Only .xlsx files are supported by the bulk import facility!"
- )
+ raise ValidationError("The bulk import file must be a .xlsx file")
def validate_bulk_import_associated_files_extension(value):
ext = os.path.splitext(value.name)[1]
valid_extensions = [".zip"]
if ext not in valid_extensions:
- raise ValidationError(
- "Only .zip files are supported by the bulk import facility!"
- )
+ raise ValidationError("The associated documents file must be a .zip file")
# TODO: Would be nice to have the object id in the file path
From f8b9a3ec79d4d2211359ab88d555e5432d00e70b Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 11:07:26 +0800
Subject: [PATCH 172/185] Fix regression that was showing id instead of display
field for those models that don't have a display field name from the list in
settings (in that case the function should return the first CharField before
falling back to the id field).
---
boranga/helpers.py | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/boranga/helpers.py b/boranga/helpers.py
index 9af22726..e65e212f 100755
--- a/boranga/helpers.py
+++ b/boranga/helpers.py
@@ -536,25 +536,18 @@ def get_display_field_for_model(model: models.Model) -> str:
Returns the field name to display for a model in the admin list display.
"""
# Find the best field to use for a display value
- display_field = None
field_names = [field.name for field in model._meta.get_fields()]
+ logger.debug(f"Field names for {model}: {field_names}")
for field_name in settings.OCR_BULK_IMPORT_LOOKUP_TABLE_DISPLAY_FIELDS:
if field_name in field_names:
- display_field = field_name
- break
+ return field_name
- if not display_field:
- # If we can't find a display field, we'll just use the first CharField we find
- for field in field_names:
- if isinstance(field, models.fields.CharField):
- display_field = field.name
- break
+ # If we can't find a display field, we'll just use the first CharField we find
+ for field_name in field_names:
+ if isinstance(model._meta.get_field(field_name), models.fields.CharField):
+ return field_name
- if not display_field:
- # Fall back to the id
- display_field = "id"
-
- return display_field
+ return "id"
def get_choices_for_field(
From b942608023d066e8daf52f20c7ee489ba7afbd17 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 11:10:52 +0800
Subject: [PATCH 173/185] Remove debug log.
---
boranga/helpers.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/boranga/helpers.py b/boranga/helpers.py
index e65e212f..3b71e338 100755
--- a/boranga/helpers.py
+++ b/boranga/helpers.py
@@ -537,7 +537,6 @@ def get_display_field_for_model(model: models.Model) -> str:
"""
# Find the best field to use for a display value
field_names = [field.name for field in model._meta.get_fields()]
- logger.debug(f"Field names for {model}: {field_names}")
for field_name in settings.OCR_BULK_IMPORT_LOOKUP_TABLE_DISPLAY_FIELDS:
if field_name in field_names:
return field_name
From 71f1d091ae6eafd7f3ae9c2a0af59112f9808b5d Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 11:41:36 +0800
Subject: [PATCH 174/185] When adding a single column. Add the column in order
based on the model type. When columns are saved or removed apply new ordering
then save to the backend.
---
.../occurrence/bulk_import_schema.vue | 23 +++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue b/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue
index 689690bf..cb3186ea 100644
--- a/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue
+++ b/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue
@@ -118,7 +118,7 @@
role="button" :key="column.id">
{{ index +
- 1 }}
+ 1 }} {{ column.order }}
@@ -447,7 +447,7 @@
Unselect Column
-
Save
Column !this.schema.columns.some(column => column.django_import_field_name == modelField.name &&
column.django_import_content_type == modelField.content_type)
)
+ // If there are already other columns with the same django content type
+ // then move the selected column to the end of the list of those columns
+ if (this.schema.columns.some(column => column.django_import_content_type == this.selectedColumn.django_import_content_type)) {
+ let lastColumnIndex = this.schema.columns.filter(column => column.django_import_content_type == this.selectedColumn.django_import_content_type).length - 1
+ this.schema.columns.splice(this.selectedColumnIndex, 1)
+ this.schema.columns.splice(lastColumnIndex, 0, this.selectedColumn)
+ this.selectedColumnIndex = lastColumnIndex
+ // Update the order of the columns to reflect the new order
+ this.applyOrderToColumns()
+ }
this.$nextTick(() => {
this.enablePopovers();
+ this.selectedColumn.model_name = this.selectedContentType.model_verbose_name
})
},
selectDjangoImportField() {
@@ -801,7 +812,11 @@ export default {
}
}
})
-
+ },
+ applyOrderToColumns() {
+ this.schema.columns.forEach((column, index) => {
+ column.order = index
+ })
},
selectColumn(column) {
if (column.django_import_content_type) {
@@ -857,7 +872,7 @@ export default {
this.schema.columns = this.schema.columns.filter(column => column !== this.selectedColumn)
this.selectedColumn = null
this.addEditMode = false
-
+ this.applyOrderToColumns()
if (column.id) {
this.save()
}
From a2e68b7d8eebb08c6d765060da9c766f24718d8c Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 15:11:30 +0800
Subject: [PATCH 175/185] Bug fix: In cases where the queryset it auto filtered
by group_type using only does not work.
---
boranga/components/occurrence/models.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/boranga/components/occurrence/models.py b/boranga/components/occurrence/models.py
index 68b3ef64..3245fc4c 100644
--- a/boranga/components/occurrence/models.py
+++ b/boranga/components/occurrence/models.py
@@ -6705,8 +6705,6 @@ def related_model_qs(self):
if issubclass(self.related_model, ArchivableModel):
related_model_qs = self.related_model.objects.exclude(archived=True)
- related_model_qs = related_model_qs.only(display_field)
-
return related_model_qs
@property
From 08b0a869017d78537ff970f245df155ea271f5c6 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 15:27:47 +0800
Subject: [PATCH 176/185] Remove fields named 'occurrence_report' from the
model field list as the relationship between the ocr and the other models is
created automatically by the ocr bulk importer. There should never be a need
to manually add the occurrence report field to a schema.
---
boranga/components/main/serializers.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/boranga/components/main/serializers.py b/boranga/components/main/serializers.py
index 9b5a414b..7ff141af 100755
--- a/boranga/components/main/serializers.py
+++ b/boranga/components/main/serializers.py
@@ -141,6 +141,7 @@ def get_model_fields(self, obj):
def filter_fields(field):
return (
field.name not in exclude_fields
+ and field.name != "occurrence_report"
and not field.auto_created
and not (
field.is_relation
From 711dd55b1bf1e8e431103fab8661a7369159c567 Mon Sep 17 00:00:00 2001
From: Oak McIlwain
Date: Mon, 14 Oct 2024 15:41:54 +0800
Subject: [PATCH 177/185] Making sure when adding new columns that they are in
order of django model. Fixed some regressions around ordering of columns.
Added messages when the user tries to add all mandatory columns where there
are no mandatory columns left (that aren't already part of the schema).
---
.../occurrence/bulk_import_schema.vue | 89 ++++++++++++++-----
1 file changed, 69 insertions(+), 20 deletions(-)
diff --git a/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue b/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue
index cb3186ea..2fb55135 100644
--- a/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue
+++ b/boranga/frontend/boranga/src/components/internal/occurrence/bulk_import_schema.vue
@@ -34,8 +34,8 @@
placeholder="Add tag" @keydown="addTag" />