right-tree/backend/right_tree/api/resource_generation_utils.py
Dana Lambert c63dbeae61 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
2021-11-18 11:43:18 +13:00

191 lines
6.7 KiB
Python

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()