<commit_before>#!/usr/bin/env python
# * coding: utf8 *
'''
tank_risk.py
ArcGIS script tool for evaluating tank risk based on spatial relationships to other data.
'''
import csv
import os
import time
from pathlib import Path

import arcpy

VERSION = '2.1.2'


def format_time(seconds):
    '''seconds: number
    returns a human-friendly string describing the amount of time
    '''
    minute = 60.00
    hour = 60.00 * minute

    if seconds < 30:
        return f'{int(seconds * 1000)} ms\'s'

    if seconds < 90:
        return f'{round(seconds, 2)} seconds'

    if seconds < 90 * minute:
        return f'{round(seconds / minute, 2)} minutes'

    return f'{round(seconds / hour, 2)} hours'


class MapSource():
    '''Class for accessing map document information.'''

    def __init__(self, pro_project, map_name):
        project = arcpy.mp.ArcGISProject(pro_project)
        self.risk_map = project.listMaps(map_name)[0]

    def get_selected_layers(self, messages):
        layer_paths = []

        for layer in self.risk_map.listLayers():
            if not layer.visible:
                messages.AddWarningMessage(f'Skipping {layer.name}, reason visibility')

                continue

            if layer.supports('DATASOURCE'):
                layer_paths.append(layer)
            elif layer.supports('WORKSPACEPATH') and layer.supports('DATASETNAME'):
                layer_paths.append(layer)
            elif layer.supports('NAME'):
                messages.AddWarningMessage(f'Could not find workspace: {layer.name}')
            else:
                messages.AddWarningMessage(f'{layer.name} not supported')

        return layer_paths


class Outputs():
    '''Outputs stores all the directory and file name info.
    - setOutputDirectory initializes all attribute.'''
    unique_time_string = None

    output_directory = None
    output_gdb_name = None
    output_gdb = None
    output_table_name = None

    output_csv_name = None

    temp_dir = None
    temp_gdb_name = None
    temp_gdb = None

    @staticmethod
    def set_output_directory(output_dir):
        Outputs.unique_time_string = time.strftime('%Y%m%d%H%M%S')

        Outputs.output_directory = output_dir
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        Outputs.output_gdb_name = 'TankRisk_{}.gdb'.format(Outputs.unique_time_string)
        Outputs.output_gdb = os.path.join(Outputs.output_directory, Outputs.output_gdb_name)
        Outputs.output_table_name = 'TankRiskResults_{}'.format(Outputs.unique_time_string)

        Outputs.output_csv_name = 'TankRiskResults_{}.csv'.format(Outputs.unique_time_string)

        Outputs.temp_dir = 'in_memory'
        Outputs.temp_gdb_name = 'nearsTemp_{}.gdb'.format(Outputs.unique_time_string)
        Outputs.temp_gdb = 'in_memory'


class LayerAttributes():
    '''LayerAttributes stores risk feature information in a way that
    is easy to access.'''

    def __init__(self, risk_type, value_result_attribute, severity_result_attribute, value_field, severity_field, calc_fields=None, value_method=None):
        #: type of risk feature
        self.type = risk_type
        #: attribute for value result
        self.value_result_attribute = value_result_attribute
        #: attribute for severity result
        self.severity_result_attribute = severity_result_attribute
        #: Value field name in output table
        self.value_field = value_field
        #: Severity field name in output table
        self.severity_field = severity_field
        #: Fields for attribute type risk features
        self.calc_fields = calc_fields
        #: Method to produce value. Not yet used.
        self.value_method = value_method


