Skip to content

Commit

Permalink
Merge pull request #517 from oakdbca/main
Browse files Browse the repository at this point in the history
Latest work
  • Loading branch information
xzzy authored Aug 14, 2024
2 parents 46cd995 + 26b2481 commit 90d647b
Show file tree
Hide file tree
Showing 13 changed files with 610 additions and 214 deletions.
42 changes: 42 additions & 0 deletions boranga/components/occurrence/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
IsOccurrenceReportReferee,
OccurrenceObjectPermission,
OccurrencePermission,
OccurrenceReportCopyPermission,
OccurrenceReportObjectPermission,
OccurrenceReportPermission,
)
Expand Down Expand Up @@ -2418,6 +2419,47 @@ def update_show_on_map(self, request, *args, **kwargs):

return Response(serializer.data)

@detail_route(
methods=[
"POST",
],
detail=True,
permission_classes=[OccurrenceReportCopyPermission],
)
@renderer_classes((JSONRenderer,))
def copy(self, request, *args, **kwargs):
instance = self.get_object()
ocr_copy = instance.copy(request.user.id)

# Log the action
ocr_copy.log_user_action(
OccurrenceReportUserAction.ACTION_COPY.format(
ocr_copy.occurrence_report_number, instance.occurrence_report_number
),
request,
)
request.user.log_user_action(
OccurrenceReportUserAction.ACTION_COPY.format(
ocr_copy.occurrence_report_number, instance.occurrence_report_number
),
request,
)
instance.log_user_action(
OccurrenceReportUserAction.ACTION_COPY_TO.format(
ocr_copy.occurrence_report_number,
),
request,
)
request.user.log_user_action(
OccurrenceReportUserAction.ACTION_COPY_TO.format(
ocr_copy.occurrence_report_number,
),
request,
)

serializer = self.get_serializer(ocr_copy)
return Response(serializer.data)


class ObserverDetailViewSet(viewsets.GenericViewSet, mixins.RetrieveModelMixin):
queryset = OCRObserverDetail.objects.all()
Expand Down
161 changes: 155 additions & 6 deletions boranga/components/occurrence/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,156 @@ def external_referral_invites(self):
archived=False, datetime_first_logged_in__isnull=True
)

@transaction.atomic
def copy(self, request_user_id):
ocr_copy = OccurrenceReport.objects.get(id=self.id)
ocr_copy.pk = None
ocr_copy.processing_status = OccurrenceReport.PROCESSING_STATUS_DRAFT
ocr_copy.customer_status = OccurrenceReport.CUSTOMER_STATUS_DRAFT
ocr_copy.occurrence_report_number = ""
ocr_copy.lodgement_date = None
ocr_copy.observation_date = None
ocr_copy.assigned_officer = None
ocr_copy.assigned_approver = None
ocr_copy.approved_by = None
ocr_copy.submitter_information = None
if request_user_id != self.submitter:
ocr_copy.submitter = request_user_id
ocr_copy.internal_application = True
ocr_copy.save(no_revision=True)

if request_user_id == self.submitter:
# Use the same submitter category as the previous proposal when the user copying is the submitter
ocr_copy.submitter_information.submitter_category_id = (
self.submitter_information.submitter_category_id
)
ocr_copy.submitter_information.save()

# Clone all the associated models
if hasattr(self, "location") and self.location:
location = clone_model(
OCRLocation,
OCRLocation,
self.location,
)
if location:
location.occurrence_report = ocr_copy
location.save()

if hasattr(self, "habitat_composition") and self.habitat_composition:
habitat_composition = clone_model(
OCRHabitatComposition,
OCRHabitatComposition,
self.habitat_composition,
)
if habitat_composition:
habitat_composition.occurrence_report = ocr_copy
habitat_composition.save()

if hasattr(self, "habitat_condition") and self.habitat_condition:
habitat_condition = clone_model(
OCRHabitatCondition,
OCRHabitatCondition,
self.habitat_condition,
)
if habitat_condition:
habitat_condition.occurrence_report = ocr_copy
habitat_condition.save()

if hasattr(self, "vegetation_structure") and self.vegetation_structure:
vegetation_structure = clone_model(
OCRVegetationStructure,
OCRVegetationStructure,
self.vegetation_structure,
)
if vegetation_structure:
vegetation_structure.occurrence_report = ocr_copy
vegetation_structure.save()

if hasattr(self, "fire_history") and self.fire_history:
fire_history = clone_model(
OCRFireHistory, OCRFireHistory, self.fire_history
)
if fire_history:
fire_history.occurrence_report = ocr_copy
fire_history.save()

if hasattr(self, "associated_species") and self.associated_species:
associated_species = clone_model(
OCRAssociatedSpecies,
OCRAssociatedSpecies,
self.associated_species,
)
if associated_species:
associated_species.occurrence_report = ocr_copy
associated_species.save()
# copy over related species separately
for i in self.associated_species.related_species.all():
associated_species.related_species.add(i)

