<commit_before>"""
ArcGIS Tool classes for integrating the CEA with ArcGIS.
"""
import os
import arcpy
from cea import globalvar
import inputlocator
reload(inputlocator)

def add_message(msg, **kwargs):
    """Log to arcpy.AddMessage() instead of print to STDOUT"""
    arcpy.AddMessage(msg % kwargs)

class PropertiesTool(object):
    """
    integrate the properties script with ArcGIS.
    """
    def __init__(self):
        self.label = 'Properties'
        self.description = 'Query building properties from statistical database'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        prop_thermal_flag = arcpy.Parameter(
            displayName="Generate thermal properties of the building envelope",
            name="prop_thermal_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        prop_thermal_flag.value = True
        prop_architecture_flag = arcpy.Parameter(
            displayName="Generate construction and architecture properties",
            name="prop_architecture_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        prop_architecture_flag.value = True
        prop_HVAC_flag = arcpy.Parameter(
            displayName="Generate HVAC systems properties",
            name="prop_HVAC_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        prop_HVAC_flag.value = True
        return [scenario_path, prop_thermal_flag, prop_architecture_flag, prop_HVAC_flag]

    def execute(self, parameters, messages):
        from cea.properties import properties

        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)

        prop_thermal_flag = parameters[1]
        prop_architecture_flag = parameters[2]
        prop_HVAC_flag = parameters[3]
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        properties(locator=locator,
                   prop_thermal_flag=prop_thermal_flag.value,
                   prop_architecture_flag=prop_architecture_flag.value,
                   prop_hvac_flag=prop_HVAC_flag.value, gv=gv)

class DemandTool(object):
    """integrate the demand script with ArcGIS"""
    def __init__(self):
        self.label = 'Demand'
        self.description = 'Calculate the Demand'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")

        return [scenario_path]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        import cea.demand
        reload(cea.demand)

        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        cea.demand.demand_calculation(locator=locator, gv=gv)

class EmbodiedEnergyTool(object):

    def __init__(self):
        self.label = 'Embodied Energy'
        self.description = 'Calculate the Emissions for operation'
        self.canRunInBackground = False

    def getParameterInfo(self):
        yearcalc = arcpy.Parameter(
            displayName="Year to calculate",
            name="yearcalc",
            datatype="GPLong",
            parameterType="Required",
            direction="Input")

        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")

        return [yearcalc, scenario_path]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        from cea.embodied import lca_embodied

        yearcalc = int(parameters[0].valueAsText)
        scenario_path = parameters[1].valueAsText
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        locator = inputlocator.InputLocator(scenario_path=scenario_path)
        lca_embodied(yearcalc=yearcalc, locator=locator, gv=gv)

class EmissionsTool(object):

    def __init__(self):
        self.label = 'Emissions Operation'
        self.description = 'Calculate emissions and primary energy due to building operation'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        Qww_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to hot water consumption.",
            name="Qww_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qww_flag.value = True
        Qhs_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to space heating.",
            name="Qhs_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qhs_flag.value = True
        Qcs_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to space cooling.",
            name="Qcs_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qcs_flag.value = True
        Qcdata_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to servers cooling.",
            name="Qcdata_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qcdata_flag.value = True
        Qcrefri_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to refrigeration.",
            name="Qcrefri_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qcrefri_flag.value = True
        Eal_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to appliances and lighting.",
            name="Eal_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Eal_flag.value = True
        Eaux_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to auxiliary electricity.",
            name="Eaux_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Eaux_flag.value = True
        Epro_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to electricity in industrial processes.",
            name="Epro_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Epro_flag.value = True
        Edata_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to electricity consumption in data centers.",
            name="Edata_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Edata_flag.value = True

        return [scenario_path, Qww_flag, Qhs_flag, Qcs_flag, Qcdata_flag, Qcrefri_flag, Eal_flag, Eaux_flag, Epro_flag,
                Edata_flag]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        from cea.emissions import lca_operation
        import inputlocator
        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)
        Qww_flag = parameters[1].value
        Qhs_flag = parameters[2].value
        Qcs_flag = parameters[3].value
        Qcdata_flag = parameters[4].value
        Qcrefri_flag = parameters[5].value
        Eal_flag = parameters[6].value
        Eaux_flag = parameters[7].value
        Epro_flag = parameters[8].value
        Edata_flag = parameters[9].value
        lca_operation(locator=locator, Qww_flag=Qww_flag, Qhs_flag=Qhs_flag, Qcs_flag=Qcs_flag, Qcdata_flag=Qcdata_flag,
                      Qcrefri_flag=Qcrefri_flag, Eal_flag=Eal_flag, Eaux_flag=Eaux_flag, Epro_flag=Epro_flag,
                      Edata_flag=Edata_flag)

class GraphsDemandTool(object):

    def __init__(self):
        self.label = 'Demand graphs'
        self.description = 'Calculate Graphs of the Demand'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        analysis_fields = arcpy.Parameter(
            displayName="Variables to analyse",
            name="analysis_fields",
            datatype="String",
            parameterType="Required",
            multiValue=True,
            direction="Input")
        analysis_fields.filter.list = []
        return [scenario_path, analysis_fields]

    def updateParameters(self, parameters):
        import pandas as pd
        scenario_path = parameters[0].valueAsText
        if not os.path.exists(scenario_path):
            parameters[0].setErrorMessage('Scenario folder not found: %s' % scenario_path)
            return
        analysis_fields = parameters[1]
        locator = inputlocator.InputLocator(scenario_path)
        df_total_demand = pd.read_csv(locator.get_total_demand())
        first_building = df_total_demand['Name'][0]
        df_building = pd.read_csv(locator.get_demand_results_file(first_building))
        fields = set(df_building.columns.tolist())
        fields.remove('DATE')
        fields.remove('Name')
        analysis_fields.filter.list = list(fields)
        return

    def execute(self, parameters, messages):
        import cea.graphs
        reload(cea.graphs)
        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)
        analysis_fields = parameters[1].valueAsText.split(';')[:4]  # max 4 fields for analysis
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        cea.graphs.graphs_demand(locator=locator, analysis_fields=analysis_fields, gv=gv)

class HeatmapsTool(object):

    def __init__(self):
        self.label = 'Heatmaps'
        self.description = 'Create heatmap data layers'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        path_variables = arcpy.Parameter(
            displayName="Choose the file to analyse",
            name="path_variables",
            datatype="String",
            parameterType="Required",
            direction="Input")
        path_variables.filter.list = []
        analysis_fields = arcpy.Parameter(
            displayName="Variables to analyse",
            name="analysis_fields",
            datatype="String",
            parameterType="Required",
            multiValue=True,
            direction="Input")
        analysis_fields.filter.list = []
        analysis_fields.parameterDependencies = ['path_variables']

        return [scenario_path, path_variables, analysis_fields]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        # scenario_path
        scenario_path = parameters[0].valueAsText
        if not os.path.exists(scenario_path):
            parameters[0].setErrorMessage('Scenario folder not found: %s' % scenario_path)
            return
        # path_variables
        locator = inputlocator.InputLocator(scenario_path)
        file_names = [os.path.basename(locator.get_total_demand())]
        file_names.extend([f for f in os.listdir(locator.get_lca_emissions_results_folder()) if f.endswith('.csv')])
        path_variables = parameters[1]
        if not path_variables.value or path_variables.value not in file_names:
            path_variables.filter.list = file_names
            path_variables.value = file_names[0]
        # analysis_fields
        analysis_fields = parameters[2]
        if path_variables.value == file_names[0]:
            file_to_analyze = locator.get_total_demand()
        else:
            file_to_analyze = os.path.join(locator.get_lca_emissions_results_folder(), path_variables.value)
        import pandas as pd
        df = pd.read_csv(file_to_analyze)
        fields = df.columns.tolist()
        fields.remove('Name')
        analysis_fields.filter.list = list(fields)
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        scenario_path = parameters[0].valueAsText
        file_to_analyze = parameters[1].valueAsText
        analysis_fields = parameters[2].valueAsText.split(';')

        locator = inputlocator.InputLocator(scenario_path)
        if file_to_analyze == os.path.basename(locator.get_total_demand()):
            file_to_analyze = locator.get_total_demand()
            path_results = locator.get_heatmaps_demand_folder()
        else:
            file_to_analyze = os.path.join(locator.get_lca_emissions_results_folder(), file_to_analyze)
            path_results = locator.get_heatmaps_emission_folder()
        import cea.heatmaps
        reload(cea.heatmaps)
        cea.heatmaps.heatmaps(locator=locator, analysis_fields=analysis_fields,
                 path_results=path_results, file_to_analyze=file_to_analyze)
        return

