Skip to content

Commit

Permalink
filter municipality api <-> vue
Browse files Browse the repository at this point in the history
  • Loading branch information
stevegerrits committed Mar 22, 2024
1 parent b624d42 commit bd1d1a0
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 83 deletions.
75 changes: 28 additions & 47 deletions frontend/src/components/FilterComponent.vue
Original file line number Diff line number Diff line change
@@ -1,47 +1,27 @@
<template>
<v-row>
<v-col v-if="formattedMunicipalities.length > 0" cols="5" sm="3" md="3" lg="3" class="d-flex align-start">
<v-autocomplete
v-model="selectedMunicipalities"
:items="municipalities.length ? municipalities.map(municipality => ({
title: municipality.name,
value: municipality.id
})) : []"
item-text="title"
item-value="value"
label="Selecteer gemeente(s)"
multiple
chips
dense
solo
<v-autocomplete v-model="selectedMunicipalities" :items="municipalities.length ? municipalities.map(municipality => ({
title: municipality.name,
value: municipality.id
})) : []" item-text="title" item-value="value" label="Selecteer gemeente(s)" multiple chips dense solo
@change="emitFilterUpdate">
</v-autocomplete>
</v-col>
<v-col cols="5" sm="3" md="3" lg="3" class="d-flex align-start">
<v-autocomplete
v-model="selectedYears"
:items="jaartallen"
label="Selecteer jaartal(len)"
multiple
chips
dense
solo
@change="emitFilterUpdate"
></v-autocomplete>
<v-autocomplete v-model="selectedYears" :items="jaartallen" label="Selecteer jaartal(len)" multiple chips dense
solo @change="emitFilterUpdate"></v-autocomplete>
</v-col>
<v-col cols="2" sm="3" md="3" lg="3" class="d-flex align-start">
<v-switch
v-model="anbAreasActief"
:label="`ANB Areas ${anbAreasActief ? 'Aan' : 'Uit'}`"
:color="anbAreasActief ? '#4C7742' : ''"
@change="emitFilterUpdate"
></v-switch>
<v-switch v-model="anbAreasActief" :label="`ANB Areas ${anbAreasActief ? 'Aan' : 'Uit'}`"
:color="anbAreasActief ? '#4C7742' : ''" @change="emitFilterUpdate"></v-switch>
</v-col>
</v-row>
</template>