class TankResult():
    '''Stores everything that is specific to each risk feature including:
    - Literals (field names)
    - Logic for scoring results for each risk feature
    - Result data for each risk feature'''
    tank_results = {}
    IN_POLYGON = 'inPolygon'
    DISTANCE = 'distance'
    ATTRIBUTE = 'attribute'
    OUTPUT_ID_FIELD = 'facilityid'

    #: attributesForFeature associates feature names with attributes
    attributes_for_feature = {
        'aquifer_recharge_discharge_areas': LayerAttributes(ATTRIBUTE, 'aquiferVal', 'aquiferSev', 'aquiferVal', 'aquiferSev', ['zone']),
        'wetlands': LayerAttributes(IN_POLYGON, 'wetLandsVal', 'wetLandsSev', 'wetLandsVal', 'wetLandsSev'),
        'lakes_nhd': LayerAttributes(DISTANCE, 'lakesVal', 'lakeSev', 'lakesVal', 'lakeSev'),
        'streams_nhd': LayerAttributes(DISTANCE, 'streamsVal', 'streamsSev', 'streamsVal', 'streamsSev'),
        'dwq_assessment_units': LayerAttributes(ATTRIBUTE, 'assessmentVal', 'assessmentSev', 'assessmentVal', 'assessmentSev', ['status2006']),
        'soil': LayerAttributes(ATTRIBUTE, 'soilVal', 'soilSev', 'soilVal', 'soilSev', ['musurftexgrp']),
        'shallow_ground_water': LayerAttributes(ATTRIBUTE, 'shallowWaterVal', 'shallowWaterSev', 'shallowWaterVal', 'shallowWaterSev', ['depth']),
        'census_tracts_2020': LayerAttributes(ATTRIBUTE, 'censusVal', 'censusSev', 'censusVal', 'censusSev', ['pop100', 'aland20']),
        'GroundWaterZones': LayerAttributes(ATTRIBUTE, 'udwspzVal', 'udwspzSev', 'udwspzVal', 'udwspzSev', ['protzone']),
        'SurfaceWaterZones': LayerAttributes(ATTRIBUTE, 'udwspzVal', 'udwspzSev', 'udwspzVal', 'udwspzSev', ['protzone']),
        'points_of_diversion': LayerAttributes(DISTANCE, 'podVal', 'podSev', 'podVal', 'podSev')
    }

    def __init__(self, tank_id):
        #: FacilityUST OBJECTID
        self.tank_id = tank_id
        #: Risk layer result attributes
        for layer_name in TankResult.attributes_for_feature:
            self.__dict__[TankResult.attributes_for_feature[layer_name].value_result_attribute] = None
            self.__dict__[TankResult.attributes_for_feature[layer_name].severity_result_attribute] = None

    def set_value_for_layer(self, layer_name, value):
        self.__dict__[TankResult.attributes_for_feature[layer_name].value_result_attribute] = value

    def get_value_for_layer(self, layer_name):
        return self.__dict__[TankResult.attributes_for_feature[layer_name].value_result_attribute]

    def set_severity_for_layer(self, layer_name, severity):
        self.__dict__[TankResult.attributes_for_feature[layer_name].severity_result_attribute] = severity

    def get_severity_for_layer(self, layer_name):
        return self.__dict__[TankResult.attributes_for_feature[layer_name].severity_result_attribute]

    @staticmethod
    def get_output_rows(feature_names):
        '''Get a list that contains the rows of the output table, including header.
        - feature_names are used to order output table fields.'''
        output_rows = []
        feature_name_order_header = list(feature_names)
        feature_name_order_values = []

        #: Add header
        header_list = [TankResult.OUTPUT_ID_FIELD]

        for layer_name in feature_name_order_header:
            value_field = TankResult.attributes_for_feature[layer_name].value_field
            severity_field = TankResult.attributes_for_feature[layer_name].severity_field

            if value_field in header_list or severity_field in header_list:
                #: Two layers can share one output field and the field doesn't need to be added to twice.
                continue

            #: Make a list without shared outputs for value adding efficiency
            feature_name_order_values.append(layer_name)
            header_list.append(value_field)
            header_list.append(severity_field)

        output_rows.append(header_list)

        #: Add values
        for feature in TankResult.tank_results.values():
            temp_values = [feature.tank_id]

            for layer_name in feature_name_order_values:
                temp_values.append(feature.get_value_for_layer(layer_name))
                temp_values.append(feature.get_severity_for_layer(layer_name))

            output_rows.append(temp_values)

        return output_rows

    @staticmethod
    def update_tank_value_and_score(row, layer_name):
        '''Update TankResult for the tankId contained in row parameter.
        -The items contained in a row are dependent on the particular RiskFeature layerName and type.'''
        value = 0
        score = 0
        tank_id = row[0]

        #: Get the result object for the current tankId.
        if tank_id not in TankResult.tank_results:
            TankResult.tank_results[tank_id] = TankResult(tank_id)

        tank = TankResult.tank_results[tank_id]

        if layer_name == 'aquifer_recharge_discharge_areas':
            value = str(row[1])

            if value == 'Discharge':
                score = 1
            elif value == 'Secondary recharge':
                score = 2
            elif value == 'Primary recharge':
                score = 5
            else:
                #: 'Bedrock recharge'
                score = 0

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'wetlands':
            value, score = TankResult.in_polygon_value_and_score(row[1])

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'lakes_nhd':
            value = row[1]
            score = TankResult.distance_score(row[1])

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'streams_nhd':
            value = row[1]
            score = TankResult.distance_score(row[1])

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'dwq_assessment_units':
            status = str(row[1])
            value = status

            if status == 'Fully Supporting':
                score = 2
            elif status == 'Impaired' or status == 'Not Assessed':
                score = 5

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'soil':
            texture = row[1]
            value = texture.casefold()

            if value.contains('gravel'):
                score = 5
            elif value.contains('cobb'):
                score = 5
            elif value.contains('ston'):
                score = 5
            elif value.contains('frag'):
                score = 5
            elif value.contains('bould'):
                score = 5
            elif value.contains('course'):
                score = 5
            elif len(value.strip()) == 0:
                score = 5
            elif value.contains('sand'):
                score = 4
            elif value.contains('flag'):
                score = 3
            elif value.contains('channer'):
                score = 3
            elif value.contains('varia'):
                score = 3
            elif value == 'loam':
                score = 3
            elif value.contains('ashy'):
                score = 3
            elif value.contains('shaly'):
                score = 2
            elif value.contains('silt'):
                score = 2
            elif value.contains('plant'):
                score = 2
            elif value.contains('pea'):
                score = 2
            elif value.contains('clay'):
                score = 1
            elif value.contains('bedr'):
                score = 1
            else:
                score = 1000

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'shallow_ground_water':
            depth = row[1]
            value = depth

            if value == 0:
                score = 5
            elif value == 10:
                score = 2.5
            elif value == 30:
                score = 1
            else:
                score = 0

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'census_tracts_2020':
            value = float(row[1]) / float(row[2])<commit_msg>fix(risk): use python string contains syntax<commit_after>#!/usr/bin/env python
