Generate planting guide pdf with appended filters and plant list
- Rename csv_utils to resource_generation_utils - Rename utils to wms_utils - Adds basic (no styles) pdf generation methods to resource generation - Adds pdf download endpoint
This commit is contained in:
parent
4ab327c442
commit
c63dbeae61
7 changed files with 225 additions and 149 deletions
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
|
@ -5,3 +5,4 @@ __pycache__
|
||||||
resources
|
resources
|
||||||
right_tree/api/data/fixtures/plants.json
|
right_tree/api/data/fixtures/plants.json
|
||||||
right_tree/staticfiles
|
right_tree/staticfiles
|
||||||
|
right_tree/api/data/generated_resources/*
|
|
@ -1,127 +0,0 @@
|
||||||
import csv
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import right_tree.api.data
|
|
||||||
from .filters import *
|
|
||||||
from .utils import get_address_from_coordinates, get_point_from_coordinates
|
|
||||||
|
|
||||||
|
|
||||||
CSV_FILENAME = 'plants.csv'
|
|
||||||
HEADER_FIELDS = ['Names', 'Growth Form / Max Height (m) / Spacing (m) / Forest Position', 'Moisture Preferences',
|
|
||||||
'Tolerances (Water / Drought / Frost / Salinity)', 'Ecosystem Services', 'Carbon Sequestration Rate', 'Planting Stage']
|
|
||||||
|
|
||||||
|
|
||||||
def get_plant_csv_filepath():
|
|
||||||
""" Retrives the filepath for the plant csv file.
|
|
||||||
"""
|
|
||||||
return Path(right_tree.api.data.__file__).resolve().parent / 'resources' / CSV_FILENAME
|
|
||||||
|
|
||||||
|
|
||||||
def get_location_filters(request):
|
|
||||||
""" Retrives the selected location data from the request.
|
|
||||||
"""
|
|
||||||
filter_rows = [['LOCATION FILTERS:']]
|
|
||||||
coordinates = request.query_params.get('coordinates')
|
|
||||||
|
|
||||||
if coordinates is not None:
|
|
||||||
eco_district_layer = ecological_district_coordinate_filter(coordinates).first()
|
|
||||||
point = get_point_from_coordinates(coordinates)
|
|
||||||
|
|
||||||
filter_rows.append(['Point coordinates:', point])
|
|
||||||
filter_rows.append(
|
|
||||||
['Ecological region:', eco_district_layer.ecologic_1 or ''])
|
|
||||||
filter_rows.append(
|
|
||||||
['Ecological district:', eco_district_layer.ecologic_2 or ''])
|
|
||||||
filter_rows.append(
|
|
||||||
['Property address:', get_address_from_coordinates(coordinates)['full_address'] or ''])
|
|
||||||
else:
|
|
||||||
filter_rows.append(["None specified"])
|
|
||||||
|
|
||||||
return filter_rows
|
|
||||||
|
|
||||||
|
|
||||||
def get_soil_filters(request):
|
|
||||||
""" Retrives the selected soil type data from the request.
|
|
||||||
"""
|
|
||||||
filter_rows = [['SOIL FILTERS:']]
|
|
||||||
soil_variant = request.query_params.get('soilVariant')
|
|
||||||
coordinates = request.query_params.get('coordinates')
|
|
||||||
|
|
||||||
if soil_variant is not None and coordinates is not None:
|
|
||||||
soil_order_obj = soil_order_coordinate_filter(coordinates).first()
|
|
||||||
|
|
||||||
filter_rows.append(
|
|
||||||
['Soil Order:', f"{soil_order_obj.name or ''} ({soil_order_obj.code or ''})"])
|
|
||||||
filter_rows.append(['Soil Variant:', soil_variant])
|
|
||||||
else:
|
|
||||||
filter_rows.append(["None specified"])
|
|
||||||
|
|
||||||
return filter_rows
|
|
||||||
|
|
||||||
|
|
||||||
def get_site_filters(request):
|
|
||||||
""" Retrives the selected site data from the request.
|
|
||||||
"""
|
|
||||||
filter_rows = [['SITE FILTERS:']]
|
|
||||||
|
|
||||||
habitat = request.query_params.get('habitat')
|
|
||||||
zone = request.query_params.get('zone')
|
|
||||||
if zone is not None and habitat is not None:
|
|
||||||
habitat_json = json.loads(habitat)
|
|
||||||
zone_json = json.loads(zone)
|
|
||||||
|
|
||||||
filter_rows.append(['Habitat:', habitat_json.get("name", "")])
|
|
||||||
filter_rows.append(['Zone Name:', zone_json.get("name", "")])
|
|
||||||
filter_rows.append(['Zone Variant:', zone_json.get("variant", "")])
|
|
||||||
filter_rows.append(
|
|
||||||
['Zone Refined Variant:', zone_json.get("refined_variant", "")])
|
|
||||||
else:
|
|
||||||
filter_rows.append(["None specified"])
|
|
||||||
|
|
||||||
return filter_rows
|
|
||||||
|
|
||||||
|
|
||||||
def get_filter_values(request):
|
|
||||||
""" Retrives all selected values/filters from the request.
|
|
||||||
"""
|
|
||||||
filter_rows = []
|
|
||||||
|
|
||||||
# Add all the location filters
|
|
||||||
filter_rows += get_location_filters(request)
|
|
||||||
filter_rows.append([''])
|
|
||||||
|
|
||||||
# Add the soil filters
|
|
||||||
filter_rows += get_soil_filters(request)
|
|
||||||
filter_rows.append([''])
|
|
||||||
|
|
||||||
# Add the project site filters
|
|
||||||
filter_rows += get_site_filters(request)
|
|
||||||
filter_rows.append([''])
|
|
||||||
|
|
||||||
return filter_rows
|
|
||||||
|
|
||||||
|
|
||||||
def create_plant_csv_file(request, plant_data):
|
|
||||||
""" Constructs a csv file that contains selected filter values and the resulting plant list.
|
|
||||||
"""
|
|
||||||
with open(get_plant_csv_filepath(), 'w', encoding='UTF8') as f:
|
|
||||||
writer = csv.writer(f)
|
|
||||||
|
|
||||||
# Write filter/selected values
|
|
||||||
for filter_row in get_filter_values(request):
|
|
||||||
writer.writerow(filter_row)
|
|
||||||
|
|
||||||
# Write the plant data
|
|
||||||
writer.writerow(HEADER_FIELDS)
|
|
||||||
for plant in plant_data:
|
|
||||||
plant_data_row = [
|
|
||||||
plant.display_name,
|
|
||||||
plant.display_growth_form,
|
|
||||||
plant.moisture_preferences,
|
|
||||||
plant.plant_tolerances,
|
|
||||||
plant.ecosystem_services,
|
|
||||||
plant.carbon_sequestration,
|
|
||||||
plant.stage
|
|
||||||
]
|
|
||||||
|
|
||||||
writer.writerow(plant_data_row)
|
|
|
@ -4,7 +4,7 @@ from django.http import Http404
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from .models import Plant, EcologicalRegion, EcologicalDistrictLayer, SoilOrder, SoilVariant
|
from .models import Plant, EcologicalRegion, EcologicalDistrictLayer, SoilOrder, SoilVariant
|
||||||
from .utils import get_point_from_coordinates
|
from .wms_utils import get_point_from_coordinates
|
||||||
|
|
||||||
|
|
||||||
def coordinate_filter(request, queryset, ignore_soil_order=False):
|
def coordinate_filter(request, queryset, ignore_soil_order=False):
|
||||||
|
|
191
backend/right_tree/api/resource_generation_utils.py
Normal file
191
backend/right_tree/api/resource_generation_utils.py
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
import csv
|
||||||
|
from os import write
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import right_tree.api.data
|
||||||
|
from .filters import *
|
||||||
|
from .wms_utils import get_address_from_coordinates, get_point_from_coordinates
|
||||||
|
|
||||||
|
import pdfkit
|
||||||
|
import pandas as pd
|
||||||
|
from PyPDF2 import PdfFileMerger
|
||||||
|
|
||||||
|
|
||||||
|
CSV_FILENAME = 'plants.csv'
|
||||||
|
PLANTING_GUIDE_PDF_FILENAME = 'planting_guide.pdf'
|
||||||
|
HEADER_FIELDS = ['Names', 'Growth Form / Max Height (m) / Spacing (m) / Forest Position', 'Moisture Preferences',
|
||||||
|
'Tolerances (Water / Drought / Frost / Salinity)', 'Ecosystem Services', 'Carbon Sequestration Rate', 'Planting Stage']
|
||||||
|
|
||||||
|
|
||||||
|
def get_plant_resource_filepath(filename=CSV_FILENAME, resource_dir='generated_resources'):
|
||||||
|
""" Retrives the filepath for the plant csv file.
|
||||||
|
"""
|
||||||
|
return Path(right_tree.api.data.__file__).resolve().parent / resource_dir / filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_location_filters(request):
|
||||||
|
""" Retrives the selected location data from the request.
|
||||||
|
"""
|
||||||
|
filter_rows = [['LOCATION FILTERS:']]
|
||||||
|
coordinates = request.query_params.get('coordinates')
|
||||||
|
|
||||||
|
if coordinates is not None:
|
||||||
|
eco_district_layer = ecological_district_coordinate_filter(
|
||||||
|
coordinates).first()
|
||||||
|
point = get_point_from_coordinates(coordinates)
|
||||||
|
|
||||||
|
filter_rows.append(['Point coordinates:', point])
|
||||||
|
filter_rows.append(['Ecological region:', eco_district_layer.ecologic_1 or ''])
|
||||||
|
filter_rows.append(['Ecological district:', eco_district_layer.ecologic_2 or ''])
|
||||||
|
filter_rows.append(['Property address:', get_address_from_coordinates(coordinates)['full_address'] or ''])
|
||||||
|
else:
|
||||||
|
filter_rows.append(["None specified"])
|
||||||
|
|
||||||
|
return filter_rows
|
||||||
|
|
||||||
|
|
||||||
|
def get_soil_filters(request):
|
||||||
|
""" Retrives the selected soil type data from the request.
|
||||||
|
"""
|
||||||
|
filter_rows = [['SOIL FILTERS:']]
|
||||||
|
soil_variant = request.query_params.get('soilVariant')
|
||||||
|
coordinates = request.query_params.get('coordinates')
|
||||||
|
|
||||||
|
if soil_variant is not None and coordinates is not None:
|
||||||
|
soil_order_obj = soil_order_coordinate_filter(coordinates).first()
|
||||||
|
|
||||||
|
filter_rows.append(['Soil Order:', f"{soil_order_obj.name or ''} ({soil_order_obj.code or ''})"])
|
||||||
|
filter_rows.append(['Soil Variant:', soil_variant])
|
||||||
|
else:
|
||||||
|
filter_rows.append(["None specified"])
|
||||||
|
|
||||||
|
return filter_rows
|
||||||
|
|
||||||
|
|
||||||
|
def get_site_filters(request):
|
||||||
|
""" Retrives the selected site data from the request.
|
||||||
|
"""
|
||||||
|
filter_rows = [['SITE FILTERS:']]
|
||||||
|
|
||||||
|
habitat = request.query_params.get('habitat')
|
||||||
|
zone = request.query_params.get('zone')
|
||||||
|
if zone is not None and habitat is not None:
|
||||||
|
habitat_json = json.loads(habitat)
|
||||||
|
zone_json = json.loads(zone)
|
||||||
|
|
||||||
|
filter_rows.append(['Habitat:', habitat_json.get("name", "")])
|
||||||
|
filter_rows.append(['Zone Name:', zone_json.get("name", "")])
|
||||||
|
filter_rows.append(['Zone Variant:', zone_json.get("variant", "")])
|
||||||
|
filter_rows.append(['Zone Refined Variant:', zone_json.get("refined_variant", "")])
|
||||||
|
else:
|
||||||
|
filter_rows.append(["None specified"])
|
||||||
|
|
||||||
|
return filter_rows
|
||||||
|
|
||||||
|
|
||||||
|
def get_filter_values(request):
|
||||||
|
""" Retrives all selected values/filters from the request.
|
||||||
|
"""
|
||||||
|
filter_rows = []
|
||||||
|
|
||||||
|
# Add all the location filters
|
||||||
|
filter_rows += get_location_filters(request)
|
||||||
|
filter_rows.append([''])
|
||||||
|
|
||||||
|
# Add the soil filters
|
||||||
|
filter_rows += get_soil_filters(request)
|
||||||
|
filter_rows.append([''])
|
||||||
|
|
||||||
|
# Add the project site filters
|
||||||
|
filter_rows += get_site_filters(request)
|
||||||
|
filter_rows.append([''])
|
||||||
|
|
||||||
|
return filter_rows
|
||||||
|
|
||||||
|
|
||||||
|
def write_csv_filter_info(request, writer):
|
||||||
|
""" Retrieves and writes filter information to a CSV file given a writer.
|
||||||
|
"""
|
||||||
|
for filter_row in get_filter_values(request):
|
||||||
|
writer.writerow(filter_row)
|
||||||
|
|
||||||
|
|
||||||
|
def write_csv_plant_info(plant_data, writer):
|
||||||
|
""" Writes plant list information to a CSV file given a writer.
|
||||||
|
"""
|
||||||
|
writer.writerow(HEADER_FIELDS)
|
||||||
|
for plant in plant_data:
|
||||||
|
plant_data_row = [
|
||||||
|
plant.display_name,
|
||||||
|
plant.display_growth_form,
|
||||||
|
plant.moisture_preferences,
|
||||||
|
plant.plant_tolerances,
|
||||||
|
plant.ecosystem_services,
|
||||||
|
plant.carbon_sequestration,
|
||||||
|
plant.stage
|
||||||
|
]
|
||||||
|
writer.writerow(plant_data_row)
|
||||||
|
|
||||||
|
|
||||||
|
def create_plant_csv_file(request, plant_data):
|
||||||
|
""" Constructs a csv file that contains selected filter values and the resulting plant list.
|
||||||
|
"""
|
||||||
|
with open(get_plant_resource_filepath(), 'w', encoding='UTF8') as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
|
||||||
|
# Write filter/selected values
|
||||||
|
write_csv_filter_info(request, writer)
|
||||||
|
|
||||||
|
# Write the plant data
|
||||||
|
write_csv_plant_info(plant_data, writer)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_pdf(data_name, data, csv_generation_function):
|
||||||
|
""" Generates a pdf from a csv given data and a csv generation method.
|
||||||
|
Requires an html file to be generated as an intermediate step.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Define filepaths - some are required as intermediate files
|
||||||
|
csv_filepath = get_plant_resource_filepath(f'{data_name}.csv')
|
||||||
|
html_filepath = get_plant_resource_filepath(f'{data_name}.html')
|
||||||
|
pdf_filepath = get_plant_resource_filepath(f'{data_name}.pdf')
|
||||||
|
|
||||||
|
# Create an initial csv file with the data
|
||||||
|
with open(csv_filepath, 'w', encoding='UTF8') as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
csv_generation_function(data, writer)
|
||||||
|
|
||||||
|
# Convert the csv to and html file then to a pdf
|
||||||
|
CSV = pd.read_csv(csv_filepath)
|
||||||
|
CSV.to_html(html_filepath)
|
||||||
|
pdfkit.from_file(str(html_filepath), str(pdf_filepath))
|
||||||
|
|
||||||
|
|
||||||
|
def create_planting_guide_pdf(request, plant_data):
|
||||||
|
""" Creates a planting guide pdf document with a pre-generated planting guide with
|
||||||
|
filter and plant list tabular informtation appended.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Define the names of the resource files as used in multiple places
|
||||||
|
filter_file_prefix = "filters"
|
||||||
|
plant_list_file_prefix = "plant_list"
|
||||||
|
|
||||||
|
# Generate the filters and plant list pdf files
|
||||||
|
generate_pdf(filter_file_prefix, request, write_csv_filter_info) # TODO: space values appear as NaN... this should be fixed
|
||||||
|
generate_pdf(plant_list_file_prefix, plant_data, write_csv_plant_info)
|
||||||
|
|
||||||
|
# Create a list of pdfs that need to be merged
|
||||||
|
planting_guide_pdf_filepath = get_plant_resource_filepath(PLANTING_GUIDE_PDF_FILENAME, 'resources')
|
||||||
|
filter_pdf_filepath = get_plant_resource_filepath(f'{filter_file_prefix}.pdf')
|
||||||
|
plant_list_pdf_filepath = get_plant_resource_filepath(f'{plant_list_file_prefix}.pdf')
|
||||||
|
pdfs = [planting_guide_pdf_filepath, filter_pdf_filepath, plant_list_pdf_filepath]
|
||||||
|
|
||||||
|
# Merge pdfs
|
||||||
|
merger = PdfFileMerger()
|
||||||
|
for pdf in pdfs:
|
||||||
|
merger.append(str(pdf))
|
||||||
|
|
||||||
|
# Create the final output pdf with all merged documents
|
||||||
|
output_pdf_filepath = get_plant_resource_filepath(PLANTING_GUIDE_PDF_FILENAME)
|
||||||
|
merger.write(str(output_pdf_filepath))
|
||||||
|
merger.close()
|
|
@ -1,20 +1,19 @@
|
||||||
from django.http import HttpResponseBadRequest, HttpResponse, HttpRequest
|
from django.http import HttpResponseBadRequest, HttpResponse, FileResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from wsgiref.util import FileWrapper
|
||||||
|
|
||||||
from right_tree.api.models import Habitat, HabitatImage, Plant, EcologicalDistrictLayer, SoilOrder
|
from right_tree.api.models import Habitat, HabitatImage, Plant, EcologicalDistrictLayer, SoilOrder
|
||||||
from right_tree.api.serializers import HabitatImageSerializer, HabitatSerializer, PlantSerializer, SoilOrderSerializer, EcologicalDistrictLayerSerializer, AddressSerializer
|
from right_tree.api.serializers import HabitatImageSerializer, HabitatSerializer, PlantSerializer, SoilOrderSerializer, EcologicalDistrictLayerSerializer, AddressSerializer
|
||||||
|
|
||||||
from .filters import *
|
from .filters import *
|
||||||
from .utils import get_address_from_coordinates
|
from .wms_utils import get_address_from_coordinates
|
||||||
from .csv_utils import create_plant_csv_file, get_plant_csv_filepath
|
from .resource_generation_utils import create_plant_csv_file, get_plant_resource_filepath, create_planting_guide_pdf, PLANTING_GUIDE_PDF_FILENAME
|
||||||
|
|
||||||
|
|
||||||
class PlantViewSet(viewsets.ModelViewSet):
|
class PlantViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
""" Filtered viewset for plants.
|
||||||
Filtered viewset for plants.
|
|
||||||
"""
|
"""
|
||||||
queryset = Plant.objects.all()
|
queryset = Plant.objects.all()
|
||||||
serializer_class = PlantSerializer
|
serializer_class = PlantSerializer
|
||||||
|
@ -27,8 +26,7 @@ class PlantViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class SoilOrderViewSet(viewsets.ModelViewSet):
|
class SoilOrderViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
""" Filtered viewset for soil details.
|
||||||
Filtered viewset for soil details.
|
|
||||||
"""
|
"""
|
||||||
serializer_class = SoilOrderSerializer
|
serializer_class = SoilOrderSerializer
|
||||||
|
|
||||||
|
@ -43,8 +41,7 @@ class SoilOrderViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class EcologicalDistrictViewSet(viewsets.ModelViewSet):
|
class EcologicalDistrictViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
""" Filtered viewset for ecological district/region details.
|
||||||
Filtered viewset for ecological district/region details.
|
|
||||||
"""
|
"""
|
||||||
serializer_class = EcologicalDistrictLayerSerializer
|
serializer_class = EcologicalDistrictLayerSerializer
|
||||||
|
|
||||||
|
@ -59,8 +56,7 @@ class EcologicalDistrictViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class LINZPropertyViewSet(viewsets.ViewSet):
|
class LINZPropertyViewSet(viewsets.ViewSet):
|
||||||
"""
|
""" Filtered viewset for ecological district/region details.
|
||||||
Filtered viewset for ecological district/region details.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def list(self, request):
|
def list(self, request):
|
||||||
|
@ -74,16 +70,14 @@ class LINZPropertyViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
|
|
||||||
class HabitatViewSet(viewsets.ModelViewSet):
|
class HabitatViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
""" Viewset for all habitats.
|
||||||
Viewset for all habitats.
|
|
||||||
"""
|
"""
|
||||||
serializer_class = HabitatSerializer
|
serializer_class = HabitatSerializer
|
||||||
queryset = Habitat.objects.all()
|
queryset = Habitat.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class HabitatImageViewSet(viewsets.ViewSet):
|
class HabitatImageViewSet(viewsets.ViewSet):
|
||||||
"""
|
""" Viewset for a habitat image.
|
||||||
Viewset for a habitat image.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def list(self, request):
|
def list(self, request):
|
||||||
|
@ -99,12 +93,27 @@ class HabitatImageViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
|
|
||||||
class CSVDownloadView(viewsets.ViewSet):
|
class CSVDownloadView(viewsets.ViewSet):
|
||||||
|
""" Viewset for a downloading a CSV plant list and filters.
|
||||||
|
"""
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
filtered_plants = get_filtered_plants(request)
|
filtered_plants = get_filtered_plants(request)
|
||||||
create_plant_csv_file(request, filtered_plants)
|
create_plant_csv_file(request, filtered_plants)
|
||||||
|
|
||||||
csv_file = open(get_plant_csv_filepath(), 'rb')
|
csv_file = open(get_plant_resource_filepath(), 'rb')
|
||||||
response = HttpResponse(csv_file, content_type='text/csv')
|
response = HttpResponse(csv_file, content_type='text/csv')
|
||||||
response['Content-Disposition'] = 'attachment; filename="plants.csv"'
|
response['Content-Disposition'] = 'attachment; filename="plants.csv"'
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
class PDFDownloadView(viewsets.ViewSet):
|
||||||
|
""" Viewset for a downloading a PDF planting guide with appended filter and plant list info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
filtered_plants = get_filtered_plants(request)
|
||||||
|
|
||||||
|
create_planting_guide_pdf(request, filtered_plants)
|
||||||
|
pdf_file = open(get_plant_resource_filepath(PLANTING_GUIDE_PDF_FILENAME), 'rb')
|
||||||
|
|
||||||
|
response = HttpResponse(FileWrapper(pdf_file), content_type='application/pdf')
|
||||||
|
return response
|
||||||
|
|
|
@ -25,8 +25,10 @@ router.register(r'soil', views.SoilOrderViewSet, basename='soil')
|
||||||
router.register(r'ecologicaldistrict', views.EcologicalDistrictViewSet, basename='ecologicaldistrict')
|
router.register(r'ecologicaldistrict', views.EcologicalDistrictViewSet, basename='ecologicaldistrict')
|
||||||
router.register(r'address', views.LINZPropertyViewSet, basename='address')
|
router.register(r'address', views.LINZPropertyViewSet, basename='address')
|
||||||
router.register(r'habitats', views.HabitatViewSet, basename='habitats')
|
router.register(r'habitats', views.HabitatViewSet, basename='habitats')
|
||||||
router.register(r'habitatimage', views.HabitatImageViewSet,basename='habitatimage')
|
router.register(r'habitatimage', views.HabitatImageViewSet, basename='habitatimage')
|
||||||
router.register(r'download/csv', views.CSVDownloadView ,basename='downloadcsv')
|
|
||||||
|
router.register(r'download/csv', views.CSVDownloadView, basename='downloadcsv')
|
||||||
|
router.register(r'download/pdf', views.PDFDownloadView, basename='downloadpdf')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
|
Loading…
Reference in a new issue