class RadiationTool(object):
    def __init__(self):
        self.label = 'Radiation'
        self.description = 'Create radiation file'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        latitude = arcpy.Parameter(
            displayName="Latitude",
            name="latitude",
            datatype="GPDouble",
            parameterType="Required",
            direction="Input")
        longitude = arcpy.Parameter(
            displayName="Longitude",
            name="longitude",
            datatype="GPDouble",
            parameterType="Required",
            direction="Input")
        timezone = arcpy.Parameter(
            displayName="Timezone",
            name="timezone",
            datatype="GPLong",
            parameterType="Required",
            direction="Input")
        year = arcpy.Parameter(
            displayName="Year",
            name="year",
            datatype="GPLong",
            parameterType="Required",
            direction="Input")

        return [scenario_path, latitude, longitude, timezone, year]

    def execute(self, parameters, messages):
        scenario_path = parameters[0].valueAsText
        latitude = parameters[1].value
        longitude = parameters[2].value
        timezone = parameters[3].value
        year = parameters[4].value

        # FIXME: use current arcgis db...
        path_arcgis_db = os.path.expanduser(os.path.join('~', 'Documents', 'ArcGIS', 'Default.gdb'))

        locator = inputlocator.InputLocator(scenario_path)

        import cea.radiation
        reload(cea.radiation)
        cea.radiation.solar_radiation_vertical(locator=locator, path_arcgis_db=path_arcgis_db, latitude=latitude,
                                               longitude=longitude, timezone=timezone, year=year)
        return
<commit_msg>added automatic longitude / latitude discovery from shapefile<commit_after>"""
ArcGIS Tool classes for integrating the CEA with ArcGIS.
"""
import os
import arcpy
from cea import globalvar
import inputlocator
reload(inputlocator)

def add_message(msg, **kwargs):
    """Log to arcpy.AddMessage() instead of print to STDOUT"""
    arcpy.AddMessage(msg % kwargs)