# * coding: utf8 *
'''
tank_risk.py
ArcGIS script tool for evaluating tank risk based on spatial relationships to other data.
'''
import csv
import os
import time
from pathlib import Path

import arcpy

VERSION = '2.1.2'


def format_time(seconds):
    '''seconds: number
    returns a human-friendly string describing the amount of time
    '''
    minute = 60.00
    hour = 60.00 * minute

    if seconds < 30:
        return f'{int(seconds * 1000)} ms\'s'

    if seconds < 90:
        return f'{round(seconds, 2)} seconds'

    if seconds < 90 * minute:
        return f'{round(seconds / minute, 2)} minutes'

    return f'{round(seconds / hour, 2)} hours'


class MapSource():
    '''Class for accessing map document information.'''

    def __init__(self, pro_project, map_name):
        project = arcpy.mp.ArcGISProject(pro_project)
        self.risk_map = project.listMaps(map_name)[0]

    def get_selected_layers(self, messages):
        layer_paths = []

        for layer in self.risk_map.listLayers():
            if not layer.visible:
                messages.AddWarningMessage(f'Skipping {layer.name}, reason visibility')

                continue

            if layer.supports('DATASOURCE'):
                layer_paths.append(layer)
            elif layer.supports('WORKSPACEPATH') and layer.supports('DATASETNAME'):
                layer_paths.append(layer)
            elif layer.supports('NAME'):
                messages.AddWarningMessage(f'Could not find workspace: {layer.name}')
            else:
                messages.AddWarningMessage(f'{layer.name} not supported')

        return layer_paths


class Outputs():
    '''Outputs stores all the directory and file name info.
    - setOutputDirectory initializes all attribute.'''
    unique_time_string = None

    output_directory = None
    output_gdb_name = None
    output_gdb = None
    output_table_name = None

    output_csv_name = None

    temp_dir = None
    temp_gdb_name = None
    temp_gdb = None

    @staticmethod
    def set_output_directory(output_dir):
        Outputs.unique_time_string = time.strftime('%Y%m%d%H%M%S')

        Outputs.output_directory = output_dir
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        Outputs.output_gdb_name = 'TankRisk_{}.gdb'.format(Outputs.unique_time_string)
        Outputs.output_gdb = os.path.join(Outputs.output_directory, Outputs.output_gdb_name)
        Outputs.output_table_name = 'TankRiskResults_{}'.format(Outputs.unique_time_string)

        Outputs.output_csv_name = 'TankRiskResults_{}.csv'.format(Outputs.unique_time_string)

        Outputs.temp_dir = 'in_memory'
        Outputs.temp_gdb_name = 'nearsTemp_{}.gdb'.format(Outputs.unique_time_string)
        Outputs.temp_gdb = 'in_memory'


