Merge branch 'main' of https://github.com/ahlusar1989/justice40-tool into ahlusar1989-main

This commit is contained in:
Saran Ahluwalia 2021-12-16 06:21:15 -05:00
commit 971ac0640c
133 changed files with 6432 additions and 11782 deletions

8
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/**"
schedule:
interval: "daily"
allow:
- dependency-type: "production"

View file

@ -4,8 +4,3 @@ Feature: All links on the dataset cards should be functional
Given I am on the "Methodology" page
When I look for the "Datasets used in methodology" CTA
Then All links under "Datasets used in methodology" should work
Scenario: If I click on any link in the additional indicators dataset, they should work
Given I am on the "Methodology" page
When I look for the "Additional Indicators" CTA
Then All links under "Additional Indicators" should work

View file

@ -1,30 +1,19 @@
@use '../../styles/design-system.scss' as *;
@import "../utils.scss";
$sidePanelLabelFontColor: #171716;
@mixin sidePanelLabelStyle {
font-size: small;
font-size: medium;
color: $sidePanelLabelFontColor;
font-weight: 600;
}
@mixin categorizationCircleStyle {
height: 0.6rem;
width: 0.6rem;
border-radius: 100%;
align-self: center;
margin-top: 2rem;
margin-right: 0.5rem;
opacity: 0.6;
}
@mixin indicatorBox {
display: flex;
flex-direction: column;
padding: 0 1rem 1.5rem;
&:last-child {
border-bottom: none;
}
.versionInfo {
padding: .5rem 1rem .5rem 1.2rem;
font-size: medium;
font-weight: bold;
border-bottom: 1px solid $sidePanelBorderColor;
}
.areaDetailContainer {
@ -39,14 +28,25 @@ $sidePanelLabelFontColor: #171716;
align-items: center;
padding-bottom: 2rem;
.isInFocus {
padding: .5rem 1rem .25rem 1.2rem;
font-size: small;
font-weight: bold;
@include u-margin-top(2);
}
.communityOfFocus {
display: flex;
.communityOfFocusCircle {
@include categorizationCircleStyle;
background: #1a4480;
h3 {
margin-top: 0;
margin-bottom: 0;
}
}
.feedbackLink {
font-size: small;
@include u-margin-top(1);
}
}
@ -64,12 +64,10 @@ $sidePanelLabelFontColor: #171716;
}
.censusText {
font-size: small;
font-size: medium;
}
}
//Divider styles
.divider {
@include sidePanelLabelStyle;
@ -79,45 +77,3 @@ $sidePanelLabelFontColor: #171716;
border-top: 1px solid $sidePanelBorderColor;
border-bottom: 1px solid $sidePanelBorderColor;
}
//Indicator box styles
.indicatorBoxMain {
@include indicatorBox;
border-bottom: $sidePanelBorder;
}
.indicatorBoxAdditional {
@include indicatorBox;
border-bottom: 1px solid #1b1b1b;
background-color: $additionalCardsBGColor;
}
.indicatorRow {
display: flex;
@media screen and (max-width: $mobileBreakpoint) {
flex: 1 0 40%;
align-self: inherit;
padding-left: 3rem;
padding-top: 1rem;
}
.indicatorName {
flex: 0 1 77%;
}
.indicatorValue {
margin-top: 1.2rem;
margin-left: 2.2rem;
.indicatorSuperscript {
top: -0.2em
}
}
.indicatorDesc {
max-width: 10rem;
}
}

View file

@ -8,13 +8,9 @@ declare namespace MapModuleScssNamespace {
censusLabel:string;
censusText: string;
divider:string;
indicatorBoxMain:string;
indicatorBoxAdditional:string;
indicatorRow:string;
indicatorName:string;
indicatorValue:string;
indicatorSuperscript:string;
indicatorDesc:string;
feedbackLink:string;
isInFocus:string;
versionInfo: string;
}
}

View file

@ -1,46 +1,32 @@
/* eslint-disable quotes */
// External Libs:
import React, {useEffect} from 'react';
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {Accordion} from '@trussworks/react-uswds';
// Components:
// import {Accordion} from '@trussworks/react-uswds';
import Category from '../Category';
import DisadvantageDot from '../DisadvantageDot';
import Indicator from '../Indicator';
// Styles and constants
import * as styles from './areaDetail.module.scss';
import * as constants from '../../data/constants';
import * as EXPLORE_COPY from '../../data/copy/explore';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
export const readablePercentile = (percentile: number | null) => {
return percentile ? Math.round(percentile * 100) : 'N/A';
};
// Todo: Add internationalization to superscript ticket #582
const getSuperscriptOrdinal = (percentile: number | string) => {
if (typeof percentile === "number") {
const englishOrdinalRules = new Intl.PluralRules('en', {
type: 'ordinal',
});
const suffixes = {
zero: 'th',
one: 'st',
two: 'nd',
few: 'rd',
many: 'th',
other: 'th',
};
return suffixes[englishOrdinalRules.select(percentile)];
}
};
interface IAreaDetailProps {
properties: constants.J40Properties,
}
export interface indicatorInfo {
label: string,
description: string,
value: number,
isDisadvagtaged: boolean,
}
const AreaDetail = ({properties}:IAreaDetailProps) => {
const intl = useIntl();
const [isCommunityFocus, setIsCommunityFocus] = React.useState<boolean>(true);
console.log("Area Detail properies: ", properties);
@ -50,122 +36,298 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
const countyName = properties[constants.COUNTY_NAME] ? properties[constants.COUNTY_NAME] : "N/A";
const stateName = properties[constants.STATE_NAME] ? properties[constants.STATE_NAME] : "N/A";
useEffect(() => {
if (score >= constants.SCORE_BOUNDARY_PRIORITIZED ) {
setIsCommunityFocus(true);
} else {
setIsCommunityFocus(false);
}
}, [score]);
const isCommunityFocus = score >= constants.SCORE_BOUNDARY_PRIORITIZED;
// const sidePanelFeedbackHref = `
// mailto:screeningtool.feedback@usds.gov?subject=Feedback on Census Tract: ${blockGroup}
// `;
interface indicatorInfo {
label: string,
description: string,
value: number,
}
// Define each indicator in the side panel with constants from copy file (for intl)
// Indicators are grouped by category
const expAgLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_AG_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_AG_LOSS),
value: properties[constants.EXP_AGRICULTURE_LOSS_PERCENTILE] ?
properties[constants.EXP_AGRICULTURE_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME] : null,
};
const expBldLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_BLD_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_BLD_LOSS),
value: properties[constants.EXP_BUILDING_LOSS_PERCENTILE] ?
properties[constants.EXP_BUILDING_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME] : null,
};
const expPopLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_POP_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_POP_LOSS),
value: properties[constants.EXP_POPULATION_LOSS_PERCENTILE] ?
properties[constants.EXP_POPULATION_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME] : null,
};
const lowInc:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LOW_INCOME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_INCOME),
value: properties[constants.POVERTY_BELOW_200_PERCENTILE] ?
properties[constants.POVERTY_BELOW_200_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_FEDERAL_POVERTY_LEVEL_200] ?
properties[constants.IS_FEDERAL_POVERTY_LEVEL_200] : null,
};
// Todo: Ticket #367 will be replacing descriptions with YAML file
const areaMedianIncome:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.AREA_MEDIAN_INCOME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.AREA_MEDIAN_INCOME),
value: properties[constants.AREA_MEDIAN_INCOME_PERCENTILE] ?
properties[constants.AREA_MEDIAN_INCOME_PERCENTILE] : null,
};
const eduInfo:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EDUCATION),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EDUCATION),
value: properties[constants.EDUCATION_PROPERTY_PERCENTILE] ?
properties[constants.EDUCATION_PROPERTY_PERCENTILE] : null,
};
const poverty:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.POVERTY),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.POVERTY),
value: properties[constants.POVERTY_PROPERTY_PERCENTILE] ?
properties[constants.POVERTY_PROPERTY_PERCENTILE] : null,
};
const asthma:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.ASTHMA),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ASTHMA),
value: properties[constants.ASTHMA_PERCENTILE] ?
properties[constants.ASTHMA_PERCENTILE] : null,
};
const diabetes:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIABETES),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIABETES),
value: properties[constants.DIABETES_PERCENTILE] ?
properties[constants.DIABETES_PERCENTILE] : null,
};
const dieselPartMatter:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIESEL_PARTICULATE_MATTER),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIESEL_PARTICULATE_MATTER),
value: properties[constants.DIESEL_MATTER_PERCENTILE] ?
properties[constants.DIESEL_MATTER_PERCENTILE] : null,
};
const lifeExpect:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LIFE_EXPECT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LIFE_EXPECT),
value: properties[constants.LIFE_PERCENTILE] ?
properties[constants.LIFE_PERCENTILE] : null,
};
const energyBurden:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.ENERGY_BURDEN),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ENERGY_BURDEN),
value: properties[constants.ENERGY_PERCENTILE] ?
properties[constants.ENERGY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME] : null,
};
const pm25:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PM_2_5),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PM_2_5),
value: properties[constants.PM25_PERCENTILE] ?
properties[constants.PM25_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_PM25_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_PM25_AND_IS_LOW_INCOME] : null,
};
const leadPaint:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LEAD_PAINT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LEAD_PAINT),
value: properties[constants.LEAD_PAINT_PERCENTILE] ?
properties[constants.LEAD_PAINT_PERCENTILE] : null,
const dieselPartMatter:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIESEL_PARTICULATE_MATTER),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIESEL_PARTICULATE_MATTER),
value: properties[constants.DIESEL_MATTER_PERCENTILE] ?
properties[constants.DIESEL_MATTER_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME] : null,
};
const trafficVolume:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.TRAFFIC_VOLUME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.TRAFFIC_VOLUME),
value: properties[constants.TRAFFIC_PERCENTILE] ?
properties[constants.TRAFFIC_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME] : null,
};
const houseBurden:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HOUSE_BURDEN),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HOUSE_BURDEN),
value: properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] ?
properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME] : null,
};
const leadPaint:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LEAD_PAINT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LEAD_PAINT),
value: properties[constants.LEAD_PAINT_PERCENTILE] ?
properties[constants.LEAD_PAINT_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME] : null,
};
// const medHomeVal:indicatorInfo = {
// label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.MED_HOME_VAL),
// description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.MED_HOME_VAL),
// value: properties[constants.MEDIAN_HOME_VALUE_PERCENTILE] ?
// properties[constants.MEDIAN_HOME_VALUE_PERCENTILE] : null,
// isDisadvagtaged: false, // TODO
// };
const proxHaz:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_HAZ),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_HAZ),
value: properties[constants.PROXIMITY_TSDF_SITES_PERCENTILE] ?
properties[constants.PROXIMITY_TSDF_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME] : null,
};
const proxNPL:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_NPL),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_NPL),
value: properties[constants.PROXIMITY_NPL_SITES_PERCENTILE] ?
properties[constants.PROXIMITY_NPL_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME] : null,
};
const proxRMP:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_RMP),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_RMP),
value: properties[constants.PROXIMITY_RMP_SITES_PERCENTILE] ?
properties[constants.PROXIMITY_RMP_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_RMP_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_RMP_AND_IS_LOW_INCOME] : null,
};
const wasteWater:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.WASTE_WATER),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.WASTE_WATER),
value: properties[constants.WASTEWATER_PERCENTILE] ?
properties[constants.WASTEWATER_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME] : null,
};
const femaRisk:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.FEMA_RISK),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.FEMA_RISK),
value: properties[constants.FEMA_PERCENTILE] ?
properties[constants.FEMA_PERCENTILE] : null,
const asthma:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.ASTHMA),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ASTHMA),
value: properties[constants.ASTHMA_PERCENTILE] ?
properties[constants.ASTHMA_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME] : null,
};
const diabetes:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIABETES),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIABETES),
value: properties[constants.DIABETES_PERCENTILE] ?
properties[constants.DIABETES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_DIABETES_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_DIABETES_AND_IS_LOW_INCOME] : null,
};
const heartDisease:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HEART_DISEASE),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HEART_DISEASE),
value: properties[constants.HEART_PERCENTILE] ?
properties[constants.HEART_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME] : null,
};
const houseBurden:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HOUSE_BURDEN),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HOUSE_BURDEN),
value: properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] ?
properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] : null,
const lifeExpect:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LIFE_EXPECT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_LIFE_EXPECT),
value: properties[constants.LIFE_PERCENTILE] ?
properties[constants.LIFE_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME] : null,
};
const lowMedInc:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LOW_MED_INC),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_MED_INCOME),
value: properties[constants.LOW_MEDIAN_INCOME_PERCENTILE] ?
properties[constants.LOW_MEDIAN_INCOME_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU] ?
properties[constants.IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU] : null,
};
const lingIso:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LING_ISO),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LING_ISO),
value: properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE] ?
properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME] : null,
};
const unemploy:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.UNEMPLOY),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.UNEMPLOY),
value: properties[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE] ?
properties[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU] ?
properties[constants.IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU] : null,
};
const poverty:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.POVERTY),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.POVERTY),
value: properties[constants.POVERTY_PROPERTY_PERCENTILE] ?
properties[constants.POVERTY_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] ?
properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] : null,
};
// const highSchool:indicatorInfo = {
// label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HIGH_SCL),
// description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HIGH_SKL),
// value: properties[constants.HIGH_SCHOOL_PROPERTY_PERCENTILE] ?
// properties[constants.HIGH_SCHOOL_PROPERTY_PERCENTILE] : null,
// isDisadvagtaged: properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] ?
// properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] : null,
// };
const indicators = [areaMedianIncome, eduInfo, poverty];
const additionalIndicators = [
asthma, diabetes, dieselPartMatter, energyBurden, femaRisk, heartDisease,
houseBurden, leadPaint, lifeExpect, pm25, trafficVolume, wasteWater,
// Aggregate indicators based on categories
const categories = [
{
id: 'climate-change',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLIMATE),
indicators: [expAgLoss, expBldLoss, expPopLoss, lowInc],
isDisadvagtaged: properties[constants.IS_CLIMATE_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_CLIMATE_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'clean-energy',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_ENERGY),
indicators: [energyBurden, pm25, lowInc],
isDisadvagtaged: properties[constants.IS_ENERGY_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_ENERGY_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'clean-transport',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_TRANSPORT),
indicators: [dieselPartMatter, trafficVolume, lowInc],
isDisadvagtaged: properties[constants.IS_TRANSPORT_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_TRANSPORT_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'sustain-house',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.SUSTAIN_HOUSE),
indicators: [houseBurden, leadPaint, lowInc],
isDisadvagtaged: properties[constants.IS_HOUSING_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_HOUSING_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'leg-pollute',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.LEG_POLLUTE),
indicators: [proxHaz, proxNPL, proxRMP, lowInc],
isDisadvagtaged: properties[constants.IS_POLLUTION_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_POLLUTION_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'clean-water',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_WATER),
indicators: [wasteWater, lowInc],
isDisadvagtaged: properties[constants.IS_WATER_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_WATER_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'health-burdens',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.HEALTH_BURDEN),
indicators: [asthma, diabetes, heartDisease, lifeExpect, lowInc],
isDisadvagtaged: properties[constants.IS_HEALTH_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_HEALTH_FACTOR_DISADVANTAGED_L] : null,
},
{
id: 'work-dev',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.WORK_DEV),
indicators: [lowMedInc, lingIso, unemploy, poverty],
isDisadvagtaged: properties[constants.IS_WORKFORCE_FACTOR_DISADVANTAGED_L] ?
properties[constants.IS_WORKFORCE_FACTOR_DISADVANTAGED_L] : null,
},
];
// Create the AccoridionItems by mapping over the categories array. In this array we define the
// various indicators for a specific category. This is an array which then maps over the <Indicator />
// component to render the actual Indicator
const categoryItems = categories.map((category) => ({
id: category.id,
title: <Category name={category.titleText} isDisadvantaged={category.isDisadvagtaged}/>,
content: (
<>
{category.indicators.map((indicator:any, index:number) => {
return <Indicator key={`ind${index}`} indicator={indicator}/>;
})}
</>
),
expanded: false,
}));
return (
<aside className={styles.areaDetailContainer} data-cy={'aside'}>
{/* Methodology version */}
<div className={styles.versionInfo}>
{EXPLORE_COPY.SIDE_PANEL_VERION.TITLE}
</div>
{/* Census Info */}
<ul className={styles.censusRow}>
<li>
<span className={styles.censusLabel}>
@ -192,139 +354,27 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
<span className={styles.censusText}>{` ${population.toLocaleString()}`}</span>
</li>
</ul>
{/* Disadvantaged? */}
<div className={styles.categorization}>
<div className={styles.isInFocus}>
{EXPLORE_COPY.COMMUNITY.IS_FOCUS}
</div>
<div className={styles.communityOfFocus}>
{isCommunityFocus ?
<>
<div className={styles.communityOfFocusCircle} />
<h3>{EXPLORE_COPY.COMMUNITY.OF_FOCUS}</h3>
<DisadvantageDot isDisadvantaged={isCommunityFocus}/>
</> :
<h3>{EXPLORE_COPY.COMMUNITY.NOT_OF_FOCUS}</h3>
}
</div>
<p className={"secondary"}>version {METHODOLOGY_COPY.VERSION_NUMBER}</p>
</div>
<div className={styles.divider}>
<h6>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.INDICATOR_COLUMN_HEADER)}
</h6>
<h6>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PERCENTILE_COLUMN_HEADER)}
</h6>
{/* eslint-disable-next-line max-len */}
{/* <a className={styles.feedbackLink} href={sidePanelFeedbackHref}>{EXPLORE_COPY.COMMUNITY.SEND_FEEDBACK}</a> */}
</div>
<>
{
indicators.map((indicator:any, index:number) => {
return <li key={`ind${index}`} className={styles.indicatorBoxMain} data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
<>
{
additionalIndicators.map((indicator:any, index:number) => {
return <li
key={`ind${index}`}
className={styles.indicatorBoxAdditional}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
{/* Temporarily remove Accordions and may place back in later, removed unused
className prop as as styles are based on the id of the Accordion Item */}
{/* <Accordion
multiselectable={true}
items={
[
{
id: 'prioritization-indicators',
title: 'Indicators',
content: (
<>
{
indicators.map((indicator:any, index:number) => {
return <li key={`ind${index}`} className={styles.indicatorBoxMain} data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
),
expanded: true,
},
{
id: 'additional-indicators',
title: 'Additional indicators (not used in prioritization)',
content: (
(
<>
{
additionalIndicators.map((indicator:any, index:number) => {
return <li
key={`ind${index}`}
className={styles.indicatorBoxAdditional}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
)
),
expanded: true,
},
]
}/> */}
{/* All category accordions in this component */}
<Accordion multiselectable={true} items={categoryItems}/>
</aside>
);

View file

@ -1,6 +1,6 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import AreaDetail, {readablePercentile} from '..';
import AreaDetail from '..';
import {LocalizedComponent} from '../../../test/testHelpers';
import * as constants from '../../../data/constants';
@ -29,9 +29,3 @@ describe('rendering of the AreaDetail', () => {
});
});
describe('tests the readablePercentile function', () => {
expect(readablePercentile(.98)).toEqual(98);
expect(readablePercentile(.07)).toEqual(7);
expect(readablePercentile(.123)).toEqual(12);
expect(readablePercentile(.789)).toEqual(79);
});

View file

@ -0,0 +1,8 @@
@use '../../styles/design-system.scss' as *;
.categoriesContainer {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
@include u-margin-top(4);
}

View file

@ -0,0 +1,12 @@
declare namespace CategoriesNamespace {
export interface ICategoriesScss {
categoriesContainer: string;
}
}
declare const CategoriesScssModule: CategoriesNamespace.ICategoriesScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: CategoriesNamespace.ICategoriesScss;
};
export = CategoriesScssModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Categories from './Categories';
describe('rendering of the Categories', () => {
const {asFragment} = render(
<LocalizedComponent>
<Categories />
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,43 @@
import React from 'react';
import {Grid} from '@trussworks/react-uswds';
import CategoryCard from '../CategoryCard';
import J40MainGridContainer from '../J40MainGridContainer';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './Categories.module.scss';
const categories = [
METHODOLOGY_COPY.CATEGORIES.CLIMATE_CHANGE,
METHODOLOGY_COPY.CATEGORIES.CLEAN_ENERGY,
METHODOLOGY_COPY.CATEGORIES.CLEAN_TRANSPORT,
METHODOLOGY_COPY.CATEGORIES.AFFORDABLE_HOUSING,
METHODOLOGY_COPY.CATEGORIES.LEGACY_POLLUTION,
METHODOLOGY_COPY.CATEGORIES.CLEAN_WATER,
METHODOLOGY_COPY.CATEGORIES.HEALTH_BURDENS,
METHODOLOGY_COPY.CATEGORIES.WORKFORCE_DEV,
];
const Categories = () => {
return (
<>
<J40MainGridContainer className={styles.categoriesContainer}>
<Grid row>
<Grid col={12}>
<h2>{METHODOLOGY_COPY.CATEGORY.HEADING}</h2>
</Grid>
</Grid>
</J40MainGridContainer>
<J40MainGridContainer className={styles.categoriesContainer}>
{
categories.map((category, index) => <CategoryCard key={index} categoryInfo={category} />)
}
</J40MainGridContainer>
</>
);
};
export default Categories;

View file

@ -0,0 +1,403 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Categories checks if component renders 1`] = `
<DocumentFragment>
<div
class="grid-container-desktop-lg"
data-testid="gridContainer"
>
<div
class="grid-row"
data-testid="grid"
>
<div
class="grid-col-12"
data-testid="grid"
>
<h2>
Categories
</h2>
</div>
</div>
</div>
<div
class="grid-container-desktop-lg"
data-testid="gridContainer"
>
<div>
<h3>
Climate change
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#exp-agr-loss-rate"
>
expected agriculture loss rate
</a>
OR
<a
href="#exp-bld-loss-rate"
>
expected building loss rate
</a>
OR
<a
href="#exp-pop-loss-rate"
>
expected population loss rate
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Clean energy and energy efficiency
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#energy-burden"
>
energy cost burden score
</a>
OR
<a
href="#pm-25"
>
PM2.5
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Clean transportation
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#diesel-pm"
>
diesel particulate matter
</a>
or
<a
href="#traffic-vol"
>
traffic proximity and volume
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Affordable and sustainable housing
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#lead-paint"
>
lead paint
</a>
AND
<a
href="#median-home"
>
the median home value
</a>
is less than
90th percentile OR at or above the 90th percentile for the
<a
href="#house-burden"
>
housing cost burden
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Reduction and remediation of legacy pollution
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#prox-haz"
>
proximity to hazardous waste facilities
</a>
OR
<a
href="#prox-npl"
>
proximity to NLP sites
</a>
OR
<a
href="#prox-rmp"
>
proximity to RMP sites
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Critical clean water and waste infrastructure
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#waste-water"
>
wastewater discharge
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Health burdens
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#asthma"
>
asthma
</a>
OR
<a
href="#diabetes"
>
diabetes
</a>
OR
<a
href="#heart-disease"
>
heart disease
</a>
OR
<a
href="#life-exp"
>
low life expectancy
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
<div>
<h3>
Training and workforce development
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#low-med-inc"
>
low median income relative to area median income
</a>
OR
at or above the 90th percentile for
<a
href="#ling-iso"
>
linguistic isolation
</a>
OR
<a
href="#unemploy"
>
unemployment
</a>
OR
for percentage individuals in households at or below 100% federal
<a
href="#poverty"
>
poverty
</a>
level at or above 90%
</p>
<p>
<strong>
AND
</strong>
where
<a
href="#high-school"
>
the high school degree achievement rates
</a>
for adults 25 years and older is less than 90%
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import Categories from './Categories';
export default Categories;

View file

@ -0,0 +1,6 @@
@use '../../styles/design-system.scss' as *;
.categoryContainer {
display: flex;
justify-content: space-between;
}

View file

@ -0,0 +1,13 @@
declare namespace CategoryNamespace {
export interface ICategoryScss {
categoryContainer: string;
disadvantageDot: string;
}
}
declare const CategoryScssModule: CategoryNamespace.ICategoryScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: CategoryNamespace.ICategoryScss;
};
export = CategoryScssModule;

View file

@ -0,0 +1,28 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Category from './Category';
describe('rendering of the Category disadvantaged', () => {
const {asFragment} = render(
<LocalizedComponent>
<Category name={'test name'} isDisadvantaged={true}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});
describe('rendering of the Category non-disadvantaged', () => {
const {asFragment} = render(
<LocalizedComponent>
<Category name={'test name'} isDisadvantaged={false}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,22 @@
import React from 'react';
import DisadvantageDot from '../DisadvantageDot';
import * as styles from './Category.module.scss';
interface ICategory {
name: string;
isDisadvantaged: boolean;
}
const Category = ({name, isDisadvantaged}:ICategory) => {
return (
<div className={styles.categoryContainer}>
<div>
{name}
</div>
<DisadvantageDot isDisadvantaged={isDisadvantaged}/>
</div>
);
};
export default Category;

View file

@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Category disadvantaged checks if component renders 1`] = `
<DocumentFragment>
<div>
<div>
test name
</div>
<div />
</div>
</DocumentFragment>
`;
exports[`rendering of the Category non-disadvantaged checks if component renders 1`] = `<DocumentFragment />`;

View file

@ -0,0 +1,3 @@
import Category from './Category';
export default Category;

View file

@ -0,0 +1,15 @@
@use '../../styles/design-system.scss' as *;
@mixin baseCard {
@include u-padding-top(0);
@include u-padding-right(6);
@include u-padding-bottom(6);
@include u-padding-left(6);
@include u-margin-bottom(6);
max-width: 34rem;
}
.categoryCard {
@include baseCard;
@include u-bg('blue-cool-5');
}

View file

@ -0,0 +1,12 @@
declare namespace IndicatorCategoryNamespace {
export interface IIndicatorCategoryScss {
categoryCard: string;
}
}
declare const IndicatorCategoryScssModule: IndicatorCategoryNamespace.IIndicatorCategoryScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: IndicatorCategoryNamespace.IIndicatorCategoryScss;
};
export = IndicatorCategoryScssModule;

View file

@ -0,0 +1,18 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import CategoryCard from './CategoryCard';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
describe('rendering of the CategoryCard', () => {
const {asFragment} = render(
<LocalizedComponent>
<CategoryCard categoryInfo={METHODOLOGY_COPY.CATEGORIES.CLIMATE_CHANGE}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,32 @@
import React from 'react';
import * as styles from './CategoryCard.module.scss';
interface ICategoryInterface {
categoryInfo: {
TITLE: JSX.Element,
IF: JSX.Element,
AND: JSX.Element,
THEN: JSX.Element
}
}
const CategoryCard = ({categoryInfo}: ICategoryInterface) => {
return (
<div className={styles.categoryCard}>
<h3>
{categoryInfo.TITLE}
</h3>
<p>
{categoryInfo.IF}
</p>
<p>
{categoryInfo.AND}
</p>
<p>
{categoryInfo.THEN}
</p>
</div>
);
};
export default CategoryCard;

View file

@ -0,0 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the CategoryCard checks if component renders 1`] = `
<DocumentFragment>
<div>
<h3>
Climate change
</h3>
<p>
<strong>
IF
</strong>
at or above 90th percentile for
<a
href="#exp-agr-loss-rate"
>
expected agriculture loss rate
</a>
OR
<a
href="#exp-bld-loss-rate"
>
expected building loss rate
</a>
OR
<a
href="#exp-pop-loss-rate"
>
expected population loss rate
</a>
</p>
<p>
<strong>
AND
</strong>
is low income
<sup>
*
</sup>
</p>
<p>
<strong>
THEN
</strong>
the community is disadvantaged.
</p>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import CategoryCard from './CategoryCard';
export default CategoryCard;

View file

@ -1,3 +1,4 @@
@use '../../styles/design-system.scss' as *;
@import '../utils.scss';
@mixin baseCard {
@ -8,7 +9,7 @@
.datasetCard {
@include baseCard;
background-color: white;
@include u-bg("gray-3");
}
.datasetCardAdditional {

View file

@ -5,15 +5,16 @@ import * as styles from './datasetCard.module.scss';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
interface IDatasetCardProps {
datasetCardProps: { [key:string]: string }
additionalIndicator: boolean
datasetCardProps: {
[key:string]: string
}
}
const DatasetCard = ({datasetCardProps, additionalIndicator}:IDatasetCardProps) => {
const DatasetCard = ({datasetCardProps}:IDatasetCardProps) => {
const intl = useIntl();
return (
<div className={additionalIndicator ? styles.datasetCardAdditional : styles.datasetCard}>
<div className={styles.datasetCard} id={datasetCardProps.domID}>
<h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3>
<div className={styles.datasetCardDescription}>
{datasetCardProps.description}
@ -22,23 +23,23 @@ const DatasetCard = ({datasetCardProps, additionalIndicator}:IDatasetCardProps)
<ul className={styles.datasetCardList}>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.SOURCE)}
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.RESP_PARTY)}
</span>
<a href={datasetCardProps.dataSourceURL} target={'_blank'} rel="noreferrer">
{datasetCardProps.dataSourceLabel}
{datasetCardProps.respPartyLabel}
</a>
</li>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.RESOLUTION)}
</span>
{datasetCardProps.dataResolution}
</li>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.DATE_RANGE)}
</span>
{datasetCardProps.dataDateRange}
{datasetCardProps.dateRange}
</li>
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.USED_IN)}
</span>
{datasetCardProps.usedIn}
</li>
</ul>
</div>

View file

@ -1,41 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of addtional indicator dataset card checks if component renders 1`] = `<DocumentFragment />`;
exports[`rendering of indicator dataset card checks if component renders 1`] = `
<DocumentFragment>
<div>
<div
id="low-income"
>
<h3>
Area Median Income
Low Income
</h3>
<div>
Median income of the census block group calculated as a percent
of the metropolitan areas or state's median income.
Percent of a block group's population in households where household income is at or below
200% of the federal poverty level.
</div>
<ul>
<li>
<span>
Data source:
Responsible Party:
</span>
<a
href="https://www.census.gov/programs-surveys/acs"
rel="noreferrer"
target="_blank"
>
Census's American Community Survey
Census's American Community Survey.
</a>
</li>
<li>
<span>
Data resolution:
Date range:
</span>
Census block group
2015-2019
</li>
<li>
<span>
Data date range:
Used in:
</span>
2015-2019
All methodologies except for training and workforce development
</li>
</ul>
</div>

View file

@ -8,19 +8,7 @@ import * as METHODOLOGY_COPY from '../../../data/copy/methodology';
describe('rendering of indicator dataset card', () => {
const {asFragment} = render(
<LocalizedComponent>
<DatasetCard key={0} datasetCardProps={METHODOLOGY_COPY.INDICATORS[0]} additionalIndicator={false}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});
describe('rendering of addtional indicator dataset card', () => {
const {asFragment} = render(
<LocalizedComponent>
<DatasetCard key={0} datasetCardProps={METHODOLOGY_COPY.ADDITIONAL_INDICATORS[0]} additionalIndicator={true}/>
<DatasetCard key={0} datasetCardProps={METHODOLOGY_COPY.INDICATORS[0]}/>
</LocalizedComponent>,
);

View file

@ -1,5 +1,14 @@
@use '../../styles/design-system.scss' as *;
.datasetCardsContainer {
@include u-margin-top(4);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.returnToTop {
display: flex;
justify-content: flex-end;
@include u-margin-bottom(4);
}

View file

@ -1,6 +1,7 @@
declare namespace DatasetContainerScssNamespace {
export interface IDatasetContainerScss {
datasetCardsContainer: string;
returnToTop: string;
}
}

View file

@ -1,5 +1,5 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {Link, useIntl} from 'gatsby-plugin-intl';
import {Grid} from '@trussworks/react-uswds';
import DatasetCard from '../DatasetCard';
@ -15,7 +15,7 @@ const DatasetContainer = () => {
return (
<>
<J40MainGridContainer fullWidth={true} blueBackground={true}>
<J40MainGridContainer fullWidth={true} blueBackground={false}>
<J40MainGridContainer
dataCy={`${hyphenizeString(METHODOLOGY_COPY.DATASETS.HEADING.defaultMessage)}-block`}>
@ -25,49 +25,26 @@ const DatasetContainer = () => {
</Grid>
</Grid>
<Grid row>
<Grid col={12} tablet={{col: 7}} className={'j40-mb-3'}>
<p>{intl.formatMessage(METHODOLOGY_COPY.DATASETS.INFO)}</p>
</Grid>
</Grid>
<div className={styles.datasetCardsContainer}>
{METHODOLOGY_COPY.INDICATORS.map((card) => <DatasetCard
key={card.indicator}
datasetCardProps={card}
additionalIndicator={false}
/>)}
</div>
</J40MainGridContainer>
</J40MainGridContainer>
<J40MainGridContainer fullWidth={true} blueBackground={false} >
<J40MainGridContainer
dataCy={`${hyphenizeString(METHODOLOGY_COPY.DATASETS.ADDITIONAL_HEADING.defaultMessage)}-block`}>
<Grid row>
<Grid col={12}>
<h2>{intl.formatMessage(METHODOLOGY_COPY.DATASETS.ADDITIONAL_HEADING)}</h2>
<div className={styles.datasetCardsContainer}>
{METHODOLOGY_COPY.INDICATORS.map((card) => <DatasetCard
key={card.indicator}
datasetCardProps={card}
/>)}
</div>
</Grid>
</Grid>
<Grid row>
<Grid col={12} tablet={{col: 7}} className={'j40-mb-3'}>
<p>{intl.formatMessage(METHODOLOGY_COPY.DATASETS.ADDITIONAL_INFO)}</p>
</Grid>
</Grid>
<div className={styles.datasetCardsContainer}>
{METHODOLOGY_COPY.ADDITIONAL_INDICATORS.map((card) => <DatasetCard
key={card.indicator}
datasetCardProps={card}
additionalIndicator={true}
/>)}
<div className={styles.returnToTop}>
<Link to={`/methodology`}>
{METHODOLOGY_COPY.RETURN_TO_TOP.LINK}
</Link>
</div>
</J40MainGridContainer>
</J40MainGridContainer>
</>
);
};

View file

@ -0,0 +1,19 @@
@use '../../styles/design-system.scss' as *;
.disadvantagedDotSmall {
@include u-circle('105');
@include u-margin-left(1);
align-self: center;
@include u-bg('blue-warm-70v');
}
.disadvantagedDotBig {
@include u-circle(4);
margin: 2.3rem 1.5rem 2rem 0;
opacity: .6;
//Maintain aspect ratio as screen width decreases
flex: 1 0 2rem;
@include u-bg('blue-warm-70v');
}

View file

@ -0,0 +1,14 @@
declare namespace DisadvantagedDotNamespace {
export interface IDisadvantagedDot {
disadvantagedDotBig: string;
disadvantagedDotSmall: string;
}
}
declare const DisadvantagedDotModule: DisadvantagedDotNamespace.IDisadvantagedDot & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: DisadvantagedDotNamespace.IDisadvantagedDot;
};
export = DisadvantagedDotModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import DisadvantageDot from './DisadvantageDot';
describe('rendering of the DisadvantageDot disadvantaged', () => {
const {asFragment} = render(
<LocalizedComponent>
<DisadvantageDot isDisadvantaged={true}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,23 @@
import React from 'react';
import * as styles from './DisadvantageDot.module.scss';
interface IDisadvantageDot {
isDisadvantaged?: boolean;
isBig?: boolean;
}
const DisadvantageDot = ({isDisadvantaged = false, isBig}:IDisadvantageDot) => {
let computedClass = '';
if (isBig) {
computedClass = styles.disadvantagedDotBig;
} else {
computedClass = isDisadvantaged ? styles.disadvantagedDotSmall : '';
}
return (
<div className={computedClass} />
);
};
export default DisadvantageDot;

View file

@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the DisadvantageDot disadvantaged checks if component renders 1`] = `
<DocumentFragment>
<div />
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import DisadvantageDot from './DisadvantageDot';
export default DisadvantageDot;

View file

@ -1,4 +1,4 @@
$primary-color: #112f4e;
@use '../../styles/design-system.scss' as *;
.downloadBoxContainer {
@ -6,7 +6,7 @@ $primary-color: #112f4e;
margin: auto;
.downloadBox {
background-color: $primary-color;
@include u-bg('blue-80v');
border-radius: 6px 6px;
.downloadBoxTextBox {
@ -15,12 +15,15 @@ $primary-color: #112f4e;
flex-direction: column;
.downloadBoxTitle {
font-weight: bold;
margin-bottom: 10px;
@include typeset('sans', 'xs', 3);
@include u-text('semibold');
@include u-margin-bottom(2);
}
.downloadBoxText {
margin-bottom: 20px;
@include typeset('sans', 'xs', 3);
@include u-margin-bottom(4);
span {
font-style: italic;
}
@ -42,8 +45,8 @@ $primary-color: #112f4e;
}
.downloadBoxButton{
background-color: white;
color: $primary-color;
@include u-bg('white');
@include u-color('blue-80v');
display: flex;
.downloadPacketText {

View file

@ -2,17 +2,32 @@ import React from 'react';
import {GovBanner} from '@trussworks/react-uswds';
import {useIntl} from 'gatsby-plugin-intl';
// Contexts:
import {useFlags} from '../../contexts/FlagContext';
import Language from '../Language';
import * as styles from './GovernmentBanner.module.scss';
const GovernmentBanner = () => {
const intl = useIntl();
const flags = useFlags();
return (
<div className={styles.fullScreenContainer}>
<div className={styles.bannerContainer}>
<GovBanner language={intl.locale === 'es' ? 'spanish' : 'english'}/>
<Language isDesktop={true}/>
{'sp' in flags ? (
<>
<GovBanner language={intl.locale === 'es' ? 'spanish' : 'english'}/>
<Language isDesktop={true}/>
</>
) : (
<>
<GovBanner/>
</>
)}
</div>
</div>
);

View file

@ -135,22 +135,6 @@ exports[`rendering of the GovernmentBanner checks if component renders 1`] = `
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
</DocumentFragment>

View file

@ -0,0 +1,59 @@
@use '../../styles/design-system.scss' as *;
@import "../utils.scss";
@mixin indicator {
display: flex;
flex-direction: column;
@include u-padding-bottom(3);
&:last-child {
border-bottom: none;
@include u-padding-bottom(0);
}
.indicatorRow {
display: flex;
@media screen and (max-width: $mobileBreakpoint) {
flex: 1 0 40%;
align-self: inherit;
padding-left: 3rem;
padding-top: 1rem;
}
.indicatorName {
flex: 0 1 77%;
display: flex;
flex-direction: column;
@include typeset('sans', '2xs', 2);
@include u-text('bold');
.indicatorDesc {
@include typeset('sans', '3xs', 2);
@include u-text('normal');
max-width: 12rem;
@include u-margin-top(0);
@media screen and (max-width: 1024px) {
max-width: 80%;
}
}
}
.indicatorValue {
margin-left: 2.2rem;
.indicatorSuperscript {
top: -0.2em
}
}
}
}
//Indicator box styles
.indicatorBoxMain {
@include indicator;
}
.disadvantagedIndicator {
@include indicator;
@include u-text('blue-warm-70v');
}

View file

@ -0,0 +1,19 @@
declare namespace IndicatorNamespace {
export interface IIndicatorScss {
indicatorBoxMain:string;
indicatorBoxAdditional:string;
indicatorRow:string;
indicatorName:string;
indicatorValue:string;
indicatorSuperscript:string;
indicatorDesc:string;
disadvantagedIndicator:string;
}
}
declare const IndicatorScssModule: IndicatorNamespace.IIndicatorScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: IndicatorNamespace.IIndicatorScss;
};
export = IndicatorScssModule;

View file

@ -0,0 +1,30 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Indicator, {readablePercentile} from './Indicator';
import {indicatorInfo} from '../AreaDetail';
const highSchool:indicatorInfo = {
label: 'some label',
description: 'some description',
value: 97,
};
describe('rendering of the Indicator', () => {
const {asFragment} = render(
<LocalizedComponent>
<Indicator indicator={highSchool}/>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});
describe('tests the readablePercentile function', () => {
expect(readablePercentile(.98)).toEqual(98);
expect(readablePercentile(.07)).toEqual(7);
expect(readablePercentile(.123)).toEqual(12);
expect(readablePercentile(.789)).toEqual(79);
});

View file

@ -0,0 +1,55 @@
import React from 'react';
import {indicatorInfo} from '../AreaDetail';
import * as styles from './Indicator.module.scss';
interface IIndicator {
indicator: indicatorInfo,
}
export const readablePercentile = (percentile: number | null) => {
return percentile ? Math.round(percentile * 100) : 'N/A';
};
// Todo: Add internationalization to superscript ticket #582
export const getSuperscriptOrdinal = (percentile: number | string) => {
if (typeof percentile === 'number') {
const englishOrdinalRules = new Intl.PluralRules('en', {
type: 'ordinal',
});
const suffixes = {
zero: 'th',
one: 'st',
two: 'nd',
few: 'rd',
many: 'th',
other: 'th',
};
return suffixes[englishOrdinalRules.select(percentile)];
}
};
const Indicator = ({indicator}:IIndicator) => {
return (
<li
className={indicator.isDisadvagtaged ? styles.disadvantagedIndicator : styles.indicatorBoxMain}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<div className={styles.indicatorName}>
{indicator.label}
<div className={styles.indicatorDesc}>
{indicator.description}
</div>
</div>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
</li>
);
};
export default Indicator;

View file

@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Indicator checks if component renders 1`] = `
<DocumentFragment>
<li
data-cy="indicatorBox"
>
<div>
<div>
some label
<div>
some description
</div>
</div>
<div>
9700
<sup>
<span>
th
</span>
</sup>
</div>
</div>
</li>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import Indicator from './Indicator';
export default Indicator;

View file

@ -140,22 +140,6 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
<div>
@ -277,24 +261,7 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
<li
class="usa-nav__primary-item"
>
<div>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
<div />
</li>
</ul>
</nav>

View file

@ -157,7 +157,10 @@ const J40Map = ({location}: IJ40Interface) => {
<Grid col={12} desktop={{col: 9}}>
{/*
The MapSearch component is wrapped in a div in order for MapSearch to render correctly in a production build.
The MapSearch component is no longer wrapped in a div in order to allow this feature
to be behind a feature flag. This was causing a bug for MapSearch to render
correctly in a production build. Leaving this comment here in case future flags are
needed in this component
When the MapSearch component is placed behind a feature flag without a div wrapping
MapSearch, the production build will inject CSS due to the null in the false conditional
@ -168,9 +171,7 @@ const J40Map = ({location}: IJ40Interface) => {
to ensure the production build works and that MapSearch and the map (ReactMapGL) render correctly.
*/}
<div>
{'sr' in flags ? <MapSearch goToPlace={goToPlace}/> : null}
</div>
<MapSearch goToPlace={goToPlace}/>
<ReactMapGL
{...viewport}

View file

@ -1,6 +1,9 @@
import React from 'react';
import {IntlContextConsumer, changeLocale} from 'gatsby-plugin-intl';
// Contexts:
import {useFlags} from '../../contexts/FlagContext';
// @ts-ignore
import languageIcon from '/node_modules/uswds/dist/img/usa-icons/language.svg';
import * as styles from './Language.module.scss';
@ -15,7 +18,9 @@ interface ILanguageProps {
}
const Language = ({isDesktop}:ILanguageProps) => {
return (
const flags = useFlags();
return 'sp' in flags ? (
<div className={isDesktop ? styles.languageContainer : styles.languageContainerMobile}>
<img className={styles.languageIcon} src={languageIcon} alt={'language icon for selecting language'}/>
<IntlContextConsumer>
@ -33,7 +38,7 @@ const Language = ({isDesktop}:ILanguageProps) => {
}
</IntlContextConsumer>
</div>
);
) : null;
};
export default Language;

View file

@ -1,22 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the Language checks if component renders 1`] = `
<DocumentFragment>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</DocumentFragment>
`;
exports[`rendering of the Language checks if component renders 1`] = `<DocumentFragment />`;

View file

@ -0,0 +1,20 @@
@use '../../styles/design-system.scss' as *;
.lowIncomeContainer {
border: 1px solid #DFE1E2;
@include u-margin-top(4);
@include u-padding-left(4);
@include u-padding-right(3);
@include u-padding-bottom(4);
.lowIncomeTitle {
@include typeset('sans', 'xs', 3);
@include u-text('semibold');
}
.lowIncomeText {
@include typeset('sans', 'xs', 3);
@include u-text('light');
}
};

View file

@ -0,0 +1,14 @@
declare namespace LowIncomeNamespace {
export interface ILowIncomeScss {
lowIncomeContainer: string;
lowIncomeTitle: string;
lowIncomeText: string;
}
}
declare const LowIncomeScssModule: LowIncomeNamespace.ILowIncomeScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: LowIncomeNamespace.ILowIncomeScss;
};
export = LowIncomeScssModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import LowIncome from './LowIncome';
describe('rendering of the LowIncome', () => {
const {asFragment} = render(
<LocalizedComponent>
<LowIncome />
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,26 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './LowIncome.module.scss';
const LowIncome = () => {
const intl = useIntl();
return (
<div className={styles.lowIncomeContainer}>
<p className={styles.lowIncomeTitle}>
<sup>*</sup>
{' '}
{intl.formatMessage(METHODOLOGY_COPY.LOW_INCOME.HEADING)}
</p>
<p className={styles.lowIncomeText}>
{intl.formatMessage(METHODOLOGY_COPY.LOW_INCOME.INFO)}
</p>
</div>
);
};
export default LowIncome;

View file

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the LowIncome checks if component renders 1`] = `
<DocumentFragment>
<div>
<p>
<sup>
*
</sup>
Low Income
</p>
<p>
At or above 65th percentile for percent of census tract population of households where household
income is at or below 200% of the federal poverty level
</p>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import LowIncome from './LowIncome';
export default LowIncome;

View file

@ -3,13 +3,14 @@ import {useIntl} from 'gatsby-plugin-intl';
import * as styles from './mapLegend.module.scss';
import * as EXPLORE_COPY from '../../data/copy/explore';
import DisadvantageDot from '../DisadvantageDot';
const MapLegend = () => {
const intl = useIntl();
return (
<div className={styles.legendContainer}>
<div className={styles.colorSwatch} />
<div>
<DisadvantageDot isBig={true} />
<div className={styles.legendTextBox}>
<div className={'j40-h4'}>
{intl.formatMessage(EXPLORE_COPY.LEGEND.PRIORITY_LABEL)}
</div>

View file

@ -1,26 +1,17 @@
@import "../utils.scss";
$min-color: #fafaf8;
$med-color: rgba(26, 68, 128, 0.2);
$max-color: rgba(26, 68, 128, 0.6);
$alertInfoColor: #e7f6f8;
.legendContainer {
display: flex;
margin: 1rem 0 1rem 2.5rem;
border: 1px solid #DFE1E2;
padding: .5rem 1.5rem 1rem 1.5rem;
margin: 1rem 0 1rem 2.5rem;
padding: 0 1.5rem 1rem 1.5rem;
.legendTextBox {
// Set maximum width for text area so that blue dot
// doesn't lose aspect ratio
flex-basis: 88%;
}
@media screen and (max-width: 640px) {
margin: 1rem 0 0;
}
}
.colorSwatch {
flex: 1 0 2rem;
box-sizing: border-box;
height: 2rem;
margin: 2rem 1.5rem 2rem 0;
border-radius: 50%;
background-color: $max-color;
}

View file

@ -1,7 +1,7 @@
declare namespace HowYouCanHelpModuleScssNamespace {
export interface IHowYouCanHelpModuleScss {
legendContainer: string;
colorSwatch: string;
legendTextBox: string;
}
}

View file

@ -24,6 +24,14 @@ const MapWrapper = ({location}: IMapWrapperProps) => {
</div>
</Grid>
</Grid>
<Grid row>
<Grid col={7}>
<h2>{EXPLORE_COPY.NOTE_ON_TERRITORIES.INTRO}</h2>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_1}</p>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_2}</p>
</Grid>
</Grid>
</>
);
};

View file

@ -0,0 +1,12 @@
@use '../../styles/design-system.scss' as *;
.formulaContainer {
@include u-margin-top(5);
p:not(:first-child) {
font-style: italic;
span {
@include u-text('bold');
}
}
};

View file

@ -0,0 +1,12 @@
declare namespace MethodologyFormulaNamespace {
export interface IMethodologyFormulaScss {
formulaContainer: string;
}
}
declare const MethodologyFormulaScssModule: MethodologyFormulaNamespace.IMethodologyFormulaScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: MethodologyFormulaNamespace.IMethodologyFormulaScss;
};
export = MethodologyFormulaScssModule;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import MethodologyFormula from './MethodologyFormula';
describe('rendering of the MethodologyFormula', () => {
const {asFragment} = render(
<LocalizedComponent>
<MethodologyFormula />
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,35 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './MethodologyFormula.module.scss';
// The site shows the formula used in the methodology. The constants seen
// below aim to capture the 3 part of that formula. These are not
// reserved words.
const MethodologyFormula = () => {
const intl = useIntl();
return (
<section className={styles.formulaContainer}>
<p>
{intl.formatMessage(METHODOLOGY_COPY.PAGE.FORMULA_INTRO)}
</p>
<p>
{METHODOLOGY_COPY.FORMULA.IF}
</p>
<p>
{METHODOLOGY_COPY.FORMULA.AND}
</p>
<p>
{METHODOLOGY_COPY.FORMULA.THEN}
</p>
</section>
);
};
export default MethodologyFormula;

View file

@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the MethodologyFormula checks if component renders 1`] = `
<DocumentFragment>
<section>
<p>
Under the current formula, a census tract will be considered disadvantaged:
</p>
<p>
<span>
IF
</span>
it is above the threshold for one or more climate or environmental indicator
</p>
<p>
<span>
AND
</span>
it is above the threshold for one or more socioeconomic indicator
</p>
<p>
<span>
THEN
</span>
the community is considered disadvantaged.
</p>
</section>
</DocumentFragment>
`;

View file

@ -0,0 +1,3 @@
import MethodologyFormula from './MethodologyFormula';
export default MethodologyFormula;

View file

@ -1,125 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the component should match the snapshot of the MapIntroduction component 1`] = `
<DocumentFragment>
<h2>
Methodology
</h2>
<div
class="grid-row"
data-testid="grid"
>
<div
class="grid-col-7"
data-testid="grid"
>
<p>
The methodology for identifying communities of focus is calculated at the census block group level. Census block geographical boundaries are determined by the U.S. Census Bureau once every ten years. This tool utilizes the census block boundaries from 2010.
</p>
<p>
The following describes the process for identifying communities of focus.
</p>
</div>
</div>
<ol
class="usa-process-list"
>
<li
class="usa-process-list__item"
>
<h3
class="usa-process-list__heading"
data-testid="processListHeading"
>
Gather datasets
</h3>
<p>
</p>
<p
class="flush"
>
The methodology includes the following inputs that are equally weighted.
</p>
<h4>
Percent of Area Median Income
</h4>
<p
class="flush"
>
If a census block group is in a metropolitan area, this value is the median income of the census block group calculated as a percent of the metropolitan areas median income.
</p>
<p>
If a census block group is not in a metropolitan area, this value is the median income of the census block group calculated as a percent of the states median income.
</p>
<h4>
Percent of households below or at 100% of the federal poverty line
</h4>
This is the percent of households in a state with a household income
below or at 100% of the
<a
href="https://www.census.gov/topics/income-poverty/poverty/guidance/poverty-measures.html"
rel="noreferrer"
target="_blank"
>
federal poverty line
</a>
. This federal poverty line is calculated
based on the composition of each household (e.g., based on household size), but it does not vary geographically.
<h4>
The high school degree achievement rate for adults 25 years and older
</h4>
<p
class="flush"
>
The percent of individuals who are 25 or older who have received a high school degree.
</p>
</li>
<li
class="usa-process-list__item"
>
<h3
class="usa-process-list__heading"
data-testid="processListHeading"
>
Determine communites of focus
</h3>
<p>
</p>
<p
class="flush"
>
Under the existing formula, a census block group will be considered a community of focus if:
</p>
<p>
(The median income is less than 80% of the area median income
</p>
<p
class="flush"
>
OR
</p>
<p
class="flush"
>
households living in poverty (at or below 100% of the federal poverty level) is greater than 20%)
</p>
<p
class="flush"
>
AND
</p>
<p
class="flush"
>
The high school degree achievement rate for adults 25 years and older is greater than 95%
</p>
</li>
</ol>
</DocumentFragment>
`;

View file

@ -1,28 +0,0 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import ScoreStepsList from './scoreStepsList';
import {LocalizedComponent} from '../test/testHelpers';
// TODO: Move this to a location that will detect on all tests
// See ticket: #550
beforeAll(() => {
jest.spyOn(global.console, 'error').mockImplementation((...params) => {
console.error(params);
});
});
describe('rendering of the component', () => {
const {asFragment} = render(
<LocalizedComponent>
<ScoreStepsList/>
</LocalizedComponent>,
);
it('should match the snapshot of the MapIntroduction component', () => {
expect(asFragment()).toMatchSnapshot();
});
it('No console errors', () => {
expect(console.error).toBeCalledTimes(0);
});
});

View file

@ -66,10 +66,10 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
short: intl.formatMessage(EXPLORE_COPY.MAP.PR_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.PR_LONG),
},
{
short: intl.formatMessage(EXPLORE_COPY.MAP.GU_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.GU_LONG),
},
// {
// short: intl.formatMessage(EXPLORE_COPY.MAP.GU_SHORT),
// long: intl.formatMessage(EXPLORE_COPY.MAP.GU_LONG),
// },
{
short: intl.formatMessage(EXPLORE_COPY.MAP.AS_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.AS_LONG),
@ -78,10 +78,10 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
short: intl.formatMessage(EXPLORE_COPY.MAP.MP_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.MP_LONG),
},
{
short: intl.formatMessage(EXPLORE_COPY.MAP.VI_SHORT),
long: intl.formatMessage(EXPLORE_COPY.MAP.VI_LONG),
},
// {
// short: intl.formatMessage(EXPLORE_COPY.MAP.VI_SHORT),
// long: intl.formatMessage(EXPLORE_COPY.MAP.VI_LONG),
// },
];
// the offset for this array should map the territories variable
const territoriesIconClassName = [
@ -89,10 +89,10 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
'mapboxgl-ctrl-zoom-to-ak',
'mapboxgl-ctrl-zoom-to-hi',
'mapboxgl-ctrl-zoom-to-pr',
'mapboxgl-ctrl-zoom-to-gu',
// 'mapboxgl-ctrl-zoom-to-gu',
'mapboxgl-ctrl-zoom-to-as',
'mapboxgl-ctrl-zoom-to-mp',
'mapboxgl-ctrl-zoom-to-vi',
// 'mapboxgl-ctrl-zoom-to-vi',
];
return (

View file

@ -30,7 +30,7 @@ export const FEATURE_TILE_LOW_ZOOM_URL = featureURLForTilesetName('low');
export const PERFORMANCE_MARKER_MAP_IDLE = 'MAP_IDLE';
// Properties
export const SCORE_PROPERTY_HIGH = 'Definition L (percentile)';
export const SCORE_PROPERTY_HIGH = 'SL_PFS';
export const SCORE_PROPERTY_LOW = 'L_SCORE';
export const GEOID_PROPERTY = 'GEOID10';
export const HIGH_SCORE_SOURCE_NAME = 'score-high';
@ -42,28 +42,73 @@ export const CURRENTLY_SELECTED_FEATURE_HIGHLIGHT_LAYER_NAME = 'currently-select
export const BLOCK_GROUP_BOUNDARY_LAYER_NAME = 'block-group-boundary-layer';
// Properties
export const POVERTY_PROPERTY_PERCENTILE = 'Poverty (Less than 200% of federal poverty line) (percentile)';
export const HOUSING_BURDEN_PROPERTY_PERCENTILE = 'Housing burden (percent) (percentile)';
export const LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE = 'Linguistic isolation (percent) (percentile)';
export const UNEMPLOYMENT_PROPERTY_PERCENTILE = 'Unemployed civilians (percent) (percentile)';
export const TOTAL_POPULATION = 'Total population';
export const EDUCATION_PROPERTY_PERCENTILE =
`Percent individuals age 25 or over with less than high school degree (percentile)`;
export const COUNTY_NAME = 'County Name';
export const STATE_NAME = 'State Name';
export const DIABETES_PERCENTILE = 'Diagnosed diabetes among adults aged >=18 years (percentile)';
export const ASTHMA_PERCENTILE = 'Current asthma among adults aged >=18 years (percentile)';
export const HEART_PERCENTILE = 'Coronary heart disease among adults aged >=18 years (percentile)';
export const LIFE_PERCENTILE = 'Life expectancy (years) (percentile)';
export const TRAFFIC_PERCENTILE = 'Traffic proximity and volume (percentile)';
export const FEMA_PERCENTILE = 'FEMA Risk Index Expected Annual Loss Score (percentile)';
export const ENERGY_PERCENTILE = 'Energy burden (percentile)';
export const WASTEWATER_PERCENTILE = 'Wastewater discharge (percentile)';
export const LEAD_PAINT_PERCENTILE = 'Percent pre-1960s housing (lead paint indicator) (percentile)';
export const DIESEL_MATTER_PERCENTILE = 'Diesel particulate matter (percentile)';
export const PM25_PERCENTILE = 'Particulate matter (PM2.5) (percentile)';
export const AREA_MEDIAN_INCOME_PERCENTILE = 'Median household income (% of AMI) (percentile)';
// Indicator values:
export const ASTHMA_PERCENTILE = 'AF_PFS';
export const COUNTY_NAME = 'CF';
export const DIABETES_PERCENTILE = 'DF_PFS';
export const DIESEL_MATTER_PERCENTILE = 'DSF_PFS';
export const ENERGY_PERCENTILE = 'EBF_PFS';
export const HEART_PERCENTILE = 'HDF_PFS';
export const HIGH_SCHOOL_PROPERTY_PERCENTILE = `HSEF`;
export const HOUSING_BURDEN_PROPERTY_PERCENTILE = 'HBF_PFS';
export const LEAD_PAINT_PERCENTILE = 'LPF_PFS';
export const LIFE_PERCENTILE = 'LLEF_PFS';
export const LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE = 'LIF_PFS';
export const LOW_MEDIAN_INCOME_PERCENTILE = 'LMI_PFS';
export const PM25_PERCENTILE = 'PM25F_PFS';
export const POVERTY_PROPERTY_PERCENTILE = 'P200_PFS';
export const STATE_NAME = 'SF';
export const TOTAL_POPULATION = 'TPF';
export const TRAFFIC_PERCENTILE = 'TF_PFS';
export const UNEMPLOYMENT_PROPERTY_PERCENTILE = 'UF_PFS';
export const WASTEWATER_PERCENTILE = 'WF_PFS';
export const EXP_AGRICULTURE_LOSS_PERCENTILE = 'EALR_PFS';
export const EXP_BUILDING_LOSS_PERCENTILE = 'EBLR_PFS';
export const EXP_POPULATION_LOSS_PERCENTILE = 'EPLR_PFS';
export const MEDIAN_HOME_VALUE_PERCENTILE = 'MHVF_PFS';
export const POVERTY_BELOW_100_PERCENTILE = 'P100_PFS';
export const POVERTY_BELOW_200_PERCENTILE = 'P200_PFS';
export const PROXIMITY_NPL_SITES_PERCENTILE = 'NPL_PFS';
export const PROXIMITY_RMP_SITES_PERCENTILE = 'RMP_PFS';
export const PROXIMITY_TSDF_SITES_PERCENTILE = 'TSDF_PFS';
// Category booleans (disadvantaged or not):
export const IS_CLIMATE_FACTOR_DISADVANTAGED_L = 'L_CLT';
export const IS_WORKFORCE_FACTOR_DISADVANTAGED_L = 'L_WKFC';
export const IS_WATER_FACTOR_DISADVANTAGED_L = 'L_WTR';
export const IS_ENERGY_FACTOR_DISADVANTAGED_L = 'L_ENY';
export const IS_TRANSPORT_FACTOR_DISADVANTAGED_L = 'L_TRN';
export const IS_HOUSING_FACTOR_DISADVANTAGED_L = 'L_HSG';
export const IS_POLLUTION_FACTOR_DISADVANTAGED_L = 'L_PLN';
export const IS_HEALTH_FACTOR_DISADVANTAGED_L = 'L_HLTH';
// Indicator booleans (disadvangted or not): (GTE = greater than or equal)
export const IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME = 'EPLRLI';
export const IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME = 'EALRLI';
export const IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME = 'EBLRLI';
export const IS_GTE_90_PM25_AND_IS_LOW_INCOME = 'PM25LI';
export const IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME = 'EBLI';
export const IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME = 'DPMLI';
export const IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME = 'TPLI';
export const IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME = 'LPMHVLI';
export const IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME = 'HBLI';
export const IS_GTE_90_RMP_AND_IS_LOW_INCOME = 'RMPLI';
export const IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME = 'SFLI';
export const IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME = 'HWLI';
export const IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME = 'WDLI';
export const IS_GTE_90_DIABETES_AND_IS_LOW_INCOME = 'DLI';
export const IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME = 'ALI';
export const IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME = 'HDLI';
export const IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME = 'LLELI';
export const IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME = 'LILHSE';
export const IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU = 'PLHSE';
export const IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU = 'LMILHSE';
export const IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU = 'ULHSE';
export const IS_FEDERAL_POVERTY_LEVEL_200 = 'FPL200S';
export const TOTAL_THRESHOLD_CRITERIA = 'TC';
export const IS_GTE_90_ISLAND_AREA_UNEMPLOYMENT_AND_IS_LOW_HS_EDU_2009 = 'IAULHSE';
export const IS_GTE_90_ISLAND_AREA_BELOW_100_POVERTY_AND_IS_LOW_HS_EDU_2009 = 'ISPLHSE';
export const IS_GTE_90_ISLAND_AREA_LOW_MEDIAN_INCOME_AND_IS_LOW_HS_EDU_2009 = 'IALMILHSE';
export type J40Properties = { [key: string]: any };
@ -136,7 +181,7 @@ export const DEFAULT_OUTLINE_COLOR = '#4EA5CF';
export const MIN_COLOR = '#FFFFFF';
export const MED_COLOR = '#D1DAE6';
export const MAX_COLOR = '#768FB3';
export const BORDER_HIGHLIGHT_COLOR = '#00BDE3';
export const BORDER_HIGHLIGHT_COLOR = '#1A4480';
export const CURRENTLY_SELECTED_FEATURE_LAYER_OPACITY = 0.5;
// Widths

View file

@ -153,6 +153,7 @@ export const MAP = defineMessages({
},
});
// Side Panel copy
export const SIDE_PANEL_INITIAL_STATE = defineMessages({
TITLE: {
@ -178,6 +179,17 @@ export const SIDE_PANEL_INITIAL_STATE = defineMessages({
},
});
export const SIDE_PANEL_VERION = {
TITLE: <FormattedMessage
id={'areaDetail.side.panel.version.title'}
defaultMessage={ 'Methodology version {version}'}
description={'methodology version number'}
values= {{
version: METHODOLOGY_COPY.VERSION_NUMBER,
}}
/>,
};
export const SIDE_PANEL_CBG_INFO = defineMessages({
CENSUS_BLOCK_GROUP: {
id: 'areaDetail.geographicInfo.censusBlockGroup',
@ -204,41 +216,148 @@ export const SIDE_PANEL_CBG_INFO = defineMessages({
export const COMMUNITY = {
OF_FOCUS: <FormattedMessage
id={'areaDetail.categorization.community.of.focus'}
defaultMessage={ 'Community of focus'}
defaultMessage={ 'YES'}
description={'the communities the score currently is focused on'}
/>,
NOT_OF_FOCUS: <FormattedMessage
id= {'areaDetail.categorization.not.community.of.focus'}
defaultMessage= {'Not a community of focus'}
defaultMessage= {'No'}
description= {'the communities the score currently is not focused on'}
/>,
IS_FOCUS: <FormattedMessage
id={'areaDetail.categorization.is.community.of.focus'}
defaultMessage={ 'Identified as disadvantaged?'}
description={'asking IF the communities is focused on'}
/>,
SEND_FEEDBACK: <FormattedMessage
id={'areaDetail.categorization.send.feedback'}
defaultMessage={ 'send feedback'}
description={'link to send feedback'}
/>,
};
export const SIDE_PANEL_CATEGORY = defineMessages({
CLIMATE: {
id: 'areaDetail.indicator.title.climate',
defaultMessage: 'Climate change',
description: 'Climate change title',
},
CLEAN_ENERGY: {
id: 'areaDetail.indicator.title.clean.energy',
defaultMessage: 'Clean, efficient energy ',
description: 'Clean, efficient energy title',
},
CLEAN_TRANSPORT: {
id: 'areaDetail.indicator.title.clean.transport',
defaultMessage: 'Clean transportation',
description: 'Clean transportation title',
},
SUSTAIN_HOUSE: {
id: 'areaDetail.indicator.title.sustain.house',
defaultMessage: 'Sustainable housing',
description: 'Sustainable housing title',
},
LEG_POLLUTE: {
id: 'areaDetail.indicator.title.legacy.pollution',
defaultMessage: 'Legacy pollution',
description: 'Legacy pollution title',
},
CLEAN_WATER: {
id: 'areaDetail.indicator.title.clean.water',
defaultMessage: 'Clean water and waste',
description: 'Clean water and waste title',
},
HEALTH_BURDEN: {
id: 'areaDetail.indicator.title.health.burden',
defaultMessage: 'Health burdens',
description: 'Health burdens title',
},
WORK_DEV: {
id: 'areaDetail.indicator.title.work.dev',
defaultMessage: 'Workforce development',
description: 'Workforce development title',
},
});
export const SIDE_PANEL_INDICATORS = defineMessages({
INDICATOR_COLUMN_HEADER: {
id: 'areaDetail.indicators.indicatorColumnHeader',
defaultMessage: 'Indicator',
description: 'the population of the feature selected',
EXP_AG_LOSS: {
id: 'areaDetail.indicator.exp.ag.loss',
defaultMessage: 'Expected agriculture loss rate',
description: 'agriculture loss rate',
},
PERCENTILE_COLUMN_HEADER: {
id: 'areaDetail.indicators.percentileColumnHeader',
defaultMessage: 'Percentile (0-100)',
description: 'the population of the feature selected',
EXP_BLD_LOSS: {
id: 'areaDetail.indicator.exp.bld.loss',
defaultMessage: 'Expected building loss rate',
description: 'building loss rate',
},
POVERTY: {
id: 'areaDetail.indicator.poverty',
defaultMessage: 'Poverty',
description: 'Household income is less than or equal to twice the federal "poverty level"',
EXP_POP_LOSS: {
id: 'areaDetail.indicator.exp.pop.loss',
defaultMessage: 'Expected population loss rate',
description: 'population loss rate',
},
AREA_MEDIAN_INCOME: {
id: 'areaDetail.indicator.areaMedianIncome',
defaultMessage: 'Area Median Income',
description: 'calculated as percent of the area median income',
LOW_INCOME: {
id: 'areaDetail.indicator.low.income',
defaultMessage: 'Low income',
description: 'low income',
},
EDUCATION: {
id: 'areaDetail.indicator.education',
defaultMessage: 'Education, less than high school',
description: 'Percent of people age 25 or older that didnt get a high school diploma',
ENERGY_BURDEN: {
id: 'areaDetail.indicator.energyBurden',
defaultMessage: 'Energy cost burden',
description: 'Average annual energy cost ($) divided by household income',
},
PM_2_5: {
id: 'areaDetail.indicator.pm25',
defaultMessage: 'PM2.5',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.dieselPartMatter',
defaultMessage: 'Diesel particulate matter',
description: 'Diesel particulate matter level in air',
},
TRAFFIC_VOLUME: {
id: 'areaDetail.indicator.trafficVolume',
defaultMessage: 'Traffic',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.leadPaint',
defaultMessage: 'Lead paint',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
MED_HOME_VAL: {
id: 'areaDetail.indicator.med.home.val',
defaultMessage: 'Median home value',
description: 'Median home value of owner-occupied housing units in the area.',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.houseBurden',
defaultMessage: 'Housing cost burden',
description: 'People ages 18 and up who report having been told by a doctor,' +
' nurse, or other health professionals that they have diabetes other than diabetes during pregnancy',
},
PROX_HAZ: {
id: 'areaDetail.indicator.prox.haz',
defaultMessage: 'Proximity to hazardous waste facilities',
description: 'Count of hazardous waste facilities ',
},
PROX_NPL: {
id: 'areaDetail.indicator.prox.npl',
defaultMessage: 'Proximity to NPL sites',
description: 'Count of proposed or listed NPL ',
},
PROX_RMP: {
id: 'areaDetail.indicator.prox.rmp',
defaultMessage: 'Proximity to RMP sites',
description: 'Count of proposed or listed RMP',
},
WASTE_WATER: {
id: 'areaDetail.indicator.wasteWater',
defaultMessage: 'Wastewater discharge',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
},
ASTHMA: {
id: 'areaDetail.indicator.asthma',
@ -250,164 +369,181 @@ export const SIDE_PANEL_INDICATORS = defineMessages({
defaultMessage: 'Diabetes',
description: 'diabetes from dr or nurse',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.dieselPartMatter',
defaultMessage: 'Diesel particulate matter',
description: 'Diesel particulate matter level in air',
},
ENERGY_BURDEN: {
id: 'areaDetail.indicator.energyBurden',
defaultMessage: 'Energy burden',
description: 'Average annual energy cost ($) divided by household income',
},
FEMA_RISK: {
id: 'areaDetail.indicator.femaRisk',
defaultMessage: 'FEMA Risk Index',
description: 'Risk based on 18 natural hazard types, in addition to a '+
'community\'s social vulnerability and community resilience',
},
HEART_DISEASE: {
id: 'areaDetail.indicator.heartDisease',
defaultMessage: 'Heart disease',
description: 'People ages 18 and up who report ever having been told by a' +
'doctor, nurse, or other health professionals that they had angina or coronary heart disease',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.houseBurden',
defaultMessage: 'Housing cost burden',
description: 'People ages 18 and up who report having been told by a doctor,' +
' nurse, or other health professionals that they have diabetes other than diabetes during pregnancy',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.leadPaint',
defaultMessage: 'Lead paint',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
LIFE_EXPECT: {
id: 'areaDetail.indicator.lifeExpect',
defaultMessage: 'Life expectancy',
defaultMessage: 'Low life expectancy',
description: 'Estimated years of life expectancy',
},
PM_2_5: {
id: 'areaDetail.indicator.pm25',
defaultMessage: 'PM2.5',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
LOW_MED_INC: {
id: 'areaDetail.indicator.low.med.income',
defaultMessage: 'Low median income',
description: 'Low median income',
},
TRAFFIC_VOLUME: {
id: 'areaDetail.indicator.trafficVolume',
defaultMessage: 'Traffic proximity and volume',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
LING_ISO: {
id: 'areaDetail.indicator.ling.iso',
defaultMessage: 'Linguistic isolation',
description: 'Linguistic isolation',
},
WASTE_WATER: {
id: 'areaDetail.indicator.wasteWater',
defaultMessage: 'Wastewater discharge',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
UNEMPLOY: {
id: 'areaDetail.indicator.unemploy',
defaultMessage: 'Unemployment',
description: 'Unemployment',
},
POVERTY: {
id: 'areaDetail.indicator.poverty',
defaultMessage: 'Poverty',
description: 'Unemployment',
},
HIGH_SCL: {
id: 'areaDetail.indicator.high.school',
defaultMessage: 'High school degree achievement rate',
description: 'High school degree achievement rate',
},
});
export const SIDE_PANEL_INDICATOR_DESCRIPTION = defineMessages({
AREA_MEDIAN_INCOME: {
id: 'areaDetail.indicator.description.area_median_income',
defaultMessage: 'Median income of the census tract calculated as a percent of the metropolitan'+
' areas or state\'s median income',
description: 'Median income of the census tract calculated as a percent of the metropolitan'+
' areas or state\'s median income',
EXP_AG_LOSS: {
id: 'areaDetail.indicator.description.exp.ag.loss',
defaultMessage: 'Economic loss rate to agriculture resulting from natural hazards',
description: 'Economic loss rate to agriculture resulting from natural hazards',
},
EDUCATION: {
id: 'areaDetail.indicator.description.education',
defaultMessage: 'Percent of people age 25 or older that didnt get a high school diploma',
description: 'Percent of people age 25 or older that didnt get a high school diploma',
EXP_BLD_LOSS: {
id: 'areaDetail.indicator.description.exp.bld.loss',
defaultMessage: 'Economic loss rate to buildings resulting from natural hazards',
description: 'Economic loss rate to buildings resulting from natural hazards',
},
POVERTY: {
id: 'areaDetail.indicator.description.poverty',
defaultMessage: 'Percent of a tract\'s population in households where the household income' +
' is at or below 100% of the federal poverty level',
description: 'Percent of a tract\'s population in households where the household income' +
' is at or below 100% of the federal poverty level',
EXP_POP_LOSS: {
id: 'areaDetail.indicator.description.exp.pop.loss',
defaultMessage: 'Economic loss rate to the population in fatalities and injuries resulting from natural hazards',
description: 'Economic loss rate to the population in fatalities and injuries resulting from natural hazards',
},
ASTHMA: {
id: 'areaDetail.indicator.description.asthma',
defaultMessage: 'People who answer “yes” to both of the questions: “Have you ever been told by' +
' a doctor nurse, or other health professional that you have asthma?” and “Do you still have asthma?"',
description: 'People who answer “yes” to both of the questions: “Have you ever been told by' +
' a doctor nurse, or other health professional that you have asthma?” and “Do you still have asthma?"',
},
DIABETES: {
id: 'areaDetail.indicator.description.diabetes',
defaultMessage: 'People ages 18 and up who report having been told by a doctor, nurse, or other' +
' health professionals that they have diabetes other than diabetes during pregnancy',
description: 'People ages 18 and up who report having been told by a doctor, nurse, or other' +
' health professionals that they have diabetes other than diabetes during pregnancy',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.description.dieselPartMatter',
defaultMessage: 'Mixture of particles that is part of diesel exhaust in the air',
description: 'Mixture of particles that is part of diesel exhaust in the air',
LOW_INCOME: {
id: 'areaDetail.indicator.description.low.income',
defaultMessage: 'Household income is less than or equal to twice the federal poverty level',
description: 'Household income is less than or equal to twice the federal poverty level',
},
ENERGY_BURDEN: {
id: 'areaDetail.indicator.description.energyBurden',
defaultMessage: 'Average annual energy cost ($) divided by household income',
description: 'Average annual energy cost ($) divided by household income',
},
FEMA_RISK: {
id: 'areaDetail.indicator.description.femaRisk',
defaultMessage: 'Expected Annual Loss Score, which is the average economic loss in dollars' +
' resulting from natural hazards each year.',
description: 'Expected Annual Loss Score, which is the average economic loss in dollars' +
' resulting from natural hazards each year.',
},
HEART_DISEASE: {
id: 'areaDetail.indicator.description.heartDisease',
defaultMessage: 'People ages 18 and up who report ever having been told by a' +
' doctor, nurse, or other health professionals that they had angina or coronary heart disease',
description: 'People ages 18 and up who report ever having been told by a' +
' doctor, nurse, or other health professionals that they had angina or coronary heart disease',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.description.houseBurden',
defaultMessage: 'Households that are low income and spend more than 30% of their income on' +
' housing costs',
description: 'Households that are low income and spend more than 30% of their income on' +
' housing costs',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.description.leadPaint',
defaultMessage: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
LIFE_EXPECT: {
id: 'areaDetail.indicator.description.lifeExpect',
defaultMessage: 'Estimated years of life expectancy',
description: 'Estimated years of life expectancy',
defaultMessage: 'Energy costs divided by household income',
description: 'Energy costs divided by household income',
},
PM_2_5: {
id: 'areaDetail.indicator.description.pm25',
defaultMessage: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
defaultMessage: 'Fine inhalable particles, 2.5 micrometers and smaller',
description: 'Fine inhalable particles, 2.5 micrometers and smaller',
},
DIESEL_PARTICULATE_MATTER: {
id: 'areaDetail.indicator.description.dieselPartMatter',
defaultMessage: 'Diesel exhaust in the air',
description: 'Diesel exhaust in the air',
},
TRAFFIC_VOLUME: {
id: 'areaDetail.indicator.description.trafficVolume',
defaultMessage: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
defaultMessage: 'Count of vehicles at major roads within 500 meters',
description: 'Count of vehicles at major roads within 500 meters',
},
LEAD_PAINT: {
id: 'areaDetail.indicator.description.leadPaint',
defaultMessage: 'Pre-1960 housing',
description: 'Pre-1960 housing',
},
MED_HOME_VAL: {
id: 'areaDetail.indicator.description.med.home.val',
defaultMessage: 'Meidan home value in area',
description: 'Meidan home value in area',
},
HOUSE_BURDEN: {
id: 'areaDetail.indicator.description.houseBurden',
defaultMessage: 'Low income households spending more than 30% of income on housing',
description: 'Low income households spending more than 30% of income on housing',
},
PROX_HAZ: {
id: 'areaDetail.indicator.description.prox.haz',
defaultMessage: 'Count of hazardous waste facilities within 5 km',
description: 'Count of hazardous waste facilities within 5 km',
},
PROX_NPL: {
id: 'areaDetail.indicator.description.prox.npl',
defaultMessage: 'Proposed or listed NPL (Superfund) sites within 5 km',
description: 'Proposed or listed NPL (Superfund) sites within 5 km',
},
PROX_RMP: {
id: 'areaDetail.indicator.description.prox.npl',
defaultMessage: 'Risk Management Plan facilities within 5 km',
description: 'Risk Management Plan facilities within 5 km',
},
WASTE_WATER: {
id: 'areaDetail.indicator.description.wasteWater',
defaultMessage: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
defaultMessage: 'Toxic concentrations at stream segments within 500 meters',
description: 'Toxic concentrations at stream segments within 500 meters',
},
ASTHMA: {
id: 'areaDetail.indicator.description.asthma',
defaultMessage: 'Number of people who have been told they have asthma',
description: 'Number of people who have been told they have asthma',
},
DIABETES: {
id: 'areaDetail.indicator.description.diabetes',
defaultMessage: 'People ages 18 years and older who have diabetes other than diabetes during pregnancy',
description: 'People ages 18 years and older who have diabetes other than diabetes during pregnancy',
},
HEART_DISEASE: {
id: 'areaDetail.indicator.description.heartDisease',
defaultMessage: 'People ages 18 years and older who have been told they have heart disease',
description: 'People ages 18 years and older who have been told they have heart disease',
},
LOW_LIFE_EXPECT: {
id: 'areaDetail.indicator.description.lifeExpect',
defaultMessage: 'Average number of years of life a person can expect to live',
description: 'Average number of years of life a person can expect to live',
},
LOW_MED_INCOME: {
id: 'areaDetail.indicator.description.low.med.income',
defaultMessage: 'Median income calculated as a percent of the areas median income',
description: 'Median income calculated as a percent of the areas median income',
},
LING_ISO: {
id: 'areaDetail.indicator.description.ling.iso',
defaultMessage: `Households in which no one age 14 and over speaks English only or also speaks
a language other than English`,
description: `Households in which no one age 14 and over speaks English only or also speaks
a language other than English`,
},
UNEMPLOY: {
id: 'areaDetail.indicator.description.unemploy',
defaultMessage: 'Number of unemployed people as a percentage of the labor force',
description: 'Number of unemployed people as a percentage of the labor force',
},
POVERTY: {
id: 'areaDetail.indicator.description.poverty',
defaultMessage: `Percent of individuals in households where the household income is at or
below 100% of the federal poverty level`,
description: `Percent of individuals in households where the household income is at or
below 100% of the federal poverty level`,
},
HIGH_SKL: {
id: 'areaDetail.indicator.description.high.school',
defaultMessage: 'Percent of people ages 25 years or older whose education level is less than a high school diploma',
description: 'Percent of people ages 25 years or older whose education level is less than a high school diploma',
},
});
export const DOWNLOAD_DRAFT = {
PARAGRAPH_1: <FormattedMessage
id={'download.draft.ptag.1'}
@ -440,6 +576,41 @@ export const DOWNLOAD_DRAFT = {
/>,
};
export const NOTE_ON_TERRITORIES = {
INTRO: <FormattedMessage
id={'explore.page.note.on.territories.intro'}
defaultMessage={`A note on the U.S. territories`}
description={`territories intro text`}
/>,
PARA_1: <FormattedMessage
id={'explore.page.note.on.territories.para.1'}
defaultMessage={`
The data sources described on the {dataMethLink} page are used to
identify disadvantaged communities for all 50 states and the District of Columbia. However, not all
of these data sources are currently available for the U.S. territories. The Census ACS data from
2015-2019 was used to identify disadvantaged communities for Puerto Rico. This uses the same methodology
as all 50 states and the District of Columbia for which data is available, which is all fields in
the Training and Workforce Development category.
`}
description={`territories paragraph 1`}
values={{
dataMethLink: <Link to="/methodology">Methodology & data</Link>,
}}
/>,
PARA_2: <FormattedMessage
id={'explore.page.note.on.territories.para.2'}
defaultMessage={`
For American Samoa, Northern Mariana Islands, Guam and U.S. Virgin Islands, the last reported data from
the Census Bureau is the Decennial Census from 2010. The Decennial Census data from 2010 was used
for American Samoa and Northern Mariana Islands using only the unemployment, poverty, area median
income, and high school degree achievement rate fields in the Training and Workforce Development
category of the methodology. Work is underway to identify disadvantaged communities and update the
CEJST accordingly for Guam and the U.S. Virgin Islands.
`}
description={`territories paragraph 2`}
/>,
};
export const HOW_YOU_CAN_HELP_LIST_ITEMS = {
HEADING: <FormattedMessage
id={'youCanHelpInfoText.heading'}

File diff suppressed because it is too large Load diff

View file

@ -140,22 +140,6 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
<div>
@ -277,24 +261,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<li
class="usa-nav__primary-item"
>
<div>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
<div />
</li>
</ul>
</nav>

View file

@ -140,22 +140,6 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div>
</div>
</section>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
</div>
<div>
@ -277,24 +261,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<li
class="usa-nav__primary-item"
>
<div>
<div>
<img
alt="language icon for selecting language"
src="test-file-stub"
/>
<a
href="#"
>
English
</a>
<a
href="#"
>
Español
</a>
</div>
</div>
<div />
</li>
</ul>
</nav>

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,14 @@ import * as React from 'react';
import {Grid} from '@trussworks/react-uswds';
import {useIntl} from 'gatsby-plugin-intl';
import Categories from '../components/Categories';
import DatasetContainer from '../components/DatasetContainer';
import DownloadPacket from '../components/DownloadPacket';
import J40MainGridContainer from '../components/J40MainGridContainer';
import MethodologyFormula from '../components/MethodologyFormula';
import Layout from '../components/layout';
import ScoreStepsList from '../components/scoreStepsList';
import LowIncome from '../components/LowIncome';
// import ScoreStepsList from '../components/scoreStepsList';
import * as METHODOLOGY_COPY from '../data/copy/methodology';
@ -14,7 +17,6 @@ interface MethodPageProps {
location: Location;
}
// markup
const IndexPage = ({location}: MethodPageProps) => {
const intl = useIntl();
@ -25,29 +27,44 @@ const IndexPage = ({location}: MethodPageProps) => {
<h1>{intl.formatMessage(METHODOLOGY_COPY.PAGE.HEADING)}</h1>
{/* First column */}
<Grid row gap className={'j40-mb-5'}>
<Grid col={12} tablet={{col: 6}}>
<Grid col={12} tablet={{col: 8}}>
<section>
<p>
{intl.formatMessage(METHODOLOGY_COPY.PAGE.DESCRIPTION)}
</p>
</section>
{/* Formula section */}
<MethodologyFormula />
{/* Category description */}
<section className={`j40-mt-7`}>
<p>
{intl.formatMessage(METHODOLOGY_COPY.PAGE.CATEGORY_TEXT)}
</p>
</section>
</Grid>
<Grid col={12} tablet={{col: 6}}>
{/* Second column */}
<Grid col={12} tablet={{col: 4}}>
<DownloadPacket />
<LowIncome />
</Grid>
</Grid>
</J40MainGridContainer>
<Categories />
<DatasetContainer/>
<J40MainGridContainer>
{/* <J40MainGridContainer>
<Grid row>
<Grid col>
<ScoreStepsList/>
</Grid>
</Grid>
</J40MainGridContainer>
</J40MainGridContainer> */}
</Layout>
);
};

View file

@ -81,14 +81,19 @@ p.flush {
@include j40-element('sm', 4, 'normal', 0);
}
// 24 pixel margin-bottom
.j40-mb-3 {
@include u-margin-bottom(3)
}
// 40 pixel margin-bottom
.j40-mb-5 {
@include u-margin-bottom(5)
}
// 24 pixel margin-bottom
.j40-mb-3 {
@include u-margin-bottom(3)
// 56 pixel margin-top
.j40-mt-7 {
@include u-margin-top(7)
}
.j40-footer-ceq-font {
@ -288,15 +293,6 @@ This section will outline styles that are component specific
}
}
//Area Detail Component
p.secondary.j40-indicator {
max-width: 10rem;
@media screen and (max-width: 1024px) {
max-width: 80%;
}
}
/*
***************************************
* TIMELINE / PROCESS LIST STYLES

View file

@ -319,13 +319,13 @@ Or use `false` for unneeded weights.
----------------------------------------
*/
$theme-font-weight-thin: false;
$theme-font-weight-thin: 100;
$theme-font-weight-light: 300;
$theme-font-weight-normal: 400;
$theme-font-weight-medium: false;
$theme-font-weight-semibold: false;
$theme-font-weight-medium: 500;
$theme-font-weight-semibold: 600;
$theme-font-weight-bold: 700;
$theme-font-weight-heavy: false;
$theme-font-weight-heavy: 800;
// If USWDS is generating your @font-face rules,
// should we generate all available weights

View file

@ -159,7 +159,7 @@ We use Docker to install the necessary libraries in a container that can be run
To build the docker container the first time, make sure you're in the root directory of the repository and run `docker-compose build --no-cache`.
Once completed, run `docker-compose up`. Docker will spin up 3 containers: the client container, the static server container and the data container. Once all data is generated, you can see the application using a browser and navigating to `htto://localhost:8000`.
Once completed, run `docker-compose up`. Docker will spin up 3 containers: the client container, the static server container and the data container. Once all data is generated, you can see the application using a browser and navigating to `http://localhost:8000`.
If you want to run specific data tasks, you can open a terminal window, navigate to the root folder for this repository and then execute any command for the application using this format:
@ -309,7 +309,7 @@ If you update the score in any way, it is necessary to create new pickles so tha
It starts with the `data_pipeline/etl/score/tests/sample_data/score_data_initial.csv`, which is the first two rows of the `score/full/usa.csv`.
To update this file, run a full score generation and then update the file as follows:
To update this file, run a full score generation, then open a Python shell from the `data-pipeline` directory (e.g. `poetry run python3`), and then update the file with the following commands:
```
import pickle
from pathlib import Path
@ -322,6 +322,8 @@ score_initial_df = pd.read_csv(score_csv_path, dtype={"GEOID10_TRACT": "string"}
score_initial_df.to_csv(data_path / "data_pipeline" / "etl" / "score" / "tests" / "sample_data" /"score_data_initial.csv", index=False)
```
Now you can move on to updating individual pickles for the tests. Note that it is helpful to do them in this order:
We have four pickle files that correspond to expected files:
- `score_data_expected.pkl`: Initial score without counties
- `score_transformed_expected.pkl`: Intermediate score with `etl._extract_score` and `etl. _transform_score` applied. There's no file for this intermediate process, so we need to capture the pickle mid-process.

View file

@ -1,7 +1,6 @@
from pathlib import Path
import datetime
import pandas as pd
from data_pipeline.config import settings
from data_pipeline.score import field_names
@ -38,6 +37,9 @@ FULL_SCORE_CSV_FULL_PLUS_COUNTIES_FILE_PATH = (
# Score Tile CSV source path
DATA_SCORE_CSV_TILES_PATH = DATA_SCORE_CSV_DIR / "tiles"
DATA_SCORE_CSV_TILES_FILE_PATH = DATA_SCORE_CSV_TILES_PATH / "usa.csv"
DATA_SCORE_JSON_INDEX_FILE_PATH = (
DATA_SCORE_CSV_TILES_PATH / "tile_indexes.json"
)
## Tile path
DATA_SCORE_TILES_DIR = DATA_SCORE_DIR / "tiles"
@ -60,113 +62,220 @@ SCORE_DOWNLOADABLE_ZIP_FILE_PATH = (
# Column subsets
CENSUS_COUNTIES_COLUMNS = ["USPS", "GEOID", "NAME"]
TILES_SCORE_COLUMNS = [
field_names.GEOID_TRACT_FIELD,
field_names.STATE_FIELD,
field_names.COUNTY_FIELD,
field_names.TOTAL_POP_FIELD,
field_names.SCORE_D + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_D + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_E + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_E + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_G_COMMUNITIES,
field_names.SCORE_G,
field_names.SCORE_L_COMMUNITIES,
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HIGH_SCHOOL_ED_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HEART_DISEASE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.FEMA_RISK_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
TILES_ROUND_NUM_DECIMALS = 2
# Tiles data: full field name, tile index name
TILES_SCORE_COLUMNS = {
field_names.GEOID_TRACT_FIELD: "GTF",
field_names.STATE_FIELD: "SF",
field_names.COUNTY_FIELD: "CF",
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "DF_PFS",
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "AF_PFS",
field_names.HEART_DISEASE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "HDF_PFS",
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "DSF_PFS",
field_names.ENERGY_BURDEN_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EBF_PFS",
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EALR_PFS",
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EBLR_PFS",
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "EPLR_PFS",
field_names.HOUSING_BURDEN_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "HBF_PFS",
field_names.LOW_LIFE_EXPECTANCY_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LLEF_PFS",
field_names.LINGUISTIC_ISO_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LIF_PFS",
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LMI_PFS",
field_names.MEDIAN_HOUSE_VALUE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "MHVF_PFS",
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "PM25F_PFS",
field_names.HIGH_SCHOOL_ED_FIELD: "HSEF",
field_names.POVERTY_LESS_THAN_100_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "P100_PFS",
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
]
+ field_names.PERCENTILE_FIELD_SUFFIX: "P200_PFS",
field_names.LEAD_PAINT_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "LPF_PFS",
field_names.NPL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "NPL_PFS",
field_names.RMP_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "RMP_PFS",
field_names.TSDF_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "TSDF_PFS",
field_names.TOTAL_POP_FIELD: "TPF",
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX: "TF_PFS",
field_names.UNEMPLOYMENT_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "UF_PFS",
field_names.WASTEWATER_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX: "WF_PFS",
field_names.L_WATER: "L_WTR",
field_names.L_WORKFORCE: "L_WKFC",
field_names.L_CLIMATE: "L_CLT",
field_names.L_ENERGY: "L_ENY",
field_names.L_TRANSPORTATION: "L_TRN",
field_names.L_HOUSING: "L_HSG",
field_names.L_POLLUTION: "L_PLN",
field_names.L_HEALTH: "L_HLTH",
field_names.SCORE_L_COMMUNITIES: "SL_C",
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX: "SL_PFS",
field_names.EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD: "EPLRLI",
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD: "EALRLI",
field_names.EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD: "EBLRLI",
field_names.PM25_EXPOSURE_LOW_INCOME_FIELD: "PM25LI",
field_names.ENERGY_BURDEN_LOW_INCOME_FIELD: "EBLI",
field_names.DIESEL_PARTICULATE_MATTER_LOW_INCOME_FIELD: "DPMLI",
field_names.TRAFFIC_PROXIMITY_LOW_INCOME_FIELD: "TPLI",
field_names.LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD: "LPMHVLI",
field_names.HOUSING_BURDEN_LOW_INCOME_FIELD: "HBLI",
field_names.RMP_LOW_INCOME_FIELD: "RMPLI",
field_names.SUPERFUND_LOW_INCOME_FIELD: "SFLI",
field_names.HAZARDOUS_WASTE_LOW_INCOME_FIELD: "HWLI",
field_names.WASTEWATER_DISCHARGE_LOW_INCOME_FIELD: "WDLI",
field_names.DIABETES_LOW_INCOME_FIELD: "DLI",
field_names.ASTHMA_LOW_INCOME_FIELD: "ALI",
field_names.HEART_DISEASE_LOW_INCOME_FIELD: "HDLI",
field_names.LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD: "LLELI",
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD: "LILHSE",
field_names.POVERTY_LOW_HS_EDUCATION_FIELD: "PLHSE",
field_names.LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD: "LMILHSE",
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD: "ULHSE",
field_names.LOW_HS_EDUCATION_FIELD: "LHE",
field_names.FPL_200_SERIES: "FPL200S",
field_names.THRESHOLD_COUNT: "TC",
field_names.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD: "IAULHSE",
field_names.ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD: "ISPLHSE",
field_names.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD: "IALMILHSE",
field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD: "IALHE",
}
# columns to round floats to 2 decimals
# TODO refactor to use much smaller subset of fields we DON'T want to round
TILES_SCORE_FLOAT_COLUMNS = [
field_names.SCORE_D + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_D + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_E + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_E + field_names.TOP_25_PERCENTILE_SUFFIX,
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.LINGUISTIC_ISO_FIELD,
field_names.UNEMPLOYMENT_FIELD,
field_names.HOUSING_BURDEN_FIELD,
field_names.POVERTY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HIGH_SCHOOL_ED_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HEART_DISEASE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.FEMA_RISK_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_HOUSE_VALUE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
field_names.POVERTY_LESS_THAN_100_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.NPL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.RMP_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TSDF_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX,
]
TILES_ROUND_NUM_DECIMALS = 2
DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_BASIC = [
field_names.AMI_FIELD,
field_names.POVERTY_LESS_THAN_100_FPL_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.DIABETES_FIELD,
field_names.ASTHMA_FIELD,
field_names.HEART_DISEASE_FIELD,
field_names.LIFE_EXPECTANCY_FIELD,
field_names.TRAFFIC_FIELD,
field_names.FEMA_RISK_FIELD,
field_names.ENERGY_BURDEN_FIELD,
field_names.HOUSING_BURDEN_FIELD,
field_names.WASTEWATER_FIELD,
field_names.LEAD_PAINT_FIELD,
field_names.DIESEL_FIELD,
field_names.PM25_FIELD,
field_names.TOTAL_POP_FIELD,
]
# For every indicator above, we want to include percentile and min-max normalized variants also
DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_FULL = list(
pd.core.common.flatten(
[
[p, f"{p} (percentile)"]
for p in DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_BASIC
]
)
)
# Finally we augment with the GEOID10, county, and state
DOWNLOADABLE_SCORE_COLUMNS = [
field_names.GEOID_TRACT_FIELD,
field_names.COUNTY_FIELD,
field_names.STATE_FIELD,
field_names.SCORE_G_COMMUNITIES,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_STATE_FIELD
field_names.SCORE_L_COMMUNITIES,
field_names.TOTAL_POP_FIELD,
field_names.FPL_200_SERIES,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
*DOWNLOADABLE_SCORE_INDICATOR_COLUMNS_FULL,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD,
field_names.EXPECTED_BUILDING_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD,
field_names.EXPECTED_POPULATION_LOSS_RATE_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD,
field_names.ENERGY_BURDEN_FIELD,
field_names.ENERGY_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ENERGY_BURDEN_LOW_INCOME_FIELD,
field_names.PM25_FIELD,
field_names.PM25_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.PM25_EXPOSURE_LOW_INCOME_FIELD,
field_names.DIESEL_FIELD,
field_names.DIESEL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIESEL_PARTICULATE_MATTER_LOW_INCOME_FIELD,
field_names.TRAFFIC_FIELD,
field_names.TRAFFIC_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TRAFFIC_PROXIMITY_LOW_INCOME_FIELD,
field_names.HOUSING_BURDEN_FIELD,
field_names.HOUSING_BURDEN_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HOUSING_BURDEN_LOW_INCOME_FIELD,
field_names.LEAD_PAINT_FIELD,
field_names.LEAD_PAINT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD,
field_names.MEDIAN_HOUSE_VALUE_FIELD,
field_names.MEDIAN_HOUSE_VALUE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.TSDF_FIELD,
field_names.TSDF_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HAZARDOUS_WASTE_LOW_INCOME_FIELD,
field_names.NPL_FIELD,
field_names.NPL_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.SUPERFUND_LOW_INCOME_FIELD,
field_names.RMP_FIELD,
field_names.RMP_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.RMP_LOW_INCOME_FIELD,
field_names.WASTEWATER_FIELD,
field_names.WASTEWATER_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.WASTEWATER_DISCHARGE_LOW_INCOME_FIELD,
field_names.ASTHMA_FIELD,
field_names.ASTHMA_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.ASTHMA_LOW_INCOME_FIELD,
field_names.DIABETES_FIELD,
field_names.DIABETES_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.DIABETES_LOW_INCOME_FIELD,
field_names.HEART_DISEASE_FIELD,
field_names.HEART_DISEASE_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.HEART_DISEASE_LOW_INCOME_FIELD,
field_names.LIFE_EXPECTANCY_FIELD,
field_names.LOW_LIFE_EXPECTANCY_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
field_names.LINGUISTIC_ISO_FIELD,
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD,
field_names.UNEMPLOYMENT_FIELD,
field_names.UNEMPLOYMENT_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LINGUISTIC_ISO_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LESS_THAN_100_FPL_FIELD,
field_names.POVERTY_LESS_THAN_100_FPL_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX,
field_names.POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD,
field_names.HIGH_SCHOOL_ED_FIELD + field_names.PERCENTILE_FIELD_SUFFIX,
field_names.LOW_HS_EDUCATION_FIELD,
field_names.THRESHOLD_COUNT,
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.COMBINED_UNEMPLOYMENT_2010,
field_names.CENSUS_DECENNIAL_UNEMPLOYMENT_FIELD_2009,
field_names.COMBINED_POVERTY_LESS_THAN_100_FPL_FIELD_2010,
field_names.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD,
]

View file

@ -404,9 +404,7 @@ class ScoreETL(ExtractTransformLoad):
field_names.POVERTY_LESS_THAN_150_FPL_FIELD,
field_names.POVERTY_LESS_THAN_200_FPL_FIELD,
field_names.AMI_FIELD,
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
field_names.MEDIAN_INCOME_FIELD,
field_names.LIFE_EXPECTANCY_FIELD,
field_names.ENERGY_BURDEN_FIELD,
field_names.FEMA_RISK_FIELD,
field_names.URBAN_HEURISTIC_FIELD,
@ -439,7 +437,6 @@ class ScoreETL(ExtractTransformLoad):
field_names.CENSUS_UNEMPLOYMENT_FIELD_2010,
field_names.CENSUS_POVERTY_LESS_THAN_100_FPL_FIELD_2010,
field_names.CENSUS_DECENNIAL_TOTAL_POPULATION_FIELD_2009,
field_names.CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009,
field_names.EXTREME_HEAT_FIELD,
field_names.HEALTHY_FOOD_FIELD,
field_names.IMPENETRABLE_SURFACES_FIELD,
@ -468,7 +465,19 @@ class ScoreETL(ExtractTransformLoad):
ReversePercentile(
field_name=field_names.READING_FIELD,
low_field_name=field_names.LOW_READING_FIELD,
)
),
ReversePercentile(
field_name=field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
low_field_name=field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD,
),
ReversePercentile(
field_name=field_names.LIFE_EXPECTANCY_FIELD,
low_field_name=field_names.LOW_LIFE_EXPECTANCY_FIELD,
),
ReversePercentile(
field_name=field_names.CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009,
low_field_name=field_names.LOW_CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009,
),
]
columns_to_keep = (
@ -505,10 +514,6 @@ class ScoreETL(ExtractTransformLoad):
max_value = df_copy[numeric_column].max(skipna=True)
logger.info(
f"For data set {numeric_column}, the min value is {min_value} and the max value is {max_value}."
)
df_copy[f"{numeric_column}{field_names.MIN_MAX_FIELD_SUFFIX}"] = (
df_copy[numeric_column] - min_value
) / (max_value - min_value)

View file

@ -3,10 +3,12 @@ import pandas as pd
import geopandas as gpd
from data_pipeline.etl.base import ExtractTransformLoad
from data_pipeline.etl.score import constants
from data_pipeline.etl.sources.census.etl_utils import (
check_census_data_source,
)
from data_pipeline.etl.score.etl_utils import check_score_data_source
from data_pipeline.score import field_names
from data_pipeline.utils import get_module_logger
logger = get_module_logger(__name__)
@ -31,9 +33,19 @@ class GeoScoreETL(ExtractTransformLoad):
self.DATA_PATH / "census" / "geojson" / "us.json"
)
self.TARGET_SCORE_NAME = "Definition L (percentile)"
# Import the shortened name for Score L percentile ("SL_PFS") that's used on the
# tiles.
self.TARGET_SCORE_SHORT_FIELD = constants.TILES_SCORE_COLUMNS[
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX
]
self.TARGET_SCORE_RENAME_TO = "L_SCORE"
# Import the shortened name for tract ("GTF") that's used on the tiles.
self.TRACT_SHORT_FIELD = constants.TILES_SCORE_COLUMNS[
field_names.GEOID_TRACT_FIELD
]
self.GEOMETRY_FIELD_NAME = "geometry"
self.NUMBER_OF_BUCKETS = 10
self.geojson_usa_df: gpd.GeoDataFrame
@ -57,45 +69,52 @@ class GeoScoreETL(ExtractTransformLoad):
logger.info("Reading US GeoJSON (~6 minutes)")
self.geojson_usa_df = gpd.read_file(
self.CENSUS_USA_GEOJSON,
dtype={"GEOID10": "string"},
usecols=["GEOID10", "geometry"],
dtype={self.GEOID_FIELD_NAME: "string"},
usecols=[self.GEOID_FIELD_NAME, self.GEOMETRY_FIELD_NAME],
low_memory=False,
)
self.geojson_usa_df.head()
logger.info("Reading score CSV")
self.score_usa_df = pd.read_csv(
self.TILE_SCORE_CSV,
dtype={self.GEOID_TRACT_FIELD_NAME: "string"},
dtype={self.TRACT_SHORT_FIELD: "string"},
low_memory=False,
)
def transform(self) -> None:
# rename GEOID10_TRACT to GEOID10 on score to allow merging with Census GeoJSON
# Rename GEOID10_TRACT to GEOID10 on score to allow merging with Census GeoJSON
self.score_usa_df.rename(
columns={self.GEOID_TRACT_FIELD_NAME: "GEOID10"},
columns={self.TRACT_SHORT_FIELD: self.GEOID_FIELD_NAME},
inplace=True,
)
logger.info("Pruning Census GeoJSON")
fields = ["GEOID10", "geometry"]
fields = [self.GEOID_FIELD_NAME, self.GEOMETRY_FIELD_NAME]
self.geojson_usa_df = self.geojson_usa_df[fields]
logger.info("Merging and compressing score CSV with USA GeoJSON")
self.geojson_score_usa_high = self.score_usa_df.merge(
self.geojson_usa_df, on="GEOID10", how="left"
self.geojson_usa_df, on=self.GEOID_FIELD_NAME, how="left"
)
self.geojson_score_usa_high = gpd.GeoDataFrame(
self.geojson_score_usa_high, crs="EPSG:4326"
)
logger.info(f"Columns: {self.geojson_score_usa_high.columns}")
usa_simplified = self.geojson_score_usa_high[
["GEOID10", self.TARGET_SCORE_NAME, "geometry"]
[
self.GEOID_FIELD_NAME,
self.TARGET_SCORE_SHORT_FIELD,
self.GEOMETRY_FIELD_NAME,
]
].reset_index(drop=True)
usa_simplified.rename(
columns={self.TARGET_SCORE_NAME: self.TARGET_SCORE_RENAME_TO},
columns={
self.TARGET_SCORE_SHORT_FIELD: self.TARGET_SCORE_RENAME_TO
},
inplace=True,
)
@ -104,7 +123,7 @@ class GeoScoreETL(ExtractTransformLoad):
usa_tracts = gpd.GeoDataFrame(
usa_tracts,
columns=[self.TARGET_SCORE_RENAME_TO, "geometry"],
columns=[self.TARGET_SCORE_RENAME_TO, self.GEOMETRY_FIELD_NAME],
crs="EPSG:4326",
)
@ -122,7 +141,7 @@ class GeoScoreETL(ExtractTransformLoad):
self.geojson_score_usa_low = gpd.GeoDataFrame(
compressed,
columns=[self.TARGET_SCORE_RENAME_TO, "geometry"],
columns=[self.TARGET_SCORE_RENAME_TO, self.GEOMETRY_FIELD_NAME],
crs="EPSG:4326",
)
@ -135,7 +154,7 @@ class GeoScoreETL(ExtractTransformLoad):
) -> gpd.GeoDataFrame:
# The tract identifier is the first 11 digits of the GEOID
block_group_df["tract"] = block_group_df.apply(
lambda row: row["GEOID10"][0:11], axis=1
lambda row: row[self.GEOID_FIELD_NAME][0:11], axis=1
)
state_tracts = block_group_df.dissolve(by="tract", aggfunc="mean")
return state_tracts
@ -160,7 +179,7 @@ class GeoScoreETL(ExtractTransformLoad):
[
self.TARGET_SCORE_RENAME_TO,
f"{self.TARGET_SCORE_RENAME_TO}_bucket",
"geometry",
self.GEOMETRY_FIELD_NAME,
]
].reset_index(drop=True)
state_dissolve = state_attr.dissolve(
@ -173,11 +192,13 @@ class GeoScoreETL(ExtractTransformLoad):
) -> gpd.GeoDataFrame:
compressed = []
for i in range(num_buckets):
for j in range(len(state_bucketed_df["geometry"][i].geoms)):
for j in range(
len(state_bucketed_df[self.GEOMETRY_FIELD_NAME][i].geoms)
):
compressed.append(
[
state_bucketed_df[self.TARGET_SCORE_RENAME_TO][i],
state_bucketed_df["geometry"][i].geoms[j],
state_bucketed_df[self.GEOMETRY_FIELD_NAME][i].geoms[j],
]
)
return compressed

View file

@ -1,5 +1,7 @@
from pathlib import Path
import json
import pandas as pd
from data_pipeline.etl.base import ExtractTransformLoad
from data_pipeline.utils import get_module_logger, zip_files
from data_pipeline.score import field_names
@ -198,16 +200,37 @@ class PostScoreETL(ExtractTransformLoad):
self, score_county_state_merged_df: pd.DataFrame
) -> pd.DataFrame:
logger.info("Rounding Decimals")
score_tiles = score_county_state_merged_df[
constants.TILES_SCORE_COLUMNS
]
# grab all the keys from tiles score columns
tiles_score_column_titles = list(constants.TILES_SCORE_COLUMNS.keys())
# filter the columns on full score
score_tiles = score_county_state_merged_df[tiles_score_column_titles]
# round decimals
decimals = pd.Series(
[constants.TILES_ROUND_NUM_DECIMALS]
* len(constants.TILES_SCORE_FLOAT_COLUMNS),
index=constants.TILES_SCORE_FLOAT_COLUMNS,
)
score_tiles = score_tiles.round(decimals)
return score_tiles.round(decimals)
# create indexes
score_tiles = score_tiles.rename(
columns=constants.TILES_SCORE_COLUMNS,
inplace=False,
)
# write the json map to disk
inverse_tiles_columns = {
v: k for k, v in constants.TILES_SCORE_COLUMNS.items()
} # reverse dict
index_file_path = constants.DATA_SCORE_JSON_INDEX_FILE_PATH
index_file_path.parent.mkdir(parents=True, exist_ok=True)
with open(index_file_path, "w", encoding="utf-8") as fp:
json.dump(inverse_tiles_columns, fp)
return score_tiles
def _create_downloadable_data(
self, score_county_state_merged_df: pd.DataFrame

File diff suppressed because one or more lines are too long

View file

@ -136,7 +136,7 @@ class CensusETL(ExtractTransformLoad):
def transform(self) -> None:
"""Download all census shape files from the Census FTP and extract the geojson
to generate national and by state Census Block Group CSVs and GeoJSONs
to generate national and by state Census tract CSVs and GeoJSONs
Returns:
None
@ -225,7 +225,7 @@ class CensusETL(ExtractTransformLoad):
logger.info("Writing national geojson file")
usa_df.to_file(self.NATIONAL_TRACT_JSON_PATH, driver="GeoJSON")
logger.info("Census block groups downloading complete")
logger.info("Census tract downloading complete")
def load(self) -> None:
"""Create state CSVs, National CSV, and National GeoJSON

View file

@ -8,7 +8,10 @@
"outputs": [],
"source": [
"import IPython\n",
"import os\n",
"import pandas as pd\n",
"import pathlib\n",
"import sys\n",
"\n",
"module_path = os.path.abspath(os.path.join(\"../..\"))\n",
"if module_path not in sys.path:\n",
@ -28,12 +31,8 @@
"outputs": [],
"source": [
"# Load\n",
"path_to_score_file_1 = (\n",
" DATA_DIR / \"score\" / \"csv\" / \"full\" / \"usa1.csv\"\n",
")\n",
"path_to_score_file_2 = (\n",
" DATA_DIR / \"score\" / \"csv\" / \"full\" / \"usa2.csv\"\n",
")\n",
"path_to_score_file_1 = DATA_DIR / \"compare_two_score_csvs/usa (pre 970).csv\"\n",
"path_to_score_file_2 = DATA_DIR / \"compare_two_score_csvs/usa (post 970).csv\"\n",
"\n",
"score_1_df = pd.read_csv(\n",
" path_to_score_file_1,\n",
@ -55,7 +54,7 @@
"metadata": {},
"outputs": [],
"source": [
"# List columns in one but not the other \n",
"# List columns in one but not the other\n",
"score_2_df.columns.difference(score_1_df.columns)"
]
},
@ -68,11 +67,16 @@
"source": [
"# List rows in one but not the other\n",
"\n",
"if len(score_2_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME]) != len(score_1_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME]):\n",
"if len(score_2_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME]) != len(\n",
" score_1_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME]\n",
"):\n",
" print(\"Different lengths!\")\n",
"\n",
"print(\"Difference in tract IDs:\")\n",
"print(set(score_2_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME]) ^ set(score_1_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME]))\n"
"print(\n",
" set(score_2_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME])\n",
" ^ set(score_1_df[ExtractTransformLoad.GEOID_TRACT_FIELD_NAME])\n",
")"
]
},
{
@ -82,8 +86,13 @@
"metadata": {},
"outputs": [],
"source": [
"# Join \n",
"merged_df = score_1_df.merge(score_2_df, how=\"outer\", on=ExtractTransformLoad.GEOID_TRACT_FIELD_NAME, suffixes=('_1', '_2'))\n",
"# Join\n",
"merged_df = score_1_df.merge(\n",
" score_2_df,\n",
" how=\"outer\",\n",
" on=ExtractTransformLoad.GEOID_TRACT_FIELD_NAME,\n",
" suffixes=(\"_1\", \"_2\"),\n",
")\n",
"merged_df"
]
},
@ -94,14 +103,34 @@
"metadata": {},
"outputs": [],
"source": [
"# Check each duplicate column: \n",
"# Check each duplicate column:\n",
"# Remove the suffix \"_1\"\n",
"duplicate_columns = [x[:-2] for x in merged_df.columns if \"_1\" in x]\n",
"\n",
"for duplicate_column in duplicate_columns:\n",
" print(f\"Checking duplicate column {duplicate_column}\")\n",
" if not merged_df[f\"{duplicate_column}_1\"].equals(merged_df[f\"{duplicate_column}_2\"]):\n",
" print(merged_df[f\"{duplicate_column}_1\"].compare(merged_df[f\"{duplicate_column}_2\"]))\n",
" raise ValueError(f\"Error! Different values in {duplicate_column}\")"
"columns_to_exclude_from_duplicates_check = [\"Total threshold criteria exceeded\"]\n",
"\n",
"columns_to_check = [\n",
" column\n",
" for column in duplicate_columns\n",
" if column not in columns_to_exclude_from_duplicates_check\n",
"]\n",
"\n",
"any_errors_found = False\n",
"for column_to_check in columns_to_check:\n",
" print(f\"Checking duplicate column {column_to_check}\")\n",
" if not merged_df[f\"{column_to_check}_1\"].equals(\n",
" merged_df[f\"{column_to_check}_2\"]\n",
" ):\n",
" print(f\"Error! Different values in {column_to_check}\")\n",
" print(\n",
" merged_df[f\"{column_to_check}_1\"].compare(\n",
" merged_df[f\"{column_to_check}_2\"]\n",
" )\n",
" )\n",
" any_errors_found = True\n",
"\n",
"if any_errors_found:\n",
" raise ValueError(f\"Error! Different values in one or more columns.\")"
]
}
],