class PropertiesTool(object):
    """
    integrate the properties script with ArcGIS.
    """
    def __init__(self):
        self.label = 'Properties'
        self.description = 'Query building properties from statistical database'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        prop_thermal_flag = arcpy.Parameter(
            displayName="Generate thermal properties of the building envelope",
            name="prop_thermal_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        prop_thermal_flag.value = True
        prop_architecture_flag = arcpy.Parameter(
            displayName="Generate construction and architecture properties",
            name="prop_architecture_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        prop_architecture_flag.value = True
        prop_HVAC_flag = arcpy.Parameter(
            displayName="Generate HVAC systems properties",
            name="prop_HVAC_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        prop_HVAC_flag.value = True
        return [scenario_path, prop_thermal_flag, prop_architecture_flag, prop_HVAC_flag]

    def execute(self, parameters, messages):
        from cea.properties import properties

        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)

        prop_thermal_flag = parameters[1]
        prop_architecture_flag = parameters[2]
        prop_HVAC_flag = parameters[3]
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        properties(locator=locator,
                   prop_thermal_flag=prop_thermal_flag.value,
                   prop_architecture_flag=prop_architecture_flag.value,
                   prop_hvac_flag=prop_HVAC_flag.value, gv=gv)

class DemandTool(object):
    """integrate the demand script with ArcGIS"""
    def __init__(self):
        self.label = 'Demand'
        self.description = 'Calculate the Demand'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")

        return [scenario_path]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        import cea.demand
        reload(cea.demand)

        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        cea.demand.demand_calculation(locator=locator, gv=gv)

class EmbodiedEnergyTool(object):

    def __init__(self):
        self.label = 'Embodied Energy'
        self.description = 'Calculate the Emissions for operation'
        self.canRunInBackground = False

    def getParameterInfo(self):
        yearcalc = arcpy.Parameter(
            displayName="Year to calculate",
            name="yearcalc",
            datatype="GPLong",
            parameterType="Required",
            direction="Input")

        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")

        return [yearcalc, scenario_path]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        from cea.embodied import lca_embodied

        yearcalc = int(parameters[0].valueAsText)
        scenario_path = parameters[1].valueAsText
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        locator = inputlocator.InputLocator(scenario_path=scenario_path)
        lca_embodied(yearcalc=yearcalc, locator=locator, gv=gv)

class EmissionsTool(object):

    def __init__(self):
        self.label = 'Emissions Operation'
        self.description = 'Calculate emissions and primary energy due to building operation'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        Qww_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to hot water consumption.",
            name="Qww_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qww_flag.value = True
        Qhs_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to space heating.",
            name="Qhs_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qhs_flag.value = True
        Qcs_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to space cooling.",
            name="Qcs_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qcs_flag.value = True
        Qcdata_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to servers cooling.",
            name="Qcdata_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qcdata_flag.value = True
        Qcrefri_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to refrigeration.",
            name="Qcrefri_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Qcrefri_flag.value = True
        Eal_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to appliances and lighting.",
            name="Eal_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Eal_flag.value = True
        Eaux_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to auxiliary electricity.",
            name="Eaux_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Eaux_flag.value = True
        Epro_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to electricity in industrial processes.",
            name="Epro_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Epro_flag.value = True
        Edata_flag = arcpy.Parameter(
            displayName="Create a separate file with emissions due to electricity consumption in data centers.",
            name="Edata_flag",
            datatype="GPBoolean",
            parameterType="Required",
            direction="Input")
        Edata_flag.value = True

        return [scenario_path, Qww_flag, Qhs_flag, Qcs_flag, Qcdata_flag, Qcrefri_flag, Eal_flag, Eaux_flag, Epro_flag,
                Edata_flag]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        from cea.emissions import lca_operation
        import inputlocator
        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)
        Qww_flag = parameters[1].value
        Qhs_flag = parameters[2].value
        Qcs_flag = parameters[3].value
        Qcdata_flag = parameters[4].value
        Qcrefri_flag = parameters[5].value
        Eal_flag = parameters[6].value
        Eaux_flag = parameters[7].value
        Epro_flag = parameters[8].value
        Edata_flag = parameters[9].value
        lca_operation(locator=locator, Qww_flag=Qww_flag, Qhs_flag=Qhs_flag, Qcs_flag=Qcs_flag, Qcdata_flag=Qcdata_flag,
                      Qcrefri_flag=Qcrefri_flag, Eal_flag=Eal_flag, Eaux_flag=Eaux_flag, Epro_flag=Epro_flag,
                      Edata_flag=Edata_flag)

class GraphsDemandTool(object):

    def __init__(self):
        self.label = 'Demand graphs'
        self.description = 'Calculate Graphs of the Demand'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        analysis_fields = arcpy.Parameter(
            displayName="Variables to analyse",
            name="analysis_fields",
            datatype="String",
            parameterType="Required",
            multiValue=True,
            direction="Input")
        analysis_fields.filter.list = []
        return [scenario_path, analysis_fields]

    def updateParameters(self, parameters):
        import pandas as pd
        scenario_path = parameters[0].valueAsText
        if not os.path.exists(scenario_path):
            parameters[0].setErrorMessage('Scenario folder not found: %s' % scenario_path)
            return
        analysis_fields = parameters[1]
        locator = inputlocator.InputLocator(scenario_path)
        df_total_demand = pd.read_csv(locator.get_total_demand())
        first_building = df_total_demand['Name'][0]
        df_building = pd.read_csv(locator.get_demand_results_file(first_building))
        fields = set(df_building.columns.tolist())
        fields.remove('DATE')
        fields.remove('Name')
        analysis_fields.filter.list = list(fields)
        return

    def execute(self, parameters, messages):
        import cea.graphs
        reload(cea.graphs)
        scenario_path = parameters[0].valueAsText
        locator = inputlocator.InputLocator(scenario_path)
        analysis_fields = parameters[1].valueAsText.split(';')[:4]  # max 4 fields for analysis
        gv = globalvar.GlobalVariables()
        gv.log = add_message
        cea.graphs.graphs_demand(locator=locator, analysis_fields=analysis_fields, gv=gv)

class HeatmapsTool(object):

    def __init__(self):
        self.label = 'Heatmaps'
        self.description = 'Create heatmap data layers'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        path_variables = arcpy.Parameter(
            displayName="Choose the file to analyse",
            name="path_variables",
            datatype="String",
            parameterType="Required",
            direction="Input")
        path_variables.filter.list = []
        analysis_fields = arcpy.Parameter(
            displayName="Variables to analyse",
            name="analysis_fields",
            datatype="String",
            parameterType="Required",
            multiValue=True,
            direction="Input")
        analysis_fields.filter.list = []
        analysis_fields.parameterDependencies = ['path_variables']

        return [scenario_path, path_variables, analysis_fields]

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        # scenario_path
        scenario_path = parameters[0].valueAsText
        if not os.path.exists(scenario_path):
            parameters[0].setErrorMessage('Scenario folder not found: %s' % scenario_path)
            return
        # path_variables
        locator = inputlocator.InputLocator(scenario_path)
        file_names = [os.path.basename(locator.get_total_demand())]
        file_names.extend([f for f in os.listdir(locator.get_lca_emissions_results_folder()) if f.endswith('.csv')])
        path_variables = parameters[1]
        if not path_variables.value or path_variables.value not in file_names:
            path_variables.filter.list = file_names
            path_variables.value = file_names[0]
        # analysis_fields
        analysis_fields = parameters[2]
        if path_variables.value == file_names[0]:
            file_to_analyze = locator.get_total_demand()
        else:
            file_to_analyze = os.path.join(locator.get_lca_emissions_results_folder(), path_variables.value)
        import pandas as pd
        df = pd.read_csv(file_to_analyze)
        fields = df.columns.tolist()
        fields.remove('Name')
        analysis_fields.filter.list = list(fields)
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
        scenario_path = parameters[0].valueAsText
        file_to_analyze = parameters[1].valueAsText
        analysis_fields = parameters[2].valueAsText.split(';')

        locator = inputlocator.InputLocator(scenario_path)
        if file_to_analyze == os.path.basename(locator.get_total_demand()):
            file_to_analyze = locator.get_total_demand()
            path_results = locator.get_heatmaps_demand_folder()
        else:
            file_to_analyze = os.path.join(locator.get_lca_emissions_results_folder(), file_to_analyze)
            path_results = locator.get_heatmaps_emission_folder()
        import cea.heatmaps
        reload(cea.heatmaps)
        cea.heatmaps.heatmaps(locator=locator, analysis_fields=analysis_fields,
                 path_results=path_results, file_to_analyze=file_to_analyze)
        return

class RadiationTool(object):
    def __init__(self):
        self.label = 'Radiation'
        self.description = 'Create radiation file'
        self.canRunInBackground = False

    def getParameterInfo(self):
        scenario_path = arcpy.Parameter(
            displayName="Path to the scenario",
            name="scenario_path",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        timezone = arcpy.Parameter(
            displayName="Timezone",
            name="timezone",
            datatype="GPLong",
            parameterType="Required",
            direction="Input")
        year = arcpy.Parameter(
            displayName="Year",
            name="year",
            datatype="GPLong",
            parameterType="Required",
            direction="Input")

        return [scenario_path, timezone, year]

    def execute(self, parameters, messages):
        scenario_path = parameters[0].valueAsText
        timezone = parameters[1].value
        year = parameters[2].value

        # FIXME: use current arcgis db...
        path_arcgis_db = os.path.expanduser(os.path.join('~', 'Documents', 'ArcGIS', 'Default.gdb'))

        locator = inputlocator.InputLocator(scenario_path)
        import fiona
        with fiona.open(locator.get_building_geometry()) as shp:
            longitude = shp.crs['lon_0']
            latitude = shp.crs['lat_0']
        arcpy.AddMessage('longitude: %s' % longitude)
        arcpy.AddMessage('latitude: %s' % latitude)

        import cea.radiation
        reload(cea.radiation)
        cea.radiation.solar_radiation_vertical(locator=locator, path_arcgis_db=path_arcgis_db, latitude=latitude,
                                               longitude=longitude, timezone=timezone, year=year)
        return
<|endoftext|>


<commit_before>import warnings
from functools import wraps

try:
    from functools import lru_cache
except ImportError:
    from backports.functools_lru_cache import lru_cache

import pkg_resources
__version__ = pkg_resources.require("census")[0].version

ALL = '*'

def new_session(*args, **kwargs):
    import requests
    return requests.session(*args, **kwargs)

class APIKeyError(Exception):
    """ Invalid API key
    """

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)

def list_or_str(v):
    """ Convert a single value into a list.
    """
    if isinstance(v, (list, tuple)):
        return v
    return [v]

def supported_years(*years):
    def inner(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            year = kwargs.get('year', self.default_year)
            _years = years if years else self.years
            if int(year) not in _years:
                raise UnsupportedYearException(
                    'Geography is not available in {}. Available years include {}'.format(year, _years))
            return func(self, *args, **kwargs)
        return wrapper
    return inner

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

def merge(dicts):
    return dict(item for d in dicts for item in d.items())

class CensusException(Exception):
    pass

class UnsupportedYearException(CensusException):
    pass

class Client(object):
    endpoint_url = 'https://api.census.gov/data/%s/%s'
    definitions_url = 'https://api.census.gov/data/%s/%s/variables.json'
    definition_url = 'https://api.census.gov/data/%s/%s/variables/%s.json'

    def __init__(self, key, year=None, session=None):
        self._key = key
        self.session = session or new_session()
        if year:
            self.default_year = year

    @supported_years()
    def fields(self, year=None, flat=False):
        if year is None:
            year = self.default_year

        data = {}

        fields_url = self.definitions_url % (year, self.dataset)

        resp = self.session.get(fields_url)
        obj = resp.json()

        if flat:

            for key, elem in obj['variables'].items():
                if key in ['for', 'in']:
                    continue
                data[key] = "{}: {}".format(elem['concept'], elem['label'])

        else:

            data = obj['variables']
            if 'for' in data:
                data.pop("for", None)
            if 'in' in data:
                data.pop("in", None)

        return data

    def get(self, fields, geo, year=None, **kwargs):
        all_results = (self.query(fifty_fields, geo, year, **kwargs)
                       for fifty_fields in chunks(fields, 50))
        merged_results = [merge(result) for result in zip(*all_results)]

        return merged_results

    def query(self, fields, geo, year=None, **kwargs):

        if year is None:
            year = self.default_year

        fields = list_or_str(fields)

        url = self.endpoint_url % (year, self.dataset)

        params = {
            'get': ",".join(fields),
            'for': geo['for'],
            'key': self._key,
        }

        if 'in' in geo:
            params['in'] = geo['in']

        resp = self.session.get(url, params=params)

        if resp.status_code == 200:
            try:
                data = resp.json()
            except ValueError as ex:
                if '<title>Invalid Key</title>' in resp.text:
                    raise APIKeyError(' '.join(resp.text.splitlines()))
                else:
                    raise ex

            headers = data.pop(0)
            types = [self._field_type(header, year) for header in headers]

            results = []
            for d in data:
                dd = {}
                for header, cast, item in zip(headers, types, d):
                    try:
                        dd[header] = cast(item) if item is not None else None
                    except ValueError as e:
                        dd[header] = item

                results.append(dd)
            return results

        elif resp.status_code == 204:
            return []

        else:
            raise CensusException(resp.text)

    @lru_cache(maxsize=1024)
    def _field_type(self, field, year):
        url = self.definition_url % (year, self.dataset, field)
        resp = self.session.get(url)

        types = {"fips-for" : str,
                 "fips-in" : str,
                 "int" : int,
                 "string": str}

        if resp.status_code == 200:
            predicate_type = resp.json().get("predicateType", "string")
            return types[predicate_type]
        else:
            return str

    @supported_years()
    def us(self, fields, **kwargs):
        return self.get(fields, geo={'for': 'us:1'}, **kwargs)

    @supported_years()
    def state(self, fields, state_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years()
    def state_county(self, fields, state_fips, county_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county:{}'.format(county_fips),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years()
    def state_place(self, fields, state_fips, place, **kwargs):
        return self.get(fields, geo={
            'for': 'place:{}'.format(place),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years()
    def state_district(self, fields, state_fips, district, **kwargs):
        return self.get(fields, geo={
            'for': 'congressional district:{}'.format(district),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

class ACS5Client(Client):

    default_year = 2015
    dataset = 'acs5'

    years = (2015, 2014, 2013, 2012, 2011, 2010, 2009)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_tract(self, fields, state_fips,
                           county_fips, tract, **kwargs):
        return self.get(fields, geo={
            'for': 'tract:{}'.format(tract),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_blockgroup(self, fields, state_fips, county_fips,
                                blockgroup, tract=None, **kwargs):
        geo = {
            'for': 'block group:{}'.format(blockgroup),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }
        if tract:
            geo['in'] += ' tract:{}'.format(tract)
        return self.get(fields, geo=geo, **kwargs)

    @supported_years(2015, 2014, 2013, 2012, 2011)
    def zipcode(self, fields, zcta, **kwargs):
        return self.get(fields, geo={
            'for': 'zip code tabulation area:{}'.format(zcta),
        }, **kwargs)

class ACS5DpClient(ACS5Client):

    dataset = 'acs5/profile'

    years = (2015, 2014, 2013, 2012)

class ACS3Client(Client):

    default_year = 2013
    dataset = 'acs3'

    years = (2013, 2012)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

class ACS3DpClient(ACS3Client):

    dataset = 'acs3/profile'

class ACS1Client(Client):

    default_year = 2015
    dataset = 'acs1'

    years = (2015, 2014, 2013, 2012, 2011)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

class ACS1DpClient(ACS1Client):

    dataset = 'acs1/profile'

    years = (2015, 2014, 2013, 2012)

class SF1Client(Client):

    default_year = 2010
    dataset = 'sf1'

    years = (2010, 2000, 1990)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_tract(self, fields, state_fips,
                           county_fips, tract, **kwargs):
        return self.get(fields, geo={
            'for': 'tract:{}'.format(tract),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_blockgroup(self, fields, state_fips, county_fips,
                                blockgroup, tract=None, **kwargs):
        geo = {
            'for': 'block group:{}'.format(blockgroup),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }
        if tract:
            geo['in'] += ' tract:{}'.format(tract)
        return self.get(fields, geo=geo, **kwargs)

    @supported_years(2010)
    def state_msa(self, fields, state_fips, msa, **kwargs):
        return self.get(fields, geo={
            'for': ('metropolitan statistical area/' +
                    'micropolitan statistical area:{}'.format(msa)),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years(2010)
    def state_csa(self, fields, state_fips, csa, **kwargs):
        return self.get(fields, geo={
            'for': 'combined statistical area:{}'.format(csa),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years(2010)
    def state_district_place(self, fields, state_fips,
                             district, place, **kwargs):
        return self.get(fields, geo={
            'for': 'place:{}'.format(place),
            'in': 'state:{} congressional district:{}'.format(
                state_fips, district),
        }, **kwargs)

    @supported_years(2010)
    def state_zipcode(self, fields, state_fips, zcta, **kwargs):
        return self.get(fields, geo={
            'for': 'zip code tabulation area:{}'.format(zcta),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

class SF3Client(Client):

    default_year = 2000
    dataset = 'sf3'

    years = (2000, 1990)

    @supported_years()
    def state_county_tract(self, fields, state_fips,
                           county_fips, tract, **kwargs):
        return self.get(fields, geo={
            'for': 'tract:{}'.format(tract),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_blockgroup(self, fields, state_fips, county_fips,
                                blockgroup, tract=None, **kwargs):
        geo = {
            'for': 'block group:{}'.format(blockgroup),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }
        if tract:
            geo['in'] += ' tract:{}'.format(tract)
        return self.get(fields, geo=geo, **kwargs)

class Census(object):

    ALL = ALL

    def __init__(self, key, year=None, session=None):

        if not session:
            session = new_session()

        self.session = session
        self.session.headers.update({
            'User-Agent': ('python-census/{} '.format(__version__) +
                           'github.com/datamade/census')
        })

        self._acs = ACS5Client(key, year, session)  # deprecated
        self.acs5 = ACS5Client(key, year, session)
        self.acs3 = ACS3Client(key, year, session)
        self.acs1 = ACS1Client(key, year, session)
        self.acs5dp = ACS5DpClient(key, year, session)
        self.acs3dp = ACS3DpClient(key, year, session)
        self.acs1dp = ACS1DpClient(key, year, session)
        self.sf1 = SF1Client(key, year, session)
        self.sf3 = SF3Client(key, year, session)

    @property
    def acs(self):
        warnings.warn('Use acs5 instead of acs', DeprecationWarning)
        return self._acs
<commit_msg>Revert "handle casting exceptions caused by type mismatches returned from API endpoint"<commit_after>import warnings
from functools import wraps

try:
    from functools import lru_cache
except ImportError:
    from backports.functools_lru_cache import lru_cache

import pkg_resources
__version__ = pkg_resources.require("census")[0].version

ALL = '*'

def new_session(*args, **kwargs):
    import requests
    return requests.session(*args, **kwargs)

class APIKeyError(Exception):
    """ Invalid API key
    """

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)

def list_or_str(v):
    """ Convert a single value into a list.
    """
    if isinstance(v, (list, tuple)):
        return v
    return [v]

def supported_years(*years):
    def inner(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            year = kwargs.get('year', self.default_year)
            _years = years if years else self.years
            if int(year) not in _years:
                raise UnsupportedYearException(
                    'Geography is not available in {}. Available years include {}'.format(year, _years))
            return func(self, *args, **kwargs)
        return wrapper
    return inner

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

def merge(dicts):
    return dict(item for d in dicts for item in d.items())

class CensusException(Exception):
    pass

class UnsupportedYearException(CensusException):
    pass

class Client(object):
    endpoint_url = 'https://api.census.gov/data/%s/%s'
    definitions_url = 'https://api.census.gov/data/%s/%s/variables.json'
    definition_url = 'https://api.census.gov/data/%s/%s/variables/%s.json'

    def __init__(self, key, year=None, session=None):
        self._key = key
        self.session = session or new_session()
        if year:
            self.default_year = year

    @supported_years()
    def fields(self, year=None, flat=False):
        if year is None:
            year = self.default_year

        data = {}

        fields_url = self.definitions_url % (year, self.dataset)

        resp = self.session.get(fields_url)
        obj = resp.json()

        if flat:

            for key, elem in obj['variables'].items():
                if key in ['for', 'in']:
                    continue
                data[key] = "{}: {}".format(elem['concept'], elem['label'])

        else:

            data = obj['variables']
            if 'for' in data:
                data.pop("for", None)
            if 'in' in data:
                data.pop("in", None)

        return data

    def get(self, fields, geo, year=None, **kwargs):
        all_results = (self.query(fifty_fields, geo, year, **kwargs)
                       for fifty_fields in chunks(fields, 50))
        merged_results = [merge(result) for result in zip(*all_results)]

        return merged_results

    def query(self, fields, geo, year=None, **kwargs):

        if year is None:
            year = self.default_year

        fields = list_or_str(fields)

        url = self.endpoint_url % (year, self.dataset)

        params = {
            'get': ",".join(fields),
            'for': geo['for'],
            'key': self._key,
        }

        if 'in' in geo:
            params['in'] = geo['in']

        resp = self.session.get(url, params=params)

        if resp.status_code == 200:
            try:
                data = resp.json()
            except ValueError as ex:
                if '<title>Invalid Key</title>' in resp.text:
                    raise APIKeyError(' '.join(resp.text.splitlines()))
                else:
                    raise ex

            headers = data.pop(0)
            types = [self._field_type(header, year) for header in headers]
            results = [{header : (cast(item) if item is not None else None)
                        for header, cast, item
                        in zip(headers, types, d)}
                       for d in data]
            return results

        elif resp.status_code == 204:
            return []

        else:
            raise CensusException(resp.text)

    @lru_cache(maxsize=1024)
    def _field_type(self, field, year):
        url = self.definition_url % (year, self.dataset, field)
        resp = self.session.get(url)

        types = {"fips-for" : str,
                 "fips-in" : str,
                 "int" : int,
                 "string": str}

        if resp.status_code == 200:
            predicate_type = resp.json().get("predicateType", "string")
            return types[predicate_type]
        else:
            return str

    @supported_years()
    def us(self, fields, **kwargs):
        return self.get(fields, geo={'for': 'us:1'}, **kwargs)

    @supported_years()
    def state(self, fields, state_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years()
    def state_county(self, fields, state_fips, county_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county:{}'.format(county_fips),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years()
    def state_place(self, fields, state_fips, place, **kwargs):
        return self.get(fields, geo={
            'for': 'place:{}'.format(place),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years()
    def state_district(self, fields, state_fips, district, **kwargs):
        return self.get(fields, geo={
            'for': 'congressional district:{}'.format(district),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

class ACS5Client(Client):

    default_year = 2015
    dataset = 'acs5'

    years = (2015, 2014, 2013, 2012, 2011, 2010, 2009)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_tract(self, fields, state_fips,
                           county_fips, tract, **kwargs):
        return self.get(fields, geo={
            'for': 'tract:{}'.format(tract),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_blockgroup(self, fields, state_fips, county_fips,
                                blockgroup, tract=None, **kwargs):
        geo = {
            'for': 'block group:{}'.format(blockgroup),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }
        if tract:
            geo['in'] += ' tract:{}'.format(tract)
        return self.get(fields, geo=geo, **kwargs)

    @supported_years(2015, 2014, 2013, 2012, 2011)
    def zipcode(self, fields, zcta, **kwargs):
        return self.get(fields, geo={
            'for': 'zip code tabulation area:{}'.format(zcta),
        }, **kwargs)

class ACS5DpClient(ACS5Client):

    dataset = 'acs5/profile'

    years = (2015, 2014, 2013, 2012)

class ACS3Client(Client):

    default_year = 2013
    dataset = 'acs3'

    years = (2013, 2012)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

class ACS3DpClient(ACS3Client):

    dataset = 'acs3/profile'

class ACS1Client(Client):

    default_year = 2015
    dataset = 'acs1'

    years = (2015, 2014, 2013, 2012, 2011)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

class ACS1DpClient(ACS1Client):

    dataset = 'acs1/profile'

    years = (2015, 2014, 2013, 2012)

class SF1Client(Client):

    default_year = 2010
    dataset = 'sf1'

    years = (2010, 2000, 1990)

    @supported_years()
    def state_county_subdivision(self, fields, state_fips,
                                 county_fips, subdiv_fips, **kwargs):
        return self.get(fields, geo={
            'for': 'county subdivision:{}'.format(subdiv_fips),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_tract(self, fields, state_fips,
                           county_fips, tract, **kwargs):
        return self.get(fields, geo={
            'for': 'tract:{}'.format(tract),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_blockgroup(self, fields, state_fips, county_fips,
                                blockgroup, tract=None, **kwargs):
        geo = {
            'for': 'block group:{}'.format(blockgroup),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }
        if tract:
            geo['in'] += ' tract:{}'.format(tract)
        return self.get(fields, geo=geo, **kwargs)

    @supported_years(2010)
    def state_msa(self, fields, state_fips, msa, **kwargs):
        return self.get(fields, geo={
            'for': ('metropolitan statistical area/' +
                    'micropolitan statistical area:{}'.format(msa)),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years(2010)
    def state_csa(self, fields, state_fips, csa, **kwargs):
        return self.get(fields, geo={
            'for': 'combined statistical area:{}'.format(csa),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

    @supported_years(2010)
    def state_district_place(self, fields, state_fips,
                             district, place, **kwargs):
        return self.get(fields, geo={
            'for': 'place:{}'.format(place),
            'in': 'state:{} congressional district:{}'.format(
                state_fips, district),
        }, **kwargs)

    @supported_years(2010)
    def state_zipcode(self, fields, state_fips, zcta, **kwargs):
        return self.get(fields, geo={
            'for': 'zip code tabulation area:{}'.format(zcta),
            'in': 'state:{}'.format(state_fips),
        }, **kwargs)

class SF3Client(Client):

    default_year = 2000
    dataset = 'sf3'

    years = (2000, 1990)

    @supported_years()
    def state_county_tract(self, fields, state_fips,
                           county_fips, tract, **kwargs):
        return self.get(fields, geo={
            'for': 'tract:{}'.format(tract),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }, **kwargs)

    @supported_years()
    def state_county_blockgroup(self, fields, state_fips, county_fips,
                                blockgroup, tract=None, **kwargs):
        geo = {
            'for': 'block group:{}'.format(blockgroup),
            'in': 'state:{} county:{}'.format(state_fips, county_fips),
        }
        if tract:
            geo['in'] += ' tract:{}'.format(tract)
        return self.get(fields, geo=geo, **kwargs)

class Census(object):

    ALL = ALL

    def __init__(self, key, year=None, session=None):

        if not session:
            session = new_session()

        self.session = session
        self.session.headers.update({
            'User-Agent': ('python-census/{} '.format(__version__) +
                           'github.com/datamade/census')
        })

        self._acs = ACS5Client(key, year, session)  # deprecated
        self.acs5 = ACS5Client(key, year, session)
        self.acs3 = ACS3Client(key, year, session)
        self.acs1 = ACS1Client(key, year, session)
        self.acs5dp = ACS5DpClient(key, year, session)
        self.acs3dp = ACS3DpClient(key, year, session)
        self.acs1dp = ACS1DpClient(key, year, session)
        self.sf1 = SF1Client(key, year, session)
        self.sf3 = SF3Client(key, year, session)

    @property
    def acs(self):
        warnings.warn('Use acs5 instead of acs', DeprecationWarning)
        return self._acs
<|endoftext|>


<commit_before>"""Chalice app and routing code."""
import re
import sys
import logging
import json
import traceback
import decimal
from collections import Mapping

# Implementation note:  This file is intended to be a standalone file
# that gets copied into the lambda deployment package.  It has no dependencies
# on other parts of chalice so it can stay small and lightweight, with minimal
# startup overhead.

_PARAMS = re.compile('{\w+}')

def handle_decimals(obj):
    # Lambda will automatically serialize decimals so we need
    # to support that as well.
    if isinstance(obj, decimal.Decimal):
        return float(obj)
    return obj

def error_response(message, error_code, http_status_code):
    body = {'Code': error_code, 'Message': message}
    response = Response(body=body, status_code=http_status_code)
    return response.to_dict()

class ChaliceError(Exception):
    pass

class ChaliceViewError(ChaliceError):
    STATUS_CODE = 500

    def __init__(self, msg=''):
        super(ChaliceViewError, self).__init__(
            self.__class__.__name__ + ': %s' % msg)

class BadRequestError(ChaliceViewError):
    STATUS_CODE = 400

class UnauthorizedError(ChaliceViewError):
    STATUS_CODE = 401

class ForbiddenError(ChaliceViewError):
    STATUS_CODE = 403

class NotFoundError(ChaliceViewError):
    STATUS_CODE = 404

class MethodNotAllowedError(ChaliceViewError):
    STATUS_CODE = 405

class ConflictError(ChaliceViewError):
    STATUS_CODE = 409

class TooManyRequestsError(ChaliceViewError):
    STATUS_CODE = 429

ALL_ERRORS = [
    ChaliceViewError,
    BadRequestError,
    NotFoundError,
    UnauthorizedError,
    ForbiddenError,
    ConflictError,
    TooManyRequestsError]

class CaseInsensitiveMapping(Mapping):
    """Case insensitive and read-only mapping."""

    def __init__(self, mapping):
        mapping = mapping or {}
        self._dict = {k.lower(): v for k, v in mapping.items()}

    def __getitem__(self, key):
        return self._dict[key.lower()]

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)

    def __repr__(self):
        return 'CaseInsensitiveMapping(%s)' % repr(self._dict)

class Authorizer(object):
    name = ''

    def to_swagger(self):
        raise NotImplementedError("to_swagger")

class CognitoUserPoolAuthorizer(object):

    _AUTH_TYPE = 'cognito_user_pools'

    def __init__(self, name, header, provider_arns):
        self.name = name
        self._header = header
        self._provider_arns = provider_arns

    def to_swagger(self):
        return {
            'in': 'header',
            'type': 'apiKey',
            'name': self._header,
            'x-amazon-apigateway-authtype': self._AUTH_TYPE,
            'x-amazon-apigateway-authorizer': {
                'type': self._AUTH_TYPE,
                'providerARNs': self._provider_arns,
            }
        }

class CustomAuthorizer(object):

    _AUTH_TYPE = 'custom'

    def __init__(self, name, header, authorizer_uri, ttl_seconds=300):
        self.name = name
        self._header = header
        self._authorizer_uri = authorizer_uri
        self._ttl_seconds = ttl_seconds

    def to_swagger(self):
        return {
            'in': 'header',
            'type': 'apiKey',
            'name': self._header,
            'x-amazon-apigateway-authtype': self._AUTH_TYPE,
            'x-amazon-apigateway-authorizer': {
                'type': 'token',
                'authorizerUri': self._authorizer_uri,
                'authorizerResultTtlInSeconds': self._ttl_seconds,
            }
        }

class CORSConfig(object):
    """A cors configuration to attach to a route."""

    _REQUIRED_HEADERS = ['Content-Type', 'X-Amz-Date', 'Authorization',
                         'X-Api-Key', 'X-Amz-Security-Token']

    def __init__(self, allow_origin='*', allow_headers=None,
                 expose_headers=None, max_age=None, allow_credentials=None):
        self.allow_origin = allow_origin

        if allow_headers is None:
            allow_headers = set(self._REQUIRED_HEADERS)
        else:
            allow_headers = set(allow_headers + self._REQUIRED_HEADERS)
        self._allow_headers = allow_headers

        if expose_headers is None:
            expose_headers = []
        self._expose_headers = expose_headers

        self._max_age = max_age
        self._allow_credentials = allow_credentials

    @property
    def allow_headers(self):
        return ','.join(sorted(self._allow_headers))

    def get_access_control_headers(self):
        headers = {
            'Access-Control-Allow-Origin': self.allow_origin,
            'Access-Control-Allow-Headers': self.allow_headers
        }
        if self._expose_headers:
            headers.update({
                'Access-Control-Expose-Headers': ','.join(self._expose_headers)
            })
        if self._max_age is not None:
            headers.update({
                'Access-Control-Max-Age': str(self._max_age)
            })
        if self._allow_credentials is True:
            headers.update({
                'Access-Control-Allow-Credentials': 'true'
            })

        return headers

class Request(object):
    """The current request from API gateway."""

    def __init__(self, query_params, headers, uri_params, method, body,
                 context, stage_vars):
        self.query_params = query_params
        self.headers = CaseInsensitiveMapping(headers)
        self.uri_params = uri_params
        self.method = method
        self.raw_body = body
        #: The parsed JSON from the body.  This value should
        #: only be set if the Content-Type header is application/json,
        #: which is the default content type value in chalice.
        self._json_body = None
        self.context = context
        self.stage_vars = stage_vars

    @property
    def json_body(self):
        if self.headers.get('content-type', '').startswith('application/json'):
            if self._json_body is None:
                self._json_body = json.loads(self.raw_body)
            return self._json_body

    def to_dict(self):
        copied = self.__dict__.copy()
        # We want the output of `to_dict()` to be
        # JSON serializable, so we need to remove the CaseInsensitive dict.
        copied['headers'] = dict(copied['headers'])
        return copied

class Response(object):
    def __init__(self, body, headers=None, status_code=200):
        self.body = body
        if headers is None:
            headers = {}
        self.headers = headers
        self.status_code = status_code

    def to_dict(self):
        body = self.body
        if not isinstance(body, str):
            body = json.dumps(body, default=handle_decimals)
        return {
            'headers': self.headers,
            'statusCode': self.status_code,
            'body': body,
        }

class RouteEntry(object):

    def __init__(self, view_function, view_name, path, methods,
                 authorizer_name=None,
                 api_key_required=None, content_types=None,
                 cors=False, authorizer=None):
        self.view_function = view_function
        self.view_name = view_name
        self.uri_pattern = path
        self.methods = methods
        self.authorizer_name = authorizer_name
        self.api_key_required = api_key_required
        #: A list of names to extract from path:
        #: e.g, '/foo/{bar}/{baz}/qux -> ['bar', 'baz']
        self.view_args = self._parse_view_args()
        self.content_types = content_types
        # cors is passed as either a boolean or a CORSConfig object. If it is a
        # boolean it needs to be replaced with a real CORSConfig object to
        # pass the typechecker. None in this context will not inject any cors
        # headers, otherwise the CORSConfig object will determine which
        # headers are injected.
        if cors is True:
            cors = CORSConfig()
        elif cors is False:
            cors = None
        self.cors = cors
        self.authorizer = authorizer

    def _parse_view_args(self):
        if '{' not in self.uri_pattern:
            return []
        # The [1:-1] slice is to remove the braces
        # e.g {foobar} -> foobar
        results = [r[1:-1] for r in _PARAMS.findall(self.uri_pattern)]
        return results

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

class Chalice(object):

    FORMAT_STRING = '%(name)s - %(levelname)s - %(message)s'

    def __init__(self, app_name, configure_logs=True):
        self.app_name = app_name
        self.routes = {}
        self.current_request = None
        self.debug = False
        self.configure_logs = configure_logs
        self.log = logging.getLogger(self.app_name)
        self._authorizers = {}
        if self.configure_logs:
            self._configure_logging()

    def _configure_logging(self):
        log = logging.getLogger(self.app_name)
        if self._already_configured(log):
            return
        handler = logging.StreamHandler(sys.stdout)
        # Timestamp is handled by lambda itself so the
        # default FORMAT_STRING doesn't need to include it.
        formatter = logging.Formatter(self.FORMAT_STRING)
        handler.setFormatter(formatter)
        log.propagate = False
        if self.debug:
            level = logging.DEBUG
        else:
            level = logging.ERROR
        log.setLevel(level)
        log.addHandler(handler)

    def _already_configured(self, log):
        if not log.handlers:
            return False
        for handler in log.handlers:
            if isinstance(handler, logging.StreamHandler):
                if handler.stream == sys.stdout:
                    return True
        return False

    @property
    def authorizers(self):
        return self._authorizers.copy()

    def define_authorizer(self, name, header, auth_type, provider_arns=None):
        self._authorizers[name] = {
            'header': header,
            'auth_type': auth_type,
            'provider_arns': provider_arns,
        }

    def route(self, path, **kwargs):
        def _register_view(view_func):
            self._add_route(path, view_func, **kwargs)
            return view_func
        return _register_view

    def _add_route(self, path, view_func, **kwargs):
        name = kwargs.pop('name', view_func.__name__)
        methods = kwargs.pop('methods', ['GET'])
        authorizer_name = kwargs.pop('authorizer_name', None)
        authorizer = kwargs.pop('authorizer', None)
        api_key_required = kwargs.pop('api_key_required', None)
        content_types = kwargs.pop('content_types', ['application/json'])
        cors = kwargs.pop('cors', False)
        if not isinstance(content_types, list):
            raise ValueError('In view function "%s", the content_types '
                             'value must be a list, not %s: %s'
                             % (name, type(content_types), content_types))
        if kwargs:
            raise TypeError('TypeError: route() got unexpected keyword '
                            'arguments: %s' % ', '.join(list(kwargs)))

        if path in self.routes:
            raise ValueError(
                "Duplicate route detected: '%s'\n"
                "URL paths must be unique." % path)
        entry = RouteEntry(view_func, name, path, methods,
                           authorizer_name, api_key_required,
                           content_types, cors, authorizer)
        self.routes[path] = entry

    def __call__(self, event, context):
        # This is what's invoked via lambda.
        # Sometimes the event can be something that's not
        # what we specified in our request_template mapping.
        # When that happens, we want to give a better error message here.
        resource_path = event.get('requestContext', {}).get('resourcePath')
        if resource_path is None:
            return error_response(error_code='InternalServerError',
                                  message='Unknown request.',
                                  http_status_code=500)
        http_method = event['requestContext']['httpMethod']
        if resource_path not in self.routes:
            raise ChaliceError("No view function for: %s" % resource_path)
        route_entry = self.routes[resource_path]
        if http_method not in route_entry.methods:
            return error_response(
                error_code='MethodNotAllowedError',
                message='Unsupported method: %s' % http_method,
                http_status_code=405)
        view_function = route_entry.view_function
        function_args = [event['pathParameters'][name]
                         for name in route_entry.view_args]
        self.current_request = Request(event['queryStringParameters'],
                                       event['headers'],
                                       event['pathParameters'],
                                       event['requestContext']['httpMethod'],
                                       event['body'],
                                       event['requestContext'],
                                       event['stageVariables'])
        # We're doing the header validation after creating the request
        # so can leverage the case insensitive dict that the Request class
        # uses for headers.
        if route_entry.content_types:
            content_type = self.current_request.headers.get(
                'content-type', 'application/json')
            if not self._matches_content_type(content_type,
                                              route_entry.content_types):
                return error_response(
                    error_code='UnsupportedMediaType',
                    message='Unsupported media type: %s' % content_type,
                    http_status_code=415,
                )
        response = self._get_view_function_response(view_function,
                                                    function_args)
        if self._cors_enabled_for_route(route_entry):
            self._add_cors_headers(response, route_entry.cors)
        return response.to_dict()

    def _matches_content_type(self, content_type, valid_content_types):
        if ';' in content_type:
            content_type = content_type.split(';', 1)[0].strip()
        return content_type in valid_content_types

    def _get_view_function_response(self, view_function, function_args):
        try:
            response = self._invoke_view_function(view_function, function_args)
        except ChaliceViewError as e:
            # Any chalice view error should propagate.  These
            # get mapped to various HTTP status codes in API Gateway.
            response = Response(body={'Code': e.__class__.__name__,
                                      'Message': str(e)},
                                status_code=e.STATUS_CODE)
        except Exception as e:
            headers = {}
            if self.debug:
                # If the user has turned on debug mode,
                # we'll let the original exception propogate so
                # they get more information about what went wrong.
                self.log.debug("Caught exception for %s", view_function,
                               exc_info=True)
                stack_trace = ''.join(traceback.format_exc())
                body = stack_trace
                headers['Content-Type'] = 'text/plain'
            else:
                body = {'Code': 'InternalServerError',
                        'Message': 'An internal server error occurred.'}
            response = Response(body=body, headers=headers, status_code=500)
        if not isinstance(response, Response):
            response = Response(body=response)
        self._validate_response(response)
        return response

    def _invoke_view_function(self, view_function, function_args):
        response = view_function(*function_args)
        self._validate_response(response)
        return response

    def _validate_response(self, response):
        if isinstance(response, Response):
            for header, value in response.headers.items():
                if '\n' in value:
                    raise ChaliceError("Bad value for header '%s': %r" %
                                       (header, value))

    def _cors_enabled_for_route(self, route_entry):
        return route_entry.cors is not None

    def _add_cors_headers(self, response, cors):
        for name, value in cors.get_access_control_headers().items():
            if name not in response.headers:
                response.headers[name] = value
<commit_msg>Add pending deprecation warning<commit_after>"""Chalice app and routing code."""
import re
import sys
import logging
import json
import traceback
import decimal
import warnings
from collections import Mapping

# Implementation note:  This file is intended to be a standalone file
# that gets copied into the lambda deployment package.  It has no dependencies
# on other parts of chalice so it can stay small and lightweight, with minimal
# startup overhead.

_PARAMS = re.compile('{\w+}')

def handle_decimals(obj):
    # Lambda will automatically serialize decimals so we need
    # to support that as well.
    if isinstance(obj, decimal.Decimal):
        return float(obj)
    return obj

def error_response(message, error_code, http_status_code):
    body = {'Code': error_code, 'Message': message}
    response = Response(body=body, status_code=http_status_code)
    return response.to_dict()

class ChaliceError(Exception):
    pass

class ChaliceViewError(ChaliceError):
    STATUS_CODE = 500

    def __init__(self, msg=''):
        super(ChaliceViewError, self).__init__(
            self.__class__.__name__ + ': %s' % msg)

class BadRequestError(ChaliceViewError):
    STATUS_CODE = 400

class UnauthorizedError(ChaliceViewError):
    STATUS_CODE = 401

class ForbiddenError(ChaliceViewError):
    STATUS_CODE = 403

class NotFoundError(ChaliceViewError):
    STATUS_CODE = 404

class MethodNotAllowedError(ChaliceViewError):
    STATUS_CODE = 405

class ConflictError(ChaliceViewError):
    STATUS_CODE = 409

class TooManyRequestsError(ChaliceViewError):
    STATUS_CODE = 429

ALL_ERRORS = [
    ChaliceViewError,
    BadRequestError,
    NotFoundError,
    UnauthorizedError,
    ForbiddenError,
    ConflictError,
    TooManyRequestsError]

class CaseInsensitiveMapping(Mapping):
    """Case insensitive and read-only mapping."""

    def __init__(self, mapping):
        mapping = mapping or {}
        self._dict = {k.lower(): v for k, v in mapping.items()}

    def __getitem__(self, key):
        return self._dict[key.lower()]

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)

    def __repr__(self):
        return 'CaseInsensitiveMapping(%s)' % repr(self._dict)

class Authorizer(object):
    name = ''

    def to_swagger(self):
        raise NotImplementedError("to_swagger")

class CognitoUserPoolAuthorizer(object):

    _AUTH_TYPE = 'cognito_user_pools'

    def __init__(self, name, header, provider_arns):
        self.name = name
        self._header = header
        self._provider_arns = provider_arns

    def to_swagger(self):
        return {
            'in': 'header',
            'type': 'apiKey',
            'name': self._header,
            'x-amazon-apigateway-authtype': self._AUTH_TYPE,
            'x-amazon-apigateway-authorizer': {
                'type': self._AUTH_TYPE,
                'providerARNs': self._provider_arns,
            }
        }

class CustomAuthorizer(object):

    _AUTH_TYPE = 'custom'

    def __init__(self, name, header, authorizer_uri, ttl_seconds=300):
        self.name = name
        self._header = header
        self._authorizer_uri = authorizer_uri
        self._ttl_seconds = ttl_seconds

    def to_swagger(self):
        return {
            'in': 'header',
            'type': 'apiKey',
            'name': self._header,
            'x-amazon-apigateway-authtype': self._AUTH_TYPE,
            'x-amazon-apigateway-authorizer': {
                'type': 'token',
                'authorizerUri': self._authorizer_uri,
                'authorizerResultTtlInSeconds': self._ttl_seconds,
            }
        }

class CORSConfig(object):
    """A cors configuration to attach to a route."""

    _REQUIRED_HEADERS = ['Content-Type', 'X-Amz-Date', 'Authorization',
                         'X-Api-Key', 'X-Amz-Security-Token']

    def __init__(self, allow_origin='*', allow_headers=None,
                 expose_headers=None, max_age=None, allow_credentials=None):
        self.allow_origin = allow_origin

        if allow_headers is None:
            allow_headers = set(self._REQUIRED_HEADERS)
        else:
            allow_headers = set(allow_headers + self._REQUIRED_HEADERS)
        self._allow_headers = allow_headers

        if expose_headers is None:
            expose_headers = []
        self._expose_headers = expose_headers

        self._max_age = max_age
        self._allow_credentials = allow_credentials

    @property
    def allow_headers(self):
        return ','.join(sorted(self._allow_headers))

    def get_access_control_headers(self):
        headers = {
            'Access-Control-Allow-Origin': self.allow_origin,
            'Access-Control-Allow-Headers': self.allow_headers
        }
        if self._expose_headers:
            headers.update({
                'Access-Control-Expose-Headers': ','.join(self._expose_headers)
            })
        if self._max_age is not None:
            headers.update({
                'Access-Control-Max-Age': str(self._max_age)
            })
        if self._allow_credentials is True:
            headers.update({
                'Access-Control-Allow-Credentials': 'true'
            })

        return headers

class Request(object):
    """The current request from API gateway."""

    def __init__(self, query_params, headers, uri_params, method, body,
                 context, stage_vars):
        self.query_params = query_params
        self.headers = CaseInsensitiveMapping(headers)
        self.uri_params = uri_params
        self.method = method
        self.raw_body = body
        #: The parsed JSON from the body.  This value should
        #: only be set if the Content-Type header is application/json,
        #: which is the default content type value in chalice.
        self._json_body = None
        self.context = context
        self.stage_vars = stage_vars

    @property
    def json_body(self):
        if self.headers.get('content-type', '').startswith('application/json'):
            if self._json_body is None:
                self._json_body = json.loads(self.raw_body)
            return self._json_body

    def to_dict(self):
        copied = self.__dict__.copy()
        # We want the output of `to_dict()` to be
        # JSON serializable, so we need to remove the CaseInsensitive dict.
        copied['headers'] = dict(copied['headers'])
        return copied

class Response(object):
    def __init__(self, body, headers=None, status_code=200):
        self.body = body
        if headers is None:
            headers = {}
        self.headers = headers
        self.status_code = status_code

    def to_dict(self):
        body = self.body
        if not isinstance(body, str):
            body = json.dumps(body, default=handle_decimals)
        return {
            'headers': self.headers,
            'statusCode': self.status_code,
            'body': body,
        }

class RouteEntry(object):

    def __init__(self, view_function, view_name, path, methods,
                 authorizer_name=None,
                 api_key_required=None, content_types=None,
                 cors=False, authorizer=None):
        self.view_function = view_function
        self.view_name = view_name
        self.uri_pattern = path
        self.methods = methods
        self.authorizer_name = authorizer_name
        self.api_key_required = api_key_required
        #: A list of names to extract from path:
        #: e.g, '/foo/{bar}/{baz}/qux -> ['bar', 'baz']
        self.view_args = self._parse_view_args()
        self.content_types = content_types
        # cors is passed as either a boolean or a CORSConfig object. If it is a
        # boolean it needs to be replaced with a real CORSConfig object to
        # pass the typechecker. None in this context will not inject any cors
        # headers, otherwise the CORSConfig object will determine which
        # headers are injected.
        if cors is True:
            cors = CORSConfig()
        elif cors is False:
            cors = None
        self.cors = cors
        self.authorizer = authorizer

    def _parse_view_args(self):
        if '{' not in self.uri_pattern:
            return []
        # The [1:-1] slice is to remove the braces
        # e.g {foobar} -> foobar
        results = [r[1:-1] for r in _PARAMS.findall(self.uri_pattern)]
        return results

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

class Chalice(object):

    FORMAT_STRING = '%(name)s - %(levelname)s - %(message)s'

    def __init__(self, app_name, configure_logs=True):
        self.app_name = app_name
        self.routes = {}
        self.current_request = None
        self.debug = False
        self.configure_logs = configure_logs
        self.log = logging.getLogger(self.app_name)
        self._authorizers = {}
        if self.configure_logs:
            self._configure_logging()

    def _configure_logging(self):
        log = logging.getLogger(self.app_name)
        if self._already_configured(log):
            return
        handler = logging.StreamHandler(sys.stdout)
        # Timestamp is handled by lambda itself so the
        # default FORMAT_STRING doesn't need to include it.
        formatter = logging.Formatter(self.FORMAT_STRING)
        handler.setFormatter(formatter)
        log.propagate = False
        if self.debug:
            level = logging.DEBUG
        else:
            level = logging.ERROR
        log.setLevel(level)
        log.addHandler(handler)

    def _already_configured(self, log):
        if not log.handlers:
            return False
        for handler in log.handlers:
            if isinstance(handler, logging.StreamHandler):
                if handler.stream == sys.stdout:
                    return True
        return False

    @property
    def authorizers(self):
        return self._authorizers.copy()

    def define_authorizer(self, name, header, auth_type, provider_arns=None):
        warnings.warn(
            "define_authorizer() is deprecated and will be removed in future "
            "versions of chalice.  Please use CognitoUserPoolAuthorizer(...) "
            "instead", PendingDeprecationWarning)
        self._authorizers[name] = {
            'header': header,
            'auth_type': auth_type,
            'provider_arns': provider_arns,
        }

    def route(self, path, **kwargs):
        def _register_view(view_func):
            self._add_route(path, view_func, **kwargs)
            return view_func
        return _register_view

    def _add_route(self, path, view_func, **kwargs):
        name = kwargs.pop('name', view_func.__name__)
        methods = kwargs.pop('methods', ['GET'])
        authorizer_name = kwargs.pop('authorizer_name', None)
        authorizer = kwargs.pop('authorizer', None)
        api_key_required = kwargs.pop('api_key_required', None)
        content_types = kwargs.pop('content_types', ['application/json'])
        cors = kwargs.pop('cors', False)
        if not isinstance(content_types, list):
            raise ValueError('In view function "%s", the content_types '
                             'value must be a list, not %s: %s'
                             % (name, type(content_types), content_types))
        if kwargs:
            raise TypeError('TypeError: route() got unexpected keyword '
                            'arguments: %s' % ', '.join(list(kwargs)))

        if path in self.routes:
            raise ValueError(
                "Duplicate route detected: '%s'\n"
                "URL paths must be unique." % path)
        entry = RouteEntry(view_func, name, path, methods,
                           authorizer_name, api_key_required,
                           content_types, cors, authorizer)
        self.routes[path] = entry

    def __call__(self, event, context):
        # This is what's invoked via lambda.
        # Sometimes the event can be something that's not
        # what we specified in our request_template mapping.
        # When that happens, we want to give a better error message here.
        resource_path = event.get('requestContext', {}).get('resourcePath')
        if resource_path is None:
            return error_response(error_code='InternalServerError',
                                  message='Unknown request.',
                                  http_status_code=500)
        http_method = event['requestContext']['httpMethod']
        if resource_path not in self.routes:
            raise ChaliceError("No view function for: %s" % resource_path)
        route_entry = self.routes[resource_path]
        if http_method not in route_entry.methods:
            return error_response(
                error_code='MethodNotAllowedError',
                message='Unsupported method: %s' % http_method,
                http_status_code=405)
        view_function = route_entry.view_function
        function_args = [event['pathParameters'][name]
                         for name in route_entry.view_args]
        self.current_request = Request(event['queryStringParameters'],
                                       event['headers'],
                                       event['pathParameters'],
                                       event['requestContext']['httpMethod'],
                                       event['body'],
                                       event['requestContext'],
                                       event['stageVariables'])
        # We're doing the header validation after creating the request
        # so can leverage the case insensitive dict that the Request class
        # uses for headers.
        if route_entry.content_types:
            content_type = self.current_request.headers.get(
                'content-type', 'application/json')
            if not self._matches_content_type(content_type,
                                              route_entry.content_types):
                return error_response(
                    error_code='UnsupportedMediaType',
                    message='Unsupported media type: %s' % content_type,
                    http_status_code=415,
                )
        response = self._get_view_function_response(view_function,
                                                    function_args)
        if self._cors_enabled_for_route(route_entry):
            self._add_cors_headers(response, route_entry.cors)
        return response.to_dict()

    def _matches_content_type(self, content_type, valid_content_types):
        if ';' in content_type:
            content_type = content_type.split(';', 1)[0].strip()
        return content_type in valid_content_types

    def _get_view_function_response(self, view_function, function_args):
        try:
            response = self._invoke_view_function(view_function, function_args)
        except ChaliceViewError as e:
            # Any chalice view error should propagate.  These
            # get mapped to various HTTP status codes in API Gateway.
            response = Response(body={'Code': e.__class__.__name__,
                                      'Message': str(e)},
                                status_code=e.STATUS_CODE)
        except Exception as e:
            headers = {}
            if self.debug:
                # If the user has turned on debug mode,
                # we'll let the original exception propogate so
                # they get more information about what went wrong.
                self.log.debug("Caught exception for %s", view_function,
                               exc_info=True)
                stack_trace = ''.join(traceback.format_exc())
                body = stack_trace
                headers['Content-Type'] = 'text/plain'
            else:
                body = {'Code': 'InternalServerError',
                        'Message': 'An internal server error occurred.'}
            response = Response(body=body, headers=headers, status_code=500)
        if not isinstance(response, Response):
            response = Response(body=response)
        self._validate_response(response)
        return response

    def _invoke_view_function(self, view_function, function_args):
        response = view_function(*function_args)
        self._validate_response(response)
        return response

    def _validate_response(self, response):
        if isinstance(response, Response):
            for header, value in response.headers.items():
                if '\n' in value:
                    raise ChaliceError("Bad value for header '%s': %r" %
                                       (header, value))

    def _cors_enabled_for_route(self, route_entry):
        return route_entry.cors is not None

    def _add_cors_headers(self, response, cors):
        for name, value in cors.get_access_control_headers().items():
            if name not in response.headers:
                response.headers[name] = value
<|endoftext|>


<commit_before>from rpython.rlib import streamio, objectmodel
from rpython.rlib.rstruct.runpack import runpack
from spyvm.util import system

if not objectmodel.we_are_translated():
    def runpack(fmt, arg):
        from struct import unpack
        return unpack(fmt, arg)[0]
def chrs2int(b):
    assert len(b) == 4
    return runpack('>i', b)

def swapped_chrs2int(b):
    assert len(b) == 4
    return runpack('<i', b)

def chrs2long(b):
    assert len(b) == 8
    return runpack('>q', b)

def swapped_chrs2long(b):
    assert len(b) == 8
    return runpack('<q', b)

class Stream(object):
    """ Simple input stream.
    Data is completely read into memory.
    Constructor can raise OSError. """

    def __init__(self, filename=None, inputfile=None, data=None):
        if filename:
            f = streamio.open_file_as_stream(filename, mode="rb", buffering=0)
            try:
                self.data = f.readall()
            finally:
                f.close()
        elif inputfile:
            try:
                self.data = inputfile.read()
            finally:
                inputfile.close()
        elif data:
            self.data = data
        else:
            raise RuntimeError("need to supply either inputfile or data")

        self.reset()

    def bytes2dword_with_correct_endianness(self, bytes):
        if self.big_endian:
            return runpack(">I", bytes)
        else:
            return runpack("<I", bytes)

    def bytes2qword_with_correct_endianness(self, bytes):
        if self.big_endian:
            return runpack(">Q", bytes)
        else:
            return runpack("<Q", bytes)

    def peek_bytes(self, n):
        return self.data[self.pos:self.pos+n]

    def next_bytes(self, n):
        bytes = self.peek_bytes(n)
        self.pos += n
        self.count += n
        return bytes

    def peek(self):
        if self.pos >= len(self.data):
            raise IndexError
        data_peek = self.data[self.pos:self.pos + self.word_size]
        if self.use_long_read:
            assert system.IS_64BIT, "do not support reading 64 bit slots in 32 bit build"
            if self.big_endian:
                return chrs2long(data_peek)
            else:
                return swapped_chrs2long(data_peek)
        else:
            if self.big_endian:
                return chrs2int(data_peek)
            else:
                return swapped_chrs2int(data_peek)

    def next(self):
        integer = self.peek()
        self.pos += self.word_size
        self.count += self.word_size
        return integer

    def next_short(self):
        short = self.peek() >> (self.word_size * 8 - 16)
        self.pos += 2
        self.count += 2
        return short

    def next_qword(self):
        bytes = self.next_bytes(8)
        qword = self.bytes2qword_with_correct_endianness(bytes)
        return qword

    def reset(self):
        self.big_endian = True
        self.pos = 0
        self.count = 0
        self.be_32bit()

    def reset_count(self):
        self.count = 0

    def skipbytes(self, jump):
        assert jump > 0
        assert (self.pos + jump) <= len(self.data)
        self.pos += jump
        self.count += jump

    def skipwords(self, jump):
        self.skipbytes(jump * self.word_size)
        assert (self.pos + jump) <= len(self.data)
        self.pos += jump
        self.count += jump

    def length(self):
        return len(self.data)

    def close(self):
        pass # already closed

    def be_64bit(self):
        self.word_size = 8
        self.use_long_read = True

    def be_32bit(self):
        self.word_size = 4
        self.use_long_read = False
<commit_msg>Fix last commit<commit_after>from rpython.rlib import streamio, objectmodel
from rpython.rlib.rstruct.runpack import runpack as rlib_runpack
from spyvm.util import system

def runpack(fmt, arg):
    if objectmodel.we_are_translated():
        return rlib_runpack(fmt, arg)
    else:
        from struct import unpack
        return unpack(fmt, arg)[0]
runpack._annspecialcase_ = 'specialize:arg(0)'

def chrs2int(b):
    assert len(b) == 4
    return runpack('>i', b)

def swapped_chrs2int(b):
    assert len(b) == 4
    return runpack('<i', b)

def chrs2long(b):
    assert len(b) == 8
    return runpack('>q', b)

def swapped_chrs2long(b):
    assert len(b) == 8
    return runpack('<q', b)

class Stream(object):
    """ Simple input stream.
    Data is completely read into memory.
    Constructor can raise OSError. """

    def __init__(self, filename=None, inputfile=None, data=None):
        if filename:
            f = streamio.open_file_as_stream(filename, mode="rb", buffering=0)
            try:
                self.data = f.readall()
            finally:
                f.close()
        elif inputfile:
            try:
                self.data = inputfile.read()
            finally:
                inputfile.close()
        elif data:
            self.data = data
        else:
            raise RuntimeError("need to supply either inputfile or data")

        self.reset()

    def bytes2dword_with_correct_endianness(self, bytes):
        if self.big_endian:
            return runpack(">I", bytes)
        else:
            return runpack("<I", bytes)

    def bytes2qword_with_correct_endianness(self, bytes):
        if self.big_endian:
            return runpack(">Q", bytes)
        else:
            return runpack("<Q", bytes)

    def peek_bytes(self, n):
        return self.data[self.pos:self.pos+n]

    def next_bytes(self, n):
        bytes = self.peek_bytes(n)
        self.pos += n
        self.count += n
        return bytes

    def peek(self):
        if self.pos >= len(self.data):
            raise IndexError
        data_peek = self.data[self.pos:self.pos + self.word_size]
        if self.use_long_read:
            assert system.IS_64BIT, "do not support reading 64 bit slots in 32 bit build"
            if self.big_endian:
                return chrs2long(data_peek)
            else:
                return swapped_chrs2long(data_peek)
        else:
            if self.big_endian:
                return chrs2int(data_peek)
            else:
                return swapped_chrs2int(data_peek)

    def next(self):
        integer = self.peek()
        self.pos += self.word_size
        self.count += self.word_size
        return integer

    def next_short(self):
        short = self.peek() >> (self.word_size * 8 - 16)
        self.pos += 2
        self.count += 2
        return short

    def next_qword(self):
        bytes = self.next_bytes(8)
        qword = self.bytes2qword_with_correct_endianness(bytes)
        return qword

    def reset(self):
        self.big_endian = True
        self.pos = 0
        self.count = 0
        self.be_32bit()

    def reset_count(self):
        self.count = 0

    def skipbytes(self, jump):
        assert jump > 0
        assert (self.pos + jump) <= len(self.data)
        self.pos += jump
        self.count += jump

    def skipwords(self, jump):
        self.skipbytes(jump * self.word_size)
        assert (self.pos + jump) <= len(self.data)
        self.pos += jump
        self.count += jump

    def length(self):
        return len(self.data)

    def close(self):
        pass # already closed

    def be_64bit(self):
        self.word_size = 8
        self.use_long_read = True

    def be_32bit(self):
        self.word_size = 4
        self.use_long_read = False
<|endoftext|>