class LayerAttributes():
    '''LayerAttributes stores risk feature information in a way that
    is easy to access.'''

    def __init__(self, risk_type, value_result_attribute, severity_result_attribute, value_field, severity_field, calc_fields=None, value_method=None):
        #: type of risk feature
        self.type = risk_type
        #: attribute for value result
        self.value_result_attribute = value_result_attribute
        #: attribute for severity result
        self.severity_result_attribute = severity_result_attribute
        #: Value field name in output table
        self.value_field = value_field
        #: Severity field name in output table
        self.severity_field = severity_field
        #: Fields for attribute type risk features
        self.calc_fields = calc_fields
        #: Method to produce value. Not yet used.
        self.value_method = value_method


class TankResult():
    '''Stores everything that is specific to each risk feature including:
    - Literals (field names)
    - Logic for scoring results for each risk feature
    - Result data for each risk feature'''
    tank_results = {}
    IN_POLYGON = 'inPolygon'
    DISTANCE = 'distance'
    ATTRIBUTE = 'attribute'
    OUTPUT_ID_FIELD = 'facilityid'

    #: attributesForFeature associates feature names with attributes
    attributes_for_feature = {
        'aquifer_recharge_discharge_areas': LayerAttributes(ATTRIBUTE, 'aquiferVal', 'aquiferSev', 'aquiferVal', 'aquiferSev', ['zone']),
        'wetlands': LayerAttributes(IN_POLYGON, 'wetLandsVal', 'wetLandsSev', 'wetLandsVal', 'wetLandsSev'),
        'lakes_nhd': LayerAttributes(DISTANCE, 'lakesVal', 'lakeSev', 'lakesVal', 'lakeSev'),
        'streams_nhd': LayerAttributes(DISTANCE, 'streamsVal', 'streamsSev', 'streamsVal', 'streamsSev'),
        'dwq_assessment_units': LayerAttributes(ATTRIBUTE, 'assessmentVal', 'assessmentSev', 'assessmentVal', 'assessmentSev', ['status2006']),
        'soil': LayerAttributes(ATTRIBUTE, 'soilVal', 'soilSev', 'soilVal', 'soilSev', ['musurftexgrp']),
        'shallow_ground_water': LayerAttributes(ATTRIBUTE, 'shallowWaterVal', 'shallowWaterSev', 'shallowWaterVal', 'shallowWaterSev', ['depth']),
        'census_tracts_2020': LayerAttributes(ATTRIBUTE, 'censusVal', 'censusSev', 'censusVal', 'censusSev', ['pop100', 'aland20']),
        'GroundWaterZones': LayerAttributes(ATTRIBUTE, 'udwspzVal', 'udwspzSev', 'udwspzVal', 'udwspzSev', ['protzone']),
        'SurfaceWaterZones': LayerAttributes(ATTRIBUTE, 'udwspzVal', 'udwspzSev', 'udwspzVal', 'udwspzSev', ['protzone']),
        'points_of_diversion': LayerAttributes(DISTANCE, 'podVal', 'podSev', 'podVal', 'podSev')
    }

    def __init__(self, tank_id):
        #: FacilityUST OBJECTID
        self.tank_id = tank_id
        #: Risk layer result attributes
        for layer_name in TankResult.attributes_for_feature:
            self.__dict__[TankResult.attributes_for_feature[layer_name].value_result_attribute] = None
            self.__dict__[TankResult.attributes_for_feature[layer_name].severity_result_attribute] = None

    def set_value_for_layer(self, layer_name, value):
        self.__dict__[TankResult.attributes_for_feature[layer_name].value_result_attribute] = value

    def get_value_for_layer(self, layer_name):
        return self.__dict__[TankResult.attributes_for_feature[layer_name].value_result_attribute]

    def set_severity_for_layer(self, layer_name, severity):
        self.__dict__[TankResult.attributes_for_feature[layer_name].severity_result_attribute] = severity

    def get_severity_for_layer(self, layer_name):
        return self.__dict__[TankResult.attributes_for_feature[layer_name].severity_result_attribute]

    @staticmethod
    def get_output_rows(feature_names):
        '''Get a list that contains the rows of the output table, including header.
        - feature_names are used to order output table fields.'''
        output_rows = []
        feature_name_order_header = list(feature_names)
        feature_name_order_values = []

        #: Add header
        header_list = [TankResult.OUTPUT_ID_FIELD]

        for layer_name in feature_name_order_header:
            value_field = TankResult.attributes_for_feature[layer_name].value_field
            severity_field = TankResult.attributes_for_feature[layer_name].severity_field

            if value_field in header_list or severity_field in header_list:
                #: Two layers can share one output field and the field doesn't need to be added to twice.
                continue

            #: Make a list without shared outputs for value adding efficiency
            feature_name_order_values.append(layer_name)
            header_list.append(value_field)
            header_list.append(severity_field)

        output_rows.append(header_list)

        #: Add values
        for feature in TankResult.tank_results.values():
            temp_values = [feature.tank_id]

            for layer_name in feature_name_order_values:
                temp_values.append(feature.get_value_for_layer(layer_name))
                temp_values.append(feature.get_severity_for_layer(layer_name))

            output_rows.append(temp_values)

        return output_rows

    @staticmethod
    def update_tank_value_and_score(row, layer_name):
        '''Update TankResult for the tankId contained in row parameter.
        -The items contained in a row are dependent on the particular RiskFeature layerName and type.'''
        value = 0
        score = 0
        tank_id = row[0]

        #: Get the result object for the current tankId.
        if tank_id not in TankResult.tank_results:
            TankResult.tank_results[tank_id] = TankResult(tank_id)

        tank = TankResult.tank_results[tank_id]

        if layer_name == 'aquifer_recharge_discharge_areas':
            value = str(row[1])

            if value == 'Discharge':
                score = 1
            elif value == 'Secondary recharge':
                score = 2
            elif value == 'Primary recharge':
                score = 5
            else:
                #: 'Bedrock recharge'
                score = 0

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'wetlands':
            value, score = TankResult.in_polygon_value_and_score(row[1])

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'lakes_nhd':
            value = row[1]
            score = TankResult.distance_score(row[1])

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'streams_nhd':
            value = row[1]
            score = TankResult.distance_score(row[1])

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'dwq_assessment_units':
            status = str(row[1])
            value = status

            if status == 'Fully Supporting':
                score = 2
            elif status == 'Impaired' or status == 'Not Assessed':
                score = 5

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'soil':
            texture = row[1]
            value = texture.casefold()

            if 'gravel' in value:
                score = 5
            elif 'cobb' in value:
                score = 5
            elif 'ston' in value:
                score = 5
            elif 'frag' in value:
                score = 5
            elif 'bould' in value:
                score = 5
            elif 'course' in value:
                score = 5
            elif len(value.strip()) == 0:
                score = 5
            elif 'sand' in value:
                score = 4
            elif 'flag' in value:
                score = 3
            elif 'channer' in value:
                score = 3
            elif 'varia' in value:
                score = 3
            elif value == 'loam':
                score = 3
            elif 'ashy' in value:
                score = 3
            elif 'shaly' in value:
                score = 2
            elif 'silt' in value:
                score = 2
            elif 'plant' in value:
                score = 2
            elif 'pea' in value:
                score = 2
            elif 'clay' in value:
                score = 1
            elif 'bedr' in value:
                score = 1
            else:
                score = 1000

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'shallow_ground_water':
            depth = row[1]
            value = depth

            if value == 0:
                score = 5
            elif value == 10:
                score = 2.5
            elif value == 30:
                score = 1
            else:
                score = 0

            tank.set_value_for_layer(layer_name, value)
            tank.set_severity_for_layer(layer_name, score)

        elif layer_name == 'census_tracts_2020':
            value = float(row[1]) / float(row[2])
{'fraction_non_alphanumeric': 0.0918384036637226, 'fraction_numerical': 0.007932613673536147, 'mean_word_length': 2.409591523769692, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2322312', 'n_tokens_mistral': 7498, 'n_tokens_neox': 6992, 'n_words': 1618}