View file

@ -60,11 +60,15 @@ MEDIAN_INCOME_FIELD = "Median household income in the past 12 months"
MEDIAN_INCOME_AS_PERCENT_OF_STATE_FIELD = (
"Median household income (% of state median household income)"
)
MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD = "Median household income (% of AMI)"
PERSISTENT_POVERTY_FIELD = "Persistent Poverty Census Tract"
AMI_FIELD = "Area Median Income (State or metropolitan)"
COLLEGE_ATTENDANCE_FIELD = "Percent enrollment in college or graduate school"
MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD = (
"Median household income as a percent of area median income"
)
LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD = (
"Low median household income as a percent of area median income"
)
# Climate
FEMA_RISK_FIELD = "FEMA Risk Index Expected Annual Loss Score"
@ -105,7 +109,6 @@ ENERGY_BURDEN_FIELD = "Energy burden"
DIABETES_FIELD = "Diagnosed diabetes among adults aged >=18 years"
ASTHMA_FIELD = "Current asthma among adults aged >=18 years"
HEART_DISEASE_FIELD = "Coronary heart disease among adults aged >=18 years"
LIFE_EXPECTANCY_FIELD = "Life expectancy (years)"
CANCER_FIELD = "Cancer (excluding skin cancer) among adults aged >=18 years"
HEALTH_INSURANCE_FIELD = (
"Current lack of health insurance among adults aged 18-64 years"
@ -113,6 +116,8 @@ HEALTH_INSURANCE_FIELD = (
PHYS_HEALTH_NOT_GOOD_FIELD = (
"Physical health not good for >=14 days among adults aged >=18 years"
)
LIFE_EXPECTANCY_FIELD = "Life expectancy (years)"
LOW_LIFE_EXPECTANCY_FIELD = "Low life expectancy"
# Other Demographics
TOTAL_POP_FIELD = "Total population"
@ -130,9 +135,6 @@ OVER_64_FIELD = "Individuals over 64 years old"
# Fields from 2010 decennial census (generally only loaded for the territories)
CENSUS_DECENNIAL_MEDIAN_INCOME_2009 = "Median household income in 2009 ($)"
CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009 = (
"Median household income as a percent of territory median income in 2009"
)
CENSUS_DECENNIAL_POVERTY_LESS_THAN_100_FPL_FIELD_2009 = (
"Percentage households below 100% of federal poverty line in 2009"
)
@ -141,7 +143,10 @@ CENSUS_DECENNIAL_UNEMPLOYMENT_FIELD_2009 = (
"Unemployed civilians (percent) in 2009"
)
CENSUS_DECENNIAL_TOTAL_POPULATION_FIELD_2009 = "Total population in 2009"
CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009 = (
"Median household income as a percent of territory median income in 2009"
)
LOW_CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009 = "Low median household income as a percent of territory median income in 2009"
# Fields from 2010 ACS (loaded for comparison with the territories)
CENSUS_UNEMPLOYMENT_FIELD_2010 = "Unemployed civilians (percent) in 2010"
CENSUS_POVERTY_LESS_THAN_100_FPL_FIELD_2010 = (
@ -225,16 +230,12 @@ IMPENETRABLE_SURFACES_FIELD = "Percent impenetrable surface areas"
READING_FIELD = "Third grade reading proficiency"
LOW_READING_FIELD = "Low third grade reading proficiency"
#####
# Names for individual factors being exceeded
# Climate Change
EXPECTED_POPULATION_LOSS_RATE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for expected population loss rate and is low income"
EXPECTED_AGRICULTURE_LOSS_RATE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for expected agriculture loss rate and is low income"
EXPECTED_BUILDING_LOSS_RATE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for expected building loss rate and is low income"
EXTREME_HEAT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for summer days above 90F and "
f"the median house value is less than {MEDIAN_HOUSE_VALUE_PERCENTILE}th "
f"percentile and is low income"
)
# Clean energy and efficiency
PM25_EXPOSURE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for PM2.5 exposure and is low income"
@ -252,17 +253,10 @@ LEAD_PAINT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD = (
)
HOUSING_BURDEN_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for housing burden and is low income"
IMPENETRABLE_SURFACES_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for impenetrable surfaces and is low "
f"income"
)
# Remediation and Reduction of Legacy Pollution
RMP_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for proximity to RMP sites and is low income"
SUPERFUND_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for proximity to superfund sites and is low income"
HAZARDOUS_WASTE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for proximity to hazardous waste facilities and is low income"
AIR_TOXICS_CANCER_RISK_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for air toxics cancer risk and is low income"
RESPIRATORY_HAZARD_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for respiratory hazard index and is low income"
# Critical Clean Water and Waste Infrastructure
WASTEWATER_DISCHARGE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for wastewater discharge and is low income"
@ -276,11 +270,9 @@ ASTHMA_LOW_INCOME_FIELD = (
)
HEART_DISEASE_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for heart disease and is low income"
LIFE_EXPECTANCY_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for life expectancy and is low income"
HEALTHY_FOOD_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for low "
f"access to healthy food and is low income"
LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile "
f"for low life expectancy and is low income"
)
# Workforce
@ -299,16 +291,58 @@ POVERTY_LOW_HS_EDUCATION_FIELD = (
" and has low HS education"
)
LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for low median household income as a "
f"percent of area median income and has low HS education"
)
LOW_HS_EDUCATION_FIELD = "Low high school education"
# Workforce for island areas
ISLAND_AREAS_SUFFIX = " in 2009 (island areas)"
ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for unemployment"
f" and has low HS education{ISLAND_AREAS_SUFFIX}"
)
ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for households at or below 100% federal poverty level"
f" and has low HS education{ISLAND_AREAS_SUFFIX}"
)
ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for low median household income as a "
f"percent of area median income"
f" and has low HS education{ISLAND_AREAS_SUFFIX}"
)
ISLAND_AREAS_LOW_HS_EDUCATION_FIELD = (
f"Low high school education{ISLAND_AREAS_SUFFIX}"
)
# Not currently used in a factor
EXTREME_HEAT_MEDIAN_HOUSE_VALUE_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for summer days above 90F and "
f"the median house value is less than {MEDIAN_HOUSE_VALUE_PERCENTILE}th "
f"percentile and is low income"
)
IMPENETRABLE_SURFACES_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for impenetrable surfaces and is low "
f"income"
)
AIR_TOXICS_CANCER_RISK_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for air toxics cancer risk and is low income"
RESPIRATORY_HAZARD_LOW_INCOME_FIELD = f"At or above the {PERCENTILE}th percentile for respiratory hazard index and is low income"
HEALTHY_FOOD_LOW_INCOME_FIELD = (
f"At or above the {PERCENTILE}th percentile for low "
f"access to healthy food and is low income"
)
LOW_READING_LOW_HS_EDUCATION_FIELD = (
f"At or above the {PERCENTILE}th percentile for low 3rd grade reading proficiency"
" and has low HS education"
)
MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD = (
f"At or below the {PERCENTILE}th percentile for median income"
" and has low HS education"
)
THRESHOLD_COUNT = "Total threshold criteria exceeded"
FPL_200_SERIES = "Is low income"
# End of names for individual factors being exceeded
####