<script>
import ApiService from '@/services/apiService';
import { mapActions, mapMutations } from 'vuex';
export default {
data: () => ({
selectedMunicipalities: [],
Expand All @@ -51,30 +31,31 @@ export default {
anbAreasActief: false,
}),
computed: {
formattedMunicipalities() {
const formatted_municipalities = this.municipalities.map(municipality => {
console.log('Formatted municipality:', municipality);
return {
name: municipality.name,
id: municipality.id
};
});
console.log('formattedMunicipalities:', formatted_municipalities);
return formatted_municipalities;
formattedMunicipalities() {
const formatted_municipalities = this.municipalities.map(municipality => {
return {
name: municipality.name,
id: municipality.id
};
});
return formatted_municipalities;
},
},
watch: {
selectedMunicipalities(newVal) {
if (newVal.length === 0) {
this.$emit('updateFilters', { municipalities: [], years: this.selectedYears, anbAreasActief: this.anbAreasActief });
}
},
},
methods: {
...mapActions(['updateSelectedMunicipalities']),
...mapMutations(['setSelectedMunicipalities']),
async fetchMunicipalities() {
try {
const response = await ApiService.get('/municipalities/');
if (response.status === 200) {
console.log('Fetched municipalities (raw):', response.data);
this.municipalities = response.data;
const formatted_municipalities = this.municipalities.map(municipality => ({
name: municipality.name,
id: municipality.id
}));
this.formattedMunicipalities = formatted_municipalities;
} else {
console.error('Failed to fetch municipalities: Status Code', response.status);
}
Expand All @@ -84,8 +65,8 @@ export default {
},
emitFilterUpdate() {
this.$emit('updateFilters', {
selectedMunicipalities: this.selectedMunicipalities,
selectedYears: this.selectedYears,
municipalities: this.selectedMunicipalities,
years: this.selectedYears,
anbAreasActief: this.anbAreasActief
});
},
Expand Down
79 changes: 44 additions & 35 deletions frontend/src/components/MapPage.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<template>
<NavbarComponent />
<div id="filters"><FilterComponent :initialFilters="filtersConfig" @updateFilters="updateFilters" /></div>
<div id="filters">
<FilterComponent :initialFilters="filtersConfig" @updateFilters="updateFilters" />
</div>
<div id="main-content">
<div id="mapid"></div>
<div id="details">
Expand All @@ -18,14 +20,14 @@
</p>
<p><strong>Admin Notes:</strong> <textarea v-if="isEditing"
v-model="selectedObservation.admin_notes"></textarea> <span v-else>{{
selectedObservation.admin_notes }}</span></p>
selectedObservation.admin_notes }}</span></p>
<p><strong>Species:</strong> <input v-if="isEditing" v-model="selectedObservation.species"
type="number" /> <span v-else>{{ selectedObservation.species }}</span></p>
<p><strong>Activity:</strong> <input v-if="isEditing" v-model="selectedObservation.activity"
type="text" /> <span v-else>{{ selectedObservation.activity }}</span></p>
<p><strong>Creation Datetime:</strong> <input v-if="isEditing"
v-model="selectedObservation.creation_datetime" type="datetime-local" /> <span v-else>{{
selectedObservation.creation_datetime }}</span></p>
selectedObservation.creation_datetime }}</span></p>
<p><strong>Last Modification Datetime:</strong> <input v-if="isEditing"
v-model="selectedObservation.last_modification_datetime" type="datetime-local" /> <span
v-else>{{ selectedObservation.last_modification_datetime }}</span></p>
Expand Down Expand Up @@ -55,13 +57,12 @@ export default {
components: {
NavbarComponent,
FooterComponent,
FilterComponent
FilterComponent
},
data() {
return {
selectedObservation: null,
isEditing: false,
observations: [],
map: null,
markers: [],
filters: {
Expand All @@ -76,45 +77,49 @@ export default {
};
},
computed: {
...mapState(['isLoggedIn', 'username', 'userId']),
...mapState(['isLoggedIn', 'username', 'userId', 'observations']),
},
watch: {
observations(newVal) {
if (newVal && newVal.length > 0) {
this.updateMarkers();
}
},
},
methods: {
...mapActions(['fetchUserStatus']),
selectObservation(observationData) {
// Select a observation for viewing or editing
this.selectedObservation = observationData;
},
updateFilters(updatedFilters) {
this.filtersConfig = updatedFilters;
updateFilters(filters) {
this.filters = {
...this.filters,
...filters
};
this.applyFilters();
},
async applyFilters() {
let filterQuery = `/observations/?`;
if (this.filters.validated) {
filterQuery += `validated=${this.filters.validated}&`;
}
if (this.filters.minCreationDatetime) {
filterQuery += `min_creation_datetime=${this.filters.minCreationDatetime.toISOString()}&`;
let filterQuery = `?`;
// If there are municipality filters
if (this.filters.municipalities && this.filters.municipalities.length) {
filterQuery += `municipality_id=${this.filters.municipalities.join(',')}&`;
}
if (this.filters.maxCreationDatetime) {
filterQuery += `max_creation_datetime=${this.filters.maxCreationDatetime.toISOString()}&`;
// If there are year filters
if (this.filters.years && this.filters.years.length) {
filterQuery += `year_range=${this.filters.years.join(',')}&`;
}
await this.getObservations(filterQuery);
},
async getObservations(filterQuery = `/observations/`) {
try {
const response = await ApiService.get(filterQuery);
if (response.status !== 200) {
throw new Error(`Network response was not ok, status code: ${response.status}`);
}
this.observations = response.data;
this.updateMarkers();
} catch (error) {
console.error('There has been a problem with your fetch operation:', error);
if (!this.filters.municipalities.length && !this.filters.years.length) {
filterQuery = '';
}
// Proceed to fetch observations with the constructed or default query
this.$store.dispatch('getObservations', filterQuery);
},
initializeMapAndMarkers() {
// Initialize map and place markers for each observation
this.map = L.map('mapid').setView([51.0, 4.5], 9);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap contributors',
Expand All @@ -123,12 +128,14 @@ export default {
this.updateMarkers();
},
updateMarkers() {
// Clear existing markers
this.markers.forEach(marker => this.map.removeLayer(marker));
this.markers = []; // Reset markers array
// Add new markers based on observations data
this.observations.forEach((observation) => {
if (!this.observations.length) {
return;
}
this.observations.forEach((observation, index) => {
const locationRegex = /POINT \(([^ ]+) ([^ ]+)\)/;
const match = observation.location.match(locationRegex);
if (match) {
Expand All @@ -141,8 +148,10 @@ export default {
iconAnchor: [15, 42]
})
}).addTo(this.map)
.on('click', () => this.selectObservation(observation));
.on('click', () => this.selectObservation(observation));
this.markers.push(marker);
} else {
console.log(`Geen geldige locatie gevonden voor observatie #${index}.`);
}
});
},
Expand Down Expand Up @@ -170,12 +179,12 @@ export default {
},
mounted() {
this.fetchUserStatus().then(() => {
this.$store.dispatch('getObservations');
this.initializeMapAndMarkers();
this.getObservations();
});
setInterval(() => {
this.getObservations();
this.$store.dispatch('getObservations');
}, 120000); // Poll every 120 seconds
},
};
Expand Down
29 changes: 28 additions & 1 deletion frontend/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export default createStore({
loading: false,
error: null,
accessToken: null,
selectedMunicipalities: [],
observations: [],
};
},
mutations: {
Expand All @@ -28,6 +30,12 @@ export default createStore({
setAccessToken(state, accessToken) {
state.accessToken = accessToken;
},
setSelectedMunicipalities(state, municipalities) {
state.selectedMunicipalities = municipalities;
},
setObservations(state, observations) { // Optioneel
state.observations = observations;
},
},
actions: {
async fetchUserStatus({ commit }) {
Expand Down Expand Up @@ -69,7 +77,26 @@ export default createStore({
throw error;
}
},

async getObservations({ commit }, filterQuery = '') {
console.log(`Ophalen van observaties met filterQuery: '${filterQuery}'`);
try {
const response = await ApiService.get(`/observations${filterQuery}`); // Verwijder de slash voor filterQuery
if (response.status === 200) {
console.log("Observaties succesvol opgehaald: ", response.data);
commit('setObservations', response.data);
} else {
throw new Error(`Network response was not ok, status code: ${response.status}`);
}
} catch (error) {
console.error('There has been a problem with your fetch operation:', error);
}
},
updateSelectedMunicipalities({ commit, dispatch }, municipalities) {
commit('setSelectedMunicipalities', municipalities);
if (municipalities.length === 0) {
dispatch('getObservations');
}
},
logoutAction({ commit }) {
localStorage.removeItem('access_token');
ApiService.removeHeader();
Expand Down
51 changes: 51 additions & 0 deletions temp/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""factories."""
from vespadb.observations.models import Observation, Municipality, NestHeightEnum, NestSizeEnum, NestLocationEnum, NestTypeEnum, EradicationResultEnum, EradicationProductEnum
import factory
from django.contrib.gis.geos import MultiPolygon, Polygon
from vespadb.observations.models import Municipality
import factory.fuzzy
from django.contrib.gis.geos import Point
from django.utils import timezone
from helpers import get_random_municipality, get_point_from_municipality
from typing import Tuple, Optional

class MunicipalityFactory(factory.django.DjangoModelFactory):
class Meta:
model = Municipality

@factory.lazy_attribute
def municipality_data(self) -> Tuple[Optional[str], Optional[str], Optional[MultiPolygon]]:
# This fetches a single municipality's data and uses it across all fields
name, nis_code, polygon = get_random_municipality()
return (name, nis_code, polygon)

name = factory.LazyAttribute(lambda obj: obj.municipality_data[0])
nis_code = factory.LazyAttribute(lambda obj: obj.municipality_data[1])
polygon = factory.LazyAttribute(lambda obj: obj.municipality_data[2])

class ObservationFactory(factory.django.DjangoModelFactory):
class Meta:
model = Observation

wn_id = factory.Sequence(lambda n: n)
created_datetime = factory.LazyFunction(timezone.now)
modified_datetime = factory.LazyFunction(timezone.now)
location = factory.LazyAttribute(lambda obj: get_point_from_municipality(get_random_municipality()[2], inside=True))
source = factory.Faker('word')
species = factory.fuzzy.FuzzyChoice([x for x in range(100)])
nest_height = factory.fuzzy.FuzzyChoice(NestHeightEnum.values)
nest_size = factory.fuzzy.FuzzyChoice(NestSizeEnum.values)
nest_location = factory.fuzzy.FuzzyChoice(NestLocationEnum.values)
nest_type = factory.fuzzy.FuzzyChoice(NestTypeEnum.values)
observer_phone_number = factory.Faker('phone_number')
observer_email = factory.Faker('email')
observer_name = factory.Faker('name')
observer_allows_contact = factory.Faker('boolean')
observation_datetime = factory.LazyFunction(timezone.now)
eradication_datetime = factory.LazyFunction(timezone.now)
eradicator_name = factory.Faker('name')
eradication_result = factory.fuzzy.FuzzyChoice(EradicationResultEnum.values)
eradication_product = factory.fuzzy.FuzzyChoice(EradicationProductEnum.values)
eradication_notes = factory.Faker('text')
duplicate = factory.Faker('boolean')
images = factory.LazyFunction(lambda: [])
Loading

0 comments on commit bd1d1a0

Please sign in to comment.