Skip to content

Commit

Permalink
Added custom serializer class and services function
Browse files Browse the repository at this point in the history
  • Loading branch information
Zalo committed Dec 20, 2024
1 parent ae32129 commit bfd75d9
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 57 deletions.
11 changes: 11 additions & 0 deletions src/planscape/impacts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,14 @@ class TreatmentResultsPlotSerializer(serializers.Serializer):
metrics = serializers.ListField(
child=serializers.CharField(max_length=20), help_text="Metrics."
)


from rest_framework import serializers
from stands.models import Stand


class StandQuerySerializer(serializers.Serializer):
# Use PrimaryKeyRelatedField to ensure stand_id is valid PK from Stand model
stand_id = serializers.PrimaryKeyRelatedField(
queryset=Stand.objects.all(), required=True
)
53 changes: 50 additions & 3 deletions src/planscape/impacts/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,56 @@ def calculate_project_area_deltas(
return results


from impacts.models import TreatmentResult, ImpactVariable


def get_treatment_results_table_data(treatment_plan, stand_id):
"""
Retrieves treatment results for RATE_OF_SPREAD and FLAME_LENGTH for the given stand and treatment plan.
Return a list of dictionaries: [{"year": ..., "rate_of_spread": ..., "flame_length": ...}, ...]
"""
# Filter results for the given stand and plan, for the two variables we need
queryset = TreatmentResult.objects.filter(
treatment_plan=treatment_plan,
stand_id=stand_id,
variable__in=[ImpactVariable.RATE_OF_SPREAD, ImpactVariable.FLAME_LENGTH],
)

# If no results, return an empty list
if not queryset.exists():
return []

# Extract distinct years
years = queryset.values_list("year", flat=True).distinct().order_by("year")

# Build table rows
table_data = []
for year in years:
# For each year, get the corresponding records
year_results = queryset.filter(year=year)

# Extract the rate_of_spread value
rate_of_spread_val = (
year_results.filter(variable=ImpactVariable.RATE_OF_SPREAD)
.values_list("value", flat=True)
.first()
)

# Extract the flame_length value
flame_length_val = (
year_results.filter(variable=ImpactVariable.FLAME_LENGTH)
.values_list("Value", flat=True)
.first()
)

# Add a dictionary -- table row -- for each avaialble year
table_data.append(
{
"year": year,
"rate_of_spread": rate_of_spread_val,
"flame_length": flame_length_val,
}
)

def get_treatment_result_for_stand():
# Create logic to retrieve desired stand data from Planscape database tables
pass
# returns list of rows to view
return table_data
88 changes: 34 additions & 54 deletions src/planscape/impacts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,74 +257,54 @@ def plot(self, request, pk=None):
)
return Response(data=data_to_plot, status=status.HTTP_200_OK)



# Import endpoint database logic from impacts/services.py function
from impacts.services import get_treatment_result_for_stand
# Imports
from rest_framework import status, response
from rest_framework.decorators import action
from drf_spectacular.utils import extend_schema
from planscape.serializers import BaseErrorMessageSerializer
from impacts.serializers import StandQuerySerializer
from impacts.services import get_treatment_results_table_data
from impacts.models import TreatmentResult, ImpactVariable

@extend_schema(
description = "Retrieve treatment result information for a specific stand.",
description="Retrieve treatment result information for a specific stand.",
responses={
200: TreatmentResultSerializer, # Successful response with serialized data
404: BaseErrorMessageSerializer, # Stand not found
400: BaseErrorMessageSerializer, # Missing or invalid stand_id
200: TreatmentResultSerializer, # Successful response with serialized data
404: BaseErrorMessageSerializer, # Stand not found
400: BaseErrorMessageSerializer, # Missing or invalid stand_id
},
)

@action(
detail = True, # creates custom endpoint applied to stands in specific treatment plan (pk)
methods = ["get"], # sets custom endpoint to only respond to HTTP GET requests
filterset_class = None, # No additional filtering needed
detail=True, # creates custom endpoint applied to stands in specific treatment plan (pk)
methods=["get"], # sets custom endpoint to only respond to HTTP GET requests
filterset_class=None, # No additional filtering needed
url_path="stand-treatment-results", # The custom endpoint name appended to the URL
)

def stand_treatment_results(self, request):
def stand_treatment_results(self, request, pk=None):
"""
Endpoint to retrieve treatment results for a specific stand ID.
"""
# Get stand id from query parameters (re: as a string from URL of HTTP request)
stand_id = request.query_params.get("stand_id")

# Check if stand id is provided (re: not none, empty, etc.)
if not stand_id:
return response.Response(
{"detail": "stand_id is required."},
status = status.HTTP_400_BAD_REQUEST,
)

# Check if stand is a valid integer
try:
stand_id = int(stand_id)
except ValueError:
return response.Response(
{"detail": "stand_id must be an integer."},
status = status.HTTP_400_BAD_REQUEST,
)

# Get treatment plan id using primary key, pk (re: from the URL of HTTP request)
try:
treatment_plan = self.get_object()
except TreatmentPlan.DoesNotExist:
return response.Response(
{"detail": "stand_id must be an integer."},
status = status.HTTP_400_BAD_REQUEST,
)

# Use function from impacts/services.py to get stand treatment result
try:
result = get_treatment_result_for_stand(treatment_plan, stand_id)
except ProjectAreaTreatmentResult.DoesNotExist:
return response.Response(
{"detail": "Stand treatment results not found in this treatment plan."},
status = status.HTTP_404_NOT_FOUND,
)

# Serialize the stand treatment result and return serialized response
serializer = TreatmentResultSerializer(result)
return response.Response(serializer.data, status = status.HTTP_200_OK)
# Validates stand id by putting stand id from URL into custom serializer, then creates stand object
serializer = StandQuerySerializer(data=request.query_params)
serializer.is_valid(raise_exception=True)
stand = serializer.validated_data["stand_id"]

# Retrieves the treatment plan, using the PK from the URL
treatment_plan = self.get_object()

# Gets table data from the service function, returning error if not found
table_data = get_treatment_results_table_data(treatment_plan, stand.id)

if not table_data:
return response.Response(
{
"detail": "No treatment results found for this stand in the given treatment plan."
},
status=status.HTTP_404_NOT_FOUND,
)

# Returns the data in the correct JSON format with a 200 OK status
return response.Response(table_data, status=status.HTTP_200_OK)


@extend_schema_view(
Expand Down

0 comments on commit bfd75d9

Please sign in to comment.