Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ docker compose up

The app will log to the console, and you should be able to visit it at http://localhost:8000

**William's notes on docker installation**
- From the Docker Desktop GUI, I turned on "Use containerd for pulling and storing images" in settings
- in "docker-compose.yml", I also added "platform: linux/amd64" to the "app" and "postgres" services, because I kept receiving the following error: "Error response from daemon: no matching manifest for linux/arm64/v8 in the manifest list entries: no match for platform in manifest: not found"

## Completing the Challenge

Once you have the app up and running on your computer, you'll need to flesh out certain code blocks to make the map functional. You'll be using [Django](https://docs.djangoproject.com/en/6.0/) and [React-Leaflet](https://react-leaflet.js.org/docs/api-components/) to complete this task. By the end of this challenge, you should have:
Expand Down Expand Up @@ -67,6 +71,8 @@ Use this command to run tests:

In `map/static/js/RestaurantPermitMap.js`, create a filter that allows users to send a request for a specific year to the backend. The options shoulds be any year between 2016 and 2026, inclusive. Then, use the fetch api in the map component to make a request and receive that data.

**William note: The filter itself seems to already have been implemented when I forked the repo. **

### Step 4: Display results on the page

In the map component, process the community area and use it to display shapes for all areas on the map. Then, display the total number of restaurant permits that year as well as the maximum number of permits in any one area.
Expand Down
212,049 changes: 212,048 additions & 1 deletion data/raw/community-areas.geojson

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:

app:
image: map
platform: linux/amd64
container_name: map
build:
context: .
Expand All @@ -41,6 +42,7 @@ services:
postgres:
container_name: map-postgres
image: postgis/postgis
platform: linux/amd64
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
Expand Down
15 changes: 13 additions & 2 deletions map/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
class CommunityAreaSerializer(serializers.ModelSerializer):
class Meta:
model = CommunityArea
fields = ["name", "num_permits"]
fields = ["name", "area_id", "num_permits"]

num_permits = serializers.SerializerMethodField()

def get_num_permits(self, obj):
"""
TODO: supplement each community area object with the number
of permits issued in the given year.

e.g. The endpoint /map-data/?year=2017 should return something like:
[
{
Expand All @@ -30,5 +30,16 @@ def get_num_permits(self, obj):
}
]
"""
# Since fields in line 9 started as ["name", "num_permits"], I stayed with that structure and added area_id
# to try and match the provided example. Still, community area names are not keys.

# Get the query year
query_year = self.context.get('year')
# Get the object's area id
ca_id = obj.area_id

# Filter for permits issued in that area and year, then return
restaurants = RestaurantPermit.objects.filter(community_area_id=ca_id, issue_date__year=query_year)
return len(restaurants)

pass
99 changes: 88 additions & 11 deletions map/static/js/RestaurantPermitMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,57 @@ function YearSelect({ setFilterVal }) {
export default function RestaurantPermitMap() {
const communityAreaColors = ["#eff3ff", "#bdd7e7", "#6baed6", "#2171b5"]

const [currentYearData, setCurrentYearData] = useState([])
const [currentYearData, setCurrentYearData] = useState({
"mapData": {},
"maxNumPermits": 0,
"totalPermits": 0,
})
const [year, setYear] = useState(2026)

const yearlyDataEndpoint = `/map-data/?year=${year}`


useEffect(() => {
fetch()
fetch(yearlyDataEndpoint)
.then((res) => res.json())
.then((data) => {
/**
* TODO: Fetch the data needed to supply to map with data
*/


// After fetching data, push number of permits to a list for summing and finding max number
var permitNumberList = [];
for (const ca of data) {
permitNumberList.push(Number(ca["num_permits"]));
}
// console.log(permitNumberList, typeof(permitNumberList[0]));


const maxNumPermits = Math.max(...permitNumberList); // Calculate max permit number for year
const totalPermits = permitNumberList.reduce((sum, i) => sum + i, 0); // Calculate total permit number for year

// Stage each CA's name, permits as percentage of max permits, and number of permits in a dictionary
const mapData = {}
data.forEach(element => {
mapData[element['area_id']] = {
'name': element['name'],
'percentage': element['num_permits'] / maxNumPermits,
'number': element['num_permits'],
};
});


// Set state with these new statistics
const toSet = {
"maxNumPermits": maxNumPermits,
"totalPermits": totalPermits,
"mapData": mapData
};

//console.log(toSet);
setCurrentYearData(toSet)
//console.log(currentYearData);
})
}, [yearlyDataEndpoint])

Expand All @@ -60,6 +99,19 @@ export default function RestaurantPermitMap() {
* TODO: Use this function in setAreaInteraction to set a community
* area's color using the communityAreaColors constant above
*/

console.log("cur percent", percentageOfPermits);
console.log("max percent", currentYearData["maxNumPermits"] / currentYearData["totalPermits"]);

// Recall that percentage = a CA's number of permits divided by the max permits per CA issued that year
// The highest permit count CA will have the darkest color, and we scale other CAs in relation to
// That region (the maximum). Since there are four colors, we multiply by three (divide by a third) and
// round to calculate the index that chooses the region's color.
const index = Math.round((percentageOfPermits * 3));
console.log(index);

return communityAreaColors[index];

}

function setAreaInteraction(feature, layer) {
Expand All @@ -70,22 +122,46 @@ export default function RestaurantPermitMap() {
* 2) On hover, display a popup with the community area's raw
* permit count for the year
*/
layer.setStyle()
layer.on("", () => {
layer.bindPopup("")
layer.openPopup()

console.log(feature.properties["area_num_1"]);

// Retrieve CA's data from state using the area number (also in the "properties" of geojson data)
// Retrieve the color
const ca = currentYearData["mapData"][feature["properties"]["area_num_1"]]
const color = getColor(ca["percentage"]);
console.log(color)

// Set the region's style with the color
layer.setStyle({
weight: 2,
color: '#000000',
fillColor: color,
dashArray: '1',
fillOpacity: 1,

})

// Pop ups will include the name of the CA and the number of permits issued that year
layer.on("mouseover", () => {
const popupDisplay = `
<b>${ca["name"]}</b> <br>
Permits issued: ${ca["number"]}
`;
layer.bindPopup(popupDisplay);
layer.openPopup();
})
}

console.log(currentYearData)

return (
<>
<YearSelect filterVal={year} setFilterVal={setYear} />
<p className="fs-4">
Restaurant permits issued this year: {/* TODO: display this value */}
Restaurant permits issued this year: {currentYearData["totalPermits"]}
</p>
<p className="fs-4">
Maximum number of restaurant permits in a single area:
{/* TODO: display this value */}
Maximum number of restaurant permits in a single area: {currentYearData["maxNumPermits"]}
</p>
<MapContainer
id="restaurant-map"
Expand All @@ -96,11 +172,12 @@ export default function RestaurantPermitMap() {
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png"
/>
{currentYearData.length > 0 ? (
{Object.keys(currentYearData['mapData']).length > 0 ? (

<GeoJSON
data={RAW_COMMUNITY_AREAS}
onEachFeature={setAreaInteraction}
key={maxNumPermits}
key={JSON.stringify(currentYearData)}
/>
) : null}
</MapContainer>
Expand Down
27 changes: 27 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

@pytest.mark.django_db
def test_map_data_view():


# Create some test community areas
area1 = CommunityArea.objects.create(name="Beverly", area_id="1")
area2 = CommunityArea.objects.create(name="Lincoln Park", area_id="2")
Expand Down Expand Up @@ -39,3 +41,28 @@ def test_map_data_view():
# TODO: Complete the test by asserting that the /map-data/ endpoint
# returns the correct number of permits for Beverly and Lincoln
# Park in 2021

# Check for request success
if response.status_code == 200:
# Check format of the response
print(response.json())

# Deserialize the data
ret_data = response.json()

# Set correct values
expected = {
"Beverly": 2,
"Lincoln Park": 3
}

# Iterate through all elements of response and match with elements of the expected values
for ca in ret_data:
for ca_name in expected.keys():
if ca["name"] == ca_name:
assert ca["num_permits"] == expected[ca_name]