View file

@ -44,6 +44,8 @@ class ScoreL(Score):
robustness over 1-year ACS.
"""
# Create the combined field.
# TODO: move this combined field percentile calculation to `etl_score`,
# since most other percentile logic is there.
# There should only be one entry in either 2009 or 2019 fields, not one in both.
# But just to be safe, we take the mean and ignore null values so if there
# *were* entries in both, this result would make sense.
@ -115,7 +117,7 @@ class ScoreL(Score):
"""
self.df[field_names.THRESHOLD_COUNT] += self.df[columns_for_subset].sum(
axis=1
axis=1, skipna=True
)
def add_columns(self) -> pd.DataFrame:
@ -160,16 +162,16 @@ class ScoreL(Score):
non_workforce_factors
].any(axis=1)
self.df["Definition L (percentile)"] = self.df[
field_names.SCORE_L_COMMUNITIES
].astype(int)
self.df[
field_names.SCORE_L + field_names.PERCENTILE_FIELD_SUFFIX
] = self.df[field_names.SCORE_L_COMMUNITIES].astype(int)
return self.df
def _climate_factor(self) -> bool:
# In Xth percentile or above for FEMAs Risk Index (Source: FEMA
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -225,7 +227,7 @@ class ScoreL(Score):
def _energy_factor(self) -> bool:
# In Xth percentile or above for DOEs energy cost burden score (Source: LEAD Score)
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -268,7 +270,7 @@ class ScoreL(Score):
# or
# In Xth percentile or above traffic proximity and volume (Source: 2017 U.S. Department of Transportation (DOT) traffic data
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -315,7 +317,7 @@ class ScoreL(Score):
# or
# In Xth percentile or above for housing cost burden (Source: HUD's Comprehensive Housing Affordability Strategy dataset
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -363,7 +365,7 @@ class ScoreL(Score):
def _pollution_factor(self) -> bool:
# Proximity to Risk Management Plan sites is > X
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -410,7 +412,7 @@ class ScoreL(Score):
def _water_factor(self) -> bool:
# In Xth percentile or above for wastewater discharge (Source: EPA Risk-Screening Environmental Indicators (RSEI) Model)
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -441,7 +443,7 @@ class ScoreL(Score):
# or
# In Xth percentile or above for low life expectancy (Source: CDC Places)
# AND
# Low income: In 60th percentile or above for percent of block group population
# Low income: In Nth percentile or above for percent of block group population
# of households where household income is less than or equal to twice the federal
# poverty level. Source: Census's American Community Survey]
@ -449,8 +451,7 @@ class ScoreL(Score):
field_names.DIABETES_LOW_INCOME_FIELD,
field_names.ASTHMA_LOW_INCOME_FIELD,
field_names.HEART_DISEASE_LOW_INCOME_FIELD,
field_names.HEALTHY_FOOD_LOW_INCOME_FIELD,
field_names.LIFE_EXPECTANCY_LOW_INCOME_FIELD,
field_names.LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD,
]
diabetes_threshold = (
@ -475,24 +476,14 @@ class ScoreL(Score):
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
healthy_food_threshold = (
low_life_expectancy_threshold = (
self.df[
field_names.HEALTHY_FOOD_FIELD
field_names.LOW_LIFE_EXPECTANCY_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
life_expectancy_threshold = (
self.df[
field_names.LIFE_EXPECTANCY_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
# Note: a high life expectancy is good, so take 1 minus the threshold to invert it,
# and then look for life expenctancies lower than that (not greater than).
<= 1 - self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
self.df[field_names.DIABETES_LOW_INCOME_FIELD] = (
diabetes_threshold & self.df[field_names.FPL_200_SERIES]
)
@ -502,11 +493,8 @@ class ScoreL(Score):
self.df[field_names.HEART_DISEASE_LOW_INCOME_FIELD] = (
heart_disease_threshold & self.df[field_names.FPL_200_SERIES]
)
self.df[field_names.LIFE_EXPECTANCY_LOW_INCOME_FIELD] = (
life_expectancy_threshold & self.df[field_names.FPL_200_SERIES]
)
self.df[field_names.HEALTHY_FOOD_LOW_INCOME_FIELD] = (
healthy_food_threshold & self.df[field_names.FPL_200_SERIES]
self.df[field_names.LOW_LIFE_EXPECTANCY_LOW_INCOME_FIELD] = (
low_life_expectancy_threshold & self.df[field_names.FPL_200_SERIES]
)
self._increment_total_eligibility_exceeded(health_eligibility_columns)
@ -514,26 +502,28 @@ class ScoreL(Score):
return self.df[health_eligibility_columns].any(axis="columns")
def _workforce_factor(self) -> bool:
# Where unemployment is above X%
# Where unemployment is above Xth percentile
# or
# Where median income is less than Y% of the area median income
# Where median income as a percent of area median income is above Xth percentile
# or
# Where the percent of households at or below 100% of the federal poverty level is greater than Z%
# Where the percent of households at or below 100% of the federal poverty level
# is above Xth percentile
# or
# Where linguistic isolation is greater than Y%
# Where linguistic isolation is above Xth percentile
# AND
# Where the high school degree achievement rates for adults 25 years and older is less than 95%
# (necessary to screen out university block groups)
# Where the high school degree achievement rates for adults 25 years and older
# is less than Y%
# (necessary to screen out university tracts)
# Workforce criteria for states fields.
workforce_eligibility_columns = [
field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD,
field_names.MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
field_names.LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
]
high_scool_achievement_rate_threshold = (
self.df[field_names.LOW_HS_EDUCATION_FIELD] = (
self.df[field_names.HIGH_SCHOOL_ED_FIELD]
>= self.LACK_OF_HIGH_SCHOOL_MINIMUM_THRESHOLD
)
@ -546,14 +536,12 @@ class ScoreL(Score):
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
median_income_threshold = (
low_median_income_threshold = (
self.df[
field_names.MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
field_names.LOW_MEDIAN_INCOME_AS_PERCENT_OF_AMI_FIELD
+ field_names.PERCENTILE_FIELD_SUFFIX
]
# Note: a high median income as a % of AMI is good, so take 1 minus the threshold to invert it.
# and then look for median income lower than that (not greater than).
<= 1 - self.ENVIRONMENTAL_BURDEN_THRESHOLD
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
linguistic_isolation_threshold = (
@ -574,19 +562,20 @@ class ScoreL(Score):
self.df[field_names.LINGUISTIC_ISOLATION_LOW_HS_EDUCATION_FIELD] = (
linguistic_isolation_threshold
& high_scool_achievement_rate_threshold
& self.df[field_names.LOW_HS_EDUCATION_FIELD]
)
self.df[field_names.POVERTY_LOW_HS_EDUCATION_FIELD] = (
poverty_threshold & high_scool_achievement_rate_threshold
poverty_threshold & self.df[field_names.LOW_HS_EDUCATION_FIELD]
)
self.df[field_names.MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD] = (
median_income_threshold & high_scool_achievement_rate_threshold
self.df[field_names.LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD] = (
low_median_income_threshold
& self.df[field_names.LOW_HS_EDUCATION_FIELD]
)
self.df[field_names.UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD] = (
unemployment_threshold & high_scool_achievement_rate_threshold
unemployment_threshold & self.df[field_names.LOW_HS_EDUCATION_FIELD]
)
workforce_combined_criteria_for_states = self.df[
@ -598,12 +587,16 @@ class ScoreL(Score):
)
# Now, calculate workforce criteria for island territories.
island_areas_workforce_eligibility_columns = [
field_names.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD,
field_names.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD,
]
# F a couple of values, create a combined field and criteria field.
# First, combine unemployment.
(
self.df,
unemployment_island_areas_criteria_field_name,
island_areas_unemployment_criteria_field_name,
) = self._combine_island_areas_with_states_and_set_thresholds(
df=self.df,
column_from_island_areas=field_names.CENSUS_DECENNIAL_UNEMPLOYMENT_FIELD_2009,
@ -615,7 +608,7 @@ class ScoreL(Score):
# Next, combine poverty.
(
self.df,
poverty_island_areas_criteria_field_name,
island_areas_poverty_criteria_field_name,
) = self._combine_island_areas_with_states_and_set_thresholds(
df=self.df,
column_from_island_areas=field_names.CENSUS_DECENNIAL_POVERTY_LESS_THAN_100_FPL_FIELD_2009,
@ -624,23 +617,54 @@ class ScoreL(Score):
threshold_cutoff_for_island_areas=self.ENVIRONMENTAL_BURDEN_THRESHOLD,
)
workforce_combined_criteria_for_island_areas = (
self.df[unemployment_island_areas_criteria_field_name]
| self.df[poverty_island_areas_criteria_field_name]
# Also check whether area median income is 10th percentile or lower
# within the islands.
| (
self.df[
field_names.CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009
+ field_names.PERCENTILE_FIELD_SUFFIX
]
# Note: a high median income as a % of AMI is good, so take 1 minus the threshold to invert it.
# and then look for median income lower than that (not greater than).
< 1 - self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
) & (
# Also check whether low area median income is 90th percentile or higher
# within the islands.
island_areas_low_median_income_as_a_percent_of_ami_criteria_field_name = (
f"{field_names.LOW_CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009} exceeds "
f"{field_names.PERCENTILE}th percentile"
)
self.df[
island_areas_low_median_income_as_a_percent_of_ami_criteria_field_name
] = (
self.df[
field_names.LOW_CENSUS_DECENNIAL_AREA_MEDIAN_INCOME_PERCENT_FIELD_2009
+ field_names.PERCENTILE_FIELD_SUFFIX
]
>= self.ENVIRONMENTAL_BURDEN_THRESHOLD
)
self.df[field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD] = (
self.df[field_names.CENSUS_DECENNIAL_HIGH_SCHOOL_ED_FIELD_2009]
> self.LACK_OF_HIGH_SCHOOL_MINIMUM_THRESHOLD
>= self.LACK_OF_HIGH_SCHOOL_MINIMUM_THRESHOLD
)
self.df[
field_names.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDUCATION_FIELD
] = (
self.df[island_areas_unemployment_criteria_field_name]
& self.df[field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD]
)
self.df[field_names.ISLAND_AREAS_POVERTY_LOW_HS_EDUCATION_FIELD] = (
self.df[island_areas_poverty_criteria_field_name]
& self.df[field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD]
)
self.df[
field_names.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDUCATION_FIELD
] = (
self.df[
island_areas_low_median_income_as_a_percent_of_ami_criteria_field_name
]
& self.df[field_names.ISLAND_AREAS_LOW_HS_EDUCATION_FIELD]
)
workforce_combined_criteria_for_island_areas = self.df[
island_areas_workforce_eligibility_columns
].any(axis="columns")
self._increment_total_eligibility_exceeded(
island_areas_workforce_eligibility_columns
)
percent_of_island_tracts_highlighted = (

View file

@ -791,7 +791,7 @@ toml = "*"
[[package]]
name = "lxml"
version = "4.6.3"
version = "4.6.5"
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
category = "main"
optional = false
@ -2065,54 +2065,66 @@ liccheck = [
{file = "liccheck-0.6.2.tar.gz", hash = "sha256:5667be7c9ef6496bd381e709e938e9fe51c31d601afc44965615cdfbce375eab"},
]
lxml = [
{file = "lxml-4.6.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"},
{file = "lxml-4.6.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"},
{file = "lxml-4.6.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"},
{file = "lxml-4.6.3-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"},
{file = "lxml-4.6.3-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"},
{file = "lxml-4.6.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"},
{file = "lxml-4.6.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"},
{file = "lxml-4.6.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:64812391546a18896adaa86c77c59a4998f33c24788cadc35789e55b727a37f4"},
{file = "lxml-4.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c1a40c06fd5ba37ad39caa0b3144eb3772e813b5fb5b084198a985431c2f1e8d"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354"},
{file = "lxml-4.6.3-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16"},
{file = "lxml-4.6.3-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"},
{file = "lxml-4.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"},
{file = "lxml-4.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"},
{file = "lxml-4.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617"},
{file = "lxml-4.6.3-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"},
{file = "lxml-4.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"},
{file = "lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"},
{file = "lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92"},
{file = "lxml-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"},
{file = "lxml-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"},
{file = "lxml-4.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"},
{file = "lxml-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"},
{file = "lxml-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"},
{file = "lxml-4.6.3-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e"},
{file = "lxml-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"},
{file = "lxml-4.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae"},
{file = "lxml-4.6.3-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"},
{file = "lxml-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"},
{file = "lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"},
{file = "lxml-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"},
{file = "lxml-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"},
{file = "lxml-4.6.3-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59"},
{file = "lxml-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"},
{file = "lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a"},
{file = "lxml-4.6.3-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"},
{file = "lxml-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"},
{file = "lxml-4.6.3.tar.gz", hash = "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468"},
{file = "lxml-4.6.5-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:abcf7daa5ebcc89328326254f6dd6d566adb483d4d00178892afd386ab389de2"},
{file = "lxml-4.6.5-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3884476a90d415be79adfa4e0e393048630d0d5bcd5757c4c07d8b4b00a1096b"},
{file = "lxml-4.6.5-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:add017c5bd6b9ec3a5f09248396b6ee2ce61c5621f087eb2269c813cd8813808"},
{file = "lxml-4.6.5-cp27-cp27m-win32.whl", hash = "sha256:a702005e447d712375433ed0499cb6e1503fadd6c96a47f51d707b4d37b76d3c"},
{file = "lxml-4.6.5-cp27-cp27m-win_amd64.whl", hash = "sha256:da07c7e7fc9a3f40446b78c54dbba8bfd5c9100dfecb21b65bfe3f57844f5e71"},
{file = "lxml-4.6.5-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a708c291900c40a7ecf23f1d2384ed0bc0604e24094dd13417c7e7f8f7a50d93"},
{file = "lxml-4.6.5-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f33d8efb42e4fc2b31b3b4527940b25cdebb3026fb56a80c1c1c11a4271d2352"},
{file = "lxml-4.6.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:f6befb83bca720b71d6bd6326a3b26e9496ae6649e26585de024890fe50f49b8"},
{file = "lxml-4.6.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:59d77bfa3bea13caee95bc0d3f1c518b15049b97dd61ea8b3d71ce677a67f808"},
{file = "lxml-4.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:68a851176c931e2b3de6214347b767451243eeed3bea34c172127bbb5bf6c210"},
{file = "lxml-4.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7790a273225b0c46e5f859c1327f0f659896cc72eaa537d23aa3ad9ff2a1cc1"},
{file = "lxml-4.6.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6548fc551de15f310dd0564751d9dc3d405278d45ea9b2b369ed1eccf142e1f5"},
{file = "lxml-4.6.5-cp310-cp310-win32.whl", hash = "sha256:dc8a0dbb2a10ae8bb609584f5c504789f0f3d0d81840da4849102ec84289f952"},
{file = "lxml-4.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:1ccbfe5d17835db906f2bab6f15b34194db1a5b07929cba3cf45a96dbfbfefc0"},
{file = "lxml-4.6.5-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca9a40497f7e97a2a961c04fa8a6f23d790b0521350a8b455759d786b0bcb203"},
{file = "lxml-4.6.5-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e5b4b0d9440046ead3bd425eb2b852499241ee0cef1ae151038e4f87ede888c4"},
{file = "lxml-4.6.5-cp35-cp35m-win32.whl", hash = "sha256:87f8f7df70b90fbe7b49969f07b347e3f978f8bd1046bb8ecae659921869202b"},
{file = "lxml-4.6.5-cp35-cp35m-win_amd64.whl", hash = "sha256:ce52aad32ec6e46d1a91ff8b8014a91538800dd533914bfc4a82f5018d971408"},
{file = "lxml-4.6.5-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8021eeff7fabde21b9858ed058a8250ad230cede91764d598c2466b0ba70db8b"},
{file = "lxml-4.6.5-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cab343b265e38d4e00649cbbad9278b734c5715f9bcbb72c85a1f99b1a58e19a"},
{file = "lxml-4.6.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:3534d7c468c044f6aef3c0aff541db2826986a29ea73f2ca831f5d5284d9b570"},
{file = "lxml-4.6.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdb98f4c9e8a1735efddfaa995b0c96559792da15d56b76428bdfc29f77c4cdb"},
{file = "lxml-4.6.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5ea121cb66d7e5cb396b4c3ca90471252b94e01809805cfe3e4e44be2db3a99c"},
{file = "lxml-4.6.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:121fc6f71c692b49af6c963b84ab7084402624ffbe605287da362f8af0668ea3"},
{file = "lxml-4.6.5-cp36-cp36m-win32.whl", hash = "sha256:1a2a7659b8eb93c6daee350a0d844994d49245a0f6c05c747f619386fb90ba04"},
{file = "lxml-4.6.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2f77556266a8fe5428b8759fbfc4bd70be1d1d9c9b25d2a414f6a0c0b0f09120"},
{file = "lxml-4.6.5-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:558485218ee06458643b929765ac1eb04519ca3d1e2dcc288517de864c747c33"},
{file = "lxml-4.6.5-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ba0006799f21d83c3717fe20e2707a10bbc296475155aadf4f5850f6659b96b9"},
{file = "lxml-4.6.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:916d457ad84e05b7db52700bad0a15c56e0c3000dcaf1263b2fb7a56fe148996"},
{file = "lxml-4.6.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c580c2a61d8297a6e47f4d01f066517dbb019be98032880d19ece7f337a9401d"},
{file = "lxml-4.6.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a21b78af7e2e13bec6bea12fc33bc05730197674f3e5402ce214d07026ccfebd"},
{file = "lxml-4.6.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:46515773570a33eae13e451c8fcf440222ef24bd3b26f40774dd0bd8b6db15b2"},
{file = "lxml-4.6.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:124f09614f999551ac65e5b9875981ce4b66ac4b8e2ba9284572f741935df3d9"},
{file = "lxml-4.6.5-cp37-cp37m-win32.whl", hash = "sha256:b4015baed99d046c760f09a4c59d234d8f398a454380c3cf0b859aba97136090"},
{file = "lxml-4.6.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12ae2339d32a2b15010972e1e2467345b7bf962e155671239fba74c229564b7f"},
{file = "lxml-4.6.5-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:76b6c296e4f7a1a8a128aec42d128646897f9ae9a700ef6839cdc9b3900db9b5"},
{file = "lxml-4.6.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:534032a5ceb34bba1da193b7d386ac575127cc39338379f39a164b10d97ade89"},
{file = "lxml-4.6.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:60aeb14ff9022d2687ef98ce55f6342944c40d00916452bb90899a191802137a"},
{file = "lxml-4.6.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9801bcd52ac9c795a7d81ea67471a42cffe532e46cfb750cd5713befc5c019c0"},
{file = "lxml-4.6.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3b95fb7e6f9c2f53db88f4642231fc2b8907d854e614710996a96f1f32018d5c"},
{file = "lxml-4.6.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:642eb4cabd997c9b949a994f9643cd8ae00cf4ca8c5cd9c273962296fadf1c44"},
{file = "lxml-4.6.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:af4139172ff0263d269abdcc641e944c9de4b5d660894a3ec7e9f9db63b56ac9"},
{file = "lxml-4.6.5-cp38-cp38-win32.whl", hash = "sha256:57cf05466917e08f90e323f025b96f493f92c0344694f5702579ab4b7e2eb10d"},
{file = "lxml-4.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:4f415624cf8b065796649a5e4621773dc5c9ea574a944c76a7f8a6d3d2906b41"},
{file = "lxml-4.6.5-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7679bb6e4d9a3978a46ab19a3560e8d2b7265ef3c88152e7fdc130d649789887"},
{file = "lxml-4.6.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c34234a1bc9e466c104372af74d11a9f98338a3f72fae22b80485171a64e0144"},
{file = "lxml-4.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4b9390bf973e3907d967b75be199cf1978ca8443183cf1e78ad80ad8be9cf242"},
{file = "lxml-4.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fcc849b28f584ed1dbf277291ded5c32bb3476a37032df4a1d523b55faa5f944"},
{file = "lxml-4.6.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46f21f2600d001af10e847df9eb3b832e8a439f696c04891bcb8a8cedd859af9"},
{file = "lxml-4.6.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:99cf827f5a783038eb313beee6533dddb8bdb086d7269c5c144c1c952d142ace"},
{file = "lxml-4.6.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:925174cafb0f1179a7fd38da90302555d7445e34c9ece68019e53c946be7f542"},
{file = "lxml-4.6.5-cp39-cp39-win32.whl", hash = "sha256:12d8d6fe3ddef629ac1349fa89a638b296a34b6529573f5055d1cb4e5245f73b"},
{file = "lxml-4.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:a52e8f317336a44836475e9c802f51c2dc38d612eaa76532cb1d17690338b63b"},
{file = "lxml-4.6.5-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:11ae552a78612620afd15625be9f1b82e3cc2e634f90d6b11709b10a100cba59"},
{file = "lxml-4.6.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:473701599665d874919d05bb33b56180447b3a9da8d52d6d9799f381ce23f95c"},
{file = "lxml-4.6.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7f00cc64b49d2ef19ddae898a3def9dd8fda9c3d27c8a174c2889ee757918e71"},
{file = "lxml-4.6.5-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:73e8614258404b2689a26cb5d002512b8bc4dfa18aca86382f68f959aee9b0c8"},
{file = "lxml-4.6.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ff44de36772b05c2eb74f2b4b6d1ae29b8f41ed5506310ce1258d44826ee38c1"},
{file = "lxml-4.6.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5d5254c815c186744c8f922e2ce861a2bdeabc06520b4b30b2f7d9767791ce6e"},
{file = "lxml-4.6.5.tar.gz", hash = "sha256:6e84edecc3a82f90d44ddee2ee2a2630d4994b8471816e226d2b771cda7ac4ca"},
]
markupsafe = [
{file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
@ -2312,8 +2324,11 @@ packaging = [
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
]
pandas = [
{file = "pandas-1.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9707bdc1ea9639c886b4d3be6e2a45812c1ac0c2080f94c31b71c9fa35556f9b"},
{file = "pandas-1.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2f44425594ae85e119459bb5abb0748d76ef01d9c08583a667e3339e134218e"},
{file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:372d72a3d8a5f2dbaf566a5fa5fa7f230842ac80f29a931fb4b071502cf86b9a"},
{file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99d2350adb7b6c3f7f8f0e5dfb7d34ff8dd4bc0a53e62c445b7e43e163fce63"},
{file = "pandas-1.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:4acc28364863127bca1029fb72228e6f473bb50c32e77155e80b410e2068eeac"},
{file = "pandas-1.3.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c2646458e1dce44df9f71a01dc65f7e8fa4307f29e5c0f2f92c97f47a5bf22f5"},
{file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5298a733e5bfbb761181fd4672c36d0c627320eb999c59c65156c6a90c7e1b4f"},
{file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22808afb8f96e2269dcc5b846decacb2f526dd0b47baebc63d913bf847317c8f"},

View file

@ -0,0 +1,2 @@
[pytest]
norecursedirs = .git data

View file

@ -1,153 +0,0 @@
# Overview
This document describes our "data roadmap", which serves several purposes.
# Data roadmap goals
The goals of the data roadmap are as follows:
- Tracking data sets being considered for inclusion in the Climate and Economic Justice Screening Tool (CEJST), either as a data set that is included in the cumulative impacts score or a reference data set that is not included in the score
- Prioritizing data sets, so that it's obvious to developers working on the CEJST which data sets to incorporate next into the tool
- Gathering important details about each data set, such as its geographic resolution and the year it was last updated, so that the CEJST team can make informed decisions about what data to prioritize
- Tracking the problem areas that each data set relates to (e.g., a certain data set may relate to the problem of pesticide exposure amongst migrant farm workers)
- Enabling members of the public to submit ideas for problem areas or data sets to be considered for inclusion in the CEJST, with easy-to-use and accessible tools
- Enabling members of the public to submit revisions to the information about each problem area or data set, with easy-to-use and accessible tools
- Enabling the CEJST development team to review suggestions before incorporating them officially into the data roadmap, to filter out potential noise and spam, or consider how requests may lead to changes in software features and documentation
# User stories
These goals can map onto several user stories for the data roadmap, such as:
- As a community member, I want to suggest a new idea for a dataset.
- As a community member, I want to understand what happened with my suggestion for a new dataset.
- As a community member, I want to edit the details of a dataset proposal to add more information.
- As a WHEJAC board member, I want to vote on what data sources should be prioritized next.
- As a product manager, I want to filter based on characteristics of the data.
- As a developer, I want to know what to work on next.
# Data set descriptions
There are lots of details that are important to track for each data set. This
information helps us prepare to integrate a data set into the tool and prioritize
between different options for data in the data roadmap.
In order to support a process of peer review on edits and updates, these details are
tracked in one `YAML` file per data set description in the directory
[data_roadmap/data_set_descriptions](data_roadmap/data_set_descriptions).
Each data set description includes a number of fields, some of which are required.
The schema defining these fields is written in [Yamale](https://github.com/23andMe/Yamale)
and lives at [data_roadmap/data_set_description_schema.yaml](data_roadmap/data_set_description_schema.yaml).
Because `Yamale` does not provide a method for describing fields, we've created an
additional file that includes written descriptions of the meaning of each field in
the schema. These live in [data_roadmap/data_set_description_field_descriptions.yaml](data_roadmap/data_set_description_field_descriptions.yaml).
In order to provide a helpful starting point for people who are ready to contribute
ideas for a new data set for consideration, there is an auto-generated data set
description template that lives at [data_roadmap/data_set_description_template.yaml](data_roadmap/data_set_description_template.yaml).
# Steps to add a new data set description: the "easy" way
Soon we will create a Google Form that contributors can use to submit ideas for new
data sets. The Google Form will match the schema of the data set descriptions. Please
see [this ticket](https://app.zenhub.com/workspaces/justice40-60993f6e05473d0010ec44e3/issues/usds/justice40-tool/39)
for tracking this work.
# Steps to add a new data set description: the git-savvy way
For those who are comfortable using `git` and `Markdown`, these are the steps to
contribute a new data set description to the data roadmap:
1. Research and learn about the data set you're proposing for consideration.
2. Clone the repository and learn about the [contribution guidelines for this
project](../docs/CONTRIBUTING.md).
3. In your local version of the repository, copy the template from
`data_roadmap/data_set_description_template.yaml` into a new file that lives in
`data_roadmap/data_set_descriptions` and has the name of the data set as the name of the file.
4. Edit this file to ensure it has all of the appropriate details about the data set.
5. If you'd like, you can run the validations in `run_validations_and_write_template`
to ensure your contribution is valid according to the schema. These checks will also
run automatically on each commit.
6. Create a pull request with your new data set description and submit it for peer
review.
Thank you for contributing!
# Tooling proposal and milestones
There is no single tool that supports all the goals and user stories described above.
Therefore we've proposed combining a number of tools in a way that can support them all.
We've also proposed various "milestones" that will allow us to iteratively and
sequentially build the data roadmap in a way that supports the entire vision but
starts with small and achievable steps. These milestones are proposed in order.
This work is most accurately tracked in [this epic](https://app.zenhub.com/workspaces/justice40-60993f6e05473d0010ec44e3/issues/usds/justice40-tool/38).
We've also verbally described them below.
## Milestone: YAML files for data sets and linter (Done)
To start, we'll create a folder in this repository that can
house YAML files, one per data set. Each file will describe the characteristics of the data.
The benefit of using a YAML file for this is that it's easy to subject changes to these files to peer review through the pull request process. This allows external collaborators from the open source community to submit suggested changes, which can be reviewed by the core CEJST team.
We'll use a Python-based script to load all the files in the directory, and then run a schema validator to ensure all the files have valid entries.
For schema validation, we propose using [Yamale](https://github.com/23andMe/Yamale). This provides a lightweight schema and validator, and [integrates nicely with GitHub actions](https://github.com/nrkno/yaml-schema-validator-github-action).
If there's an improper format in any of the files, the schema validator will throw an error.
As part of this milestone, we will also set this up to run automatically with each commit to any branch as part of CI/CD.
## Milestone: Google forms integration
To make it easy for non-engineer members of the public and advisory bodies such as the WHEJAC to submit suggestions for data sets, we will configure a Google Form that maps to the schema of the data set files.
This will enable members of the public to fill out a simple form suggesting data without needing to understand Github or other engineering concepts.
At first, these responses can just go into a resulting Google Sheet and be manually copied and converted into data set description files. Later, we can write a script that converts new entries in the Google Sheet automatically into data set files. This can be setup to run as a trigger on the addition of new rows to the Google Sheet.
## Milestone: Post data in tabular format
Add a script that runs the schema validator on all files and, if successful, posts the results in a tabular format. There are straightforward packages to post a Python dictionary / `pandas` dataframe to Google Sheets and/or Airtable. As part of this milestone, we will also set this up to run automatically with each commit to `main` as part of CI/CD.
This will make it easier to filter the data to answer questions like, "which data sources are available at the census block group level".
## Milestone: Tickets created for incorporating data sets
For each data set that is being considered for inclusion soon in the tool, the project management team will create a ticket for "Incorporating \_\_\_ data set into the database", with a link to the data set detail document. This ticket will be created in the ticket tracking system used by the open source repository, which is ZenHub. This project management system will be public.
At the initial launch, we are not planning for members of the open source community to be able to create tickets, but we would like to consider a process for members of the open source community creating tickets that can go through review by the CEJST team.
This will help developers know what to work on next, and open source community members can also pick up tickets and work to integrate the data sets.
## Milestone: Add problem areas
We'll need to somehow track "problem areas" that describe problems in climate, environmental, and economic justice, even without specific proposals of data sets. For instance, a problem area may be "food insecurity", and a number of data sets can have this as their problem area.
We can change the linter to validate that every data set description maps to one or more known problem areas.
The benefit of this is that some non-data-focused members of the public or the WHEJAC advisory body may want to suggest we prioritize certain problem areas, with or without ideas for specific data sets that may best address that problem area.
It is not clear at this time the best path forward for implementing these problem area descriptions. One option is to create a folder for descriptions of problem areas, which contains YAML files that get validated according to a schema. Another option would be simply to add these as an array in the description of data sets, or add labels to the tickets once data sets are tracked in GitHub tickets.
## Milestone: Add prioritzation voting for WHEJAC and members of the public
This milestone is currently the least well-defined. It's important that members of advisory bodies like the WHEJAC and members of the public be able to "upvote" certain data sets for inclusion in the tool.
One potential for this is to use the [Stanford Participatory Budgeting Platform](https://pbstanford.org/). Here's an [example of voting on proposals within a limited budget](https://pbstanford.org/nyc8/knapsack).
For instance, going into a quarterly planning cycle, the CEJST development team could estimate the amount of time (in developer-weeks) that it would take to clean, analyze, and incorporate each potential data set. For instance, incorporating some already-cleaned census data may take 1 week of a developer's time, while incorporating new asthma data from CMS that's never been publicly released could take 5 weeks. Given a "budget" of the number of developer weeks available (e.g., 2 developers for 13 weeks, or 26 developer-weeks), advisors can vote on their top priorities for inclusion in the tool within the available "budget".

View file

@ -1,39 +0,0 @@
# There is no method for adding field descriptions to `yamale` schemas.
# Therefore, we've created a dictionary here of fields and their descriptions.
name: A short name of the data set.
source: The URL pointing towards the data set itself or more information about the
data set.
relevance_to_environmental_justice: It's useful to spell out why this data is
relevant to EJ issues and/or can be used to identify EJ communities.
spatial_resolution: Dev team needs to know if the resolution is granular enough to be useful
public_status: Whether a dataset has already gone through public release process
(like Census data) or may need a lengthy review process (like Medicaid data).
sponsor: Whether there's a federal agency or non-governmental agency that is working
to provide and maintain this data.
subjective_rating_of_data_quality: Sometimes we don't have statistics on data
quality, but we know it is likely to be accurate or not. How much has it been
vetted by an agency; is this the de facto data set for the topic?
estimated_margin_of_error: Estimated margin of error on measurement, if known. Often
more narrow geographic measures have a higher margin of error due to a smaller sample
for each measurement.
known_data_quality_issues: It can be helpful to write out known problems.
geographic_coverage_percent: We want to think about data that is comprehensive across
America.
geographic_coverage_description: A verbal description of geographic coverage.
data_formats: Developers need to know what formats the data is available in
last_updated_date: When was the data last updated / refreshed? (In format YYYY-MM-DD.
If exact date is not known, use YYYY-01-01.)
frequency_of_updates: How often is this data updated? Is it updated on a reliable
cadence?
documentation: Link to docs. Also, is the documentation good enough? Can we get the
info we need?
data_can_go_in_cloud: Some datasets can not legally go in the cloud
discussion: Review of other topics, such as
peer review (Overview or links out to peer review done on this dataset),
where and how data is available (e.g., Geoplatform.gov? Is it available from multiple
sources?),
risk assessment of the data (e.g. a vendor-processed version of the dataset might not
be open or good enough),
legal considerations (Legal disclaimers, assumption of risk, proprietary?),
accreditation (Is this source accredited?)

View file

@ -1,24 +0,0 @@
# `yamale` schema for descriptions of data sets.
name: str(required=True)
source: str(required=True)
relevance_to_environmental_justice: str(required=False)
data_formats: enum('GeoJSON', 'Esri Shapefile (SHP, DBF, SHX)', 'GML', 'KML/KMZ',
'GPX', 'CSV/XLSX', 'GDB', 'MBTILES', 'LAS', required=True)
spatial_resolution: enum('State/territory', 'County', 'Zip code', 'Census tract',
'Census block group', 'Exact address or lat/long', 'Other', required=True)
public_status: enum('Not Released', 'Public', 'Public for certain audiences', 'Other',
required=True)
sponsor: str(required=True)
subjective_rating_of_data_quality: enum('Low Quality', 'Medium Quality', 'High
Quality', required=False)
estimated_margin_of_error: num(required=False)
known_data_quality_issues: str(required=False)
geographic_coverage_percent: num(required=False)
geographic_coverage_description: str(required=False)
last_updated_date: day(min='2001-01-01', max='2100-01-01', required=True)
frequency_of_updates: enum('Less than annually', 'Approximately annually',
'Once very 1-6 months',
'Daily or more frequently than daily', 'Unknown', required=True)
documentation: str(required=False)
data_can_go_in_cloud: bool(required=False)
discussion: str(required=False)

Some files were not shown because too many files have changed in this diff Show more