# Clone the threats
for threat in self.ocr_threats.all():
ocr_threat = clone_model(
OCRConservationThreat, OCRConservationThreat, threat
)
if ocr_threat:
ocr_threat.occurrence_report = ocr_copy
ocr_threat.occurrence_report_threat = threat
ocr_threat.save()

# Clone the documents
for doc in self.documents.all():
ocr_doc = clone_model(
OccurrenceReportDocument, OccurrenceReportDocument, doc
)
if ocr_doc:
ocr_doc.occurrence_report = ocr_copy
ocr_doc.save()

# Clone any observers
observer_qs = self.observer_detail.all()
if request_user_id == self.submitter:
# If the user copying is not the submitter, only copy the main observer
observer_qs = self.observer_detail.filter(main_observer=True)
for observer in observer_qs:
ocr_observer = clone_model(OCRObserverDetail, OCRObserverDetail, observer)
if ocr_observer:
ocr_observer.occurrence_report = ocr_copy
ocr_observer.save()

# Clone any occurrence geometries
for geom in self.ocr_geometry.all():
ocr_geom = clone_model(
OccurrenceReportGeometry, OccurrenceReportGeometry, geom
)
if ocr_geom:
ocr_geom.occurrence_report = ocr_copy
ocr_geom.save()

# For flora create an empty plant count
if self.group_type.name == GroupType.GROUP_TYPE_FLORA:
plant_count = OCRPlantCount()
plant_count.occurrence_report = ocr_copy
plant_count.save()

# For fauna create an empty animal observation
if self.group_type.name == GroupType.GROUP_TYPE_FAUNA:
animal_observation = OCRAnimalObservation()
animal_observation.occurrence_report = ocr_copy
animal_observation.save()

# Create an empty observation detail
observation_detail = OCRObservationDetail()
observation_detail.occurrence_report = ocr_copy
observation_detail.save()

# Create an empty identification
identification = OCRIdentification()
identification.occurrence_report = ocr_copy
identification.save()

return ocr_copy


class OccurrenceReportDeclinedDetails(models.Model):
occurrence_report = models.OneToOneField(
Expand Down Expand Up @@ -1214,6 +1364,9 @@ class OccurrenceReportUserAction(UserAction):
ACTION_REINSTATE_PROPOSAL = "Reinstate occurrence report {}"
ACTION_APPROVAL_LEVEL_DOCUMENT = "Assign Approval level document {}"
ACTION_UPDATE_OBSERVER_DETAIL = "Update Observer {} on occurrence report {}"
ACTION_COPY = "Created occurrence report {} from a copy of occurrence report {}"
ACTION_COPY_TO = "Copy occurrence report to {}"

# Amendment
ACTION_ID_REQUEST_AMENDMENTS = "Request amendments"

Expand Down Expand Up @@ -3743,16 +3896,12 @@ def get_related_items(self, filter_type, **kwargs):

# Add parent species related items to the list (limited to one degree of separation)
if a_field.name == "species" and self.species:
return_list.extend(
self.species.get_related_items("all_except_occurrence_reports")
)
return_list.extend(self.species.get_related_items("for_occurrence"))

# Add renamed from / renamed to community related items to the list
if a_field.name == "community" and self.community:
return_list.extend(
self.community.get_related_items(
"all_except_occurrence_reports"
)
self.community.get_related_items("for_occurrence")
)

# Remove the occurrence itself from the list if it ended up there
Expand Down
11 changes: 11 additions & 0 deletions boranga/components/occurrence/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,17 @@ def has_object_permission(self, request, view, obj):
return False


class OccurrenceReportCopyPermission(BasePermission):
def has_object_permission(self, request, view, obj):
if not request.user.is_authenticated:
return False

if request.user.is_superuser:
return True

return obj.submitter == request.user.id or is_occurrence_assessor(request)


class OccurrencePermission(BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated:
Expand Down
6 changes: 6 additions & 0 deletions boranga/components/occurrence/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,7 @@ class InternalOccurrenceReportSerializer(OccurrenceReportSerializer):
can_user_assess = serializers.SerializerMethodField()
can_user_action = serializers.SerializerMethodField()
can_add_log = serializers.SerializerMethodField()
user_is_assessor = serializers.SerializerMethodField()
current_assessor = serializers.SerializerMethodField(read_only=True)
approval_details = OccurrenceReportApprovalDetailsSerializer(
read_only=True, allow_null=True
Expand Down Expand Up @@ -1455,6 +1456,7 @@ class Meta:
"has_main_observer",
"is_submitter",
"migrated_from_id",
"user_is_assessor",
)

def get_readonly(self, obj):
Expand Down Expand Up @@ -1487,6 +1489,10 @@ def get_can_user_assess(self, obj):
and obj.assigned_officer == request.user.id
)

def get_user_is_assessor(self, obj):
request = self.context["request"]
return is_occurrence_assessor(request)

def get_can_user_approve(self, obj):
request = self.context["request"]
return (
Expand Down
Loading

0 comments on commit 90d647b

Please sign in to comment.