Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE][GWELLS-2094] Address autofill/autocomplete added to Well Owner and Well Location forms #2111

Merged
merged 45 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2e96626
wip- added address autofill to well owner submit page. Autofill does …
acatchpole Jan 15, 2024
36e9416
auto select province
sanjaytkbabu Jan 15, 2024
f432f38
mailing address suggestions now hide on out of focus and onclick
sanjaytkbabu Jan 16, 2024
6dca672
wip changed how address list gets hidden to just hide/show based on a…
acatchpole Jan 16, 2024
3ff24ff
street address and city are now properly trimmed and set when autofil…
acatchpole Jan 16, 2024
302f516
updated style for the address suggestion list
acatchpole Jan 16, 2024
88d5722
address suggestion added for well location
sanjaytkbabu Jan 16, 2024
2a0d721
added edge case handling for different address configurations for wel…
acatchpole Jan 16, 2024
d63510e
in location.vue, dealt with error being generated by non-existent DOM…
acatchpole Jan 16, 2024
fc9c08f
geocoder api added to constants, cleared street address when only pro…
sanjaytkbabu Jan 16, 2024
9fb8a6d
added doc comments to new methods in owner.vue
acatchpole Jan 16, 2024
bf819e6
added doc comments to new methods in location.vue
acatchpole Jan 16, 2024
b66b9cc
in following proper practices, added proper tags to doc comments
acatchpole Jan 17, 2024
6bd63af
altered the suggestion drop down to float over other elements rather …
acatchpole Jan 17, 2024
82e9eab
changed address fetch to have a minimum query length of 3
acatchpole Jan 17, 2024
397ec58
combined the showList and hideList functions to make code more compac…
acatchpole Jan 17, 2024
4c319b9
numerous small style fixes based on dev review
acatchpole Jan 17, 2024
a004a0f
changed elements in add suggestion list from li to div to settle sona…
acatchpole Jan 17, 2024
f5900c9
a fix for an issue the previous commit created where an element was b…
acatchpole Jan 17, 2024
da0dacc
fixed an accidentally reverted change back to proper state
acatchpole Jan 17, 2024
15eca22
WIP moving api call to backend
sanjaytkbabu Jan 18, 2024
80e827b
wip the call to AdddressGeocoder works again
acatchpole Jan 19, 2024
b3a0d1d
wip
sanjaytkbabu Jan 19, 2024
399464c
wi
sanjaytkbabu Jan 19, 2024
63a1d50
fixed addressinput holding on to old value
sanjaytkbabu Jan 19, 2024
1efa3f1
code cleanup
sanjaytkbabu Jan 19, 2024
f9da4de
code cleanup
sanjaytkbabu Jan 19, 2024
ea3f90c
removed test print statements from AddressGeocoder
acatchpole Jan 19, 2024
4cafbd8
api called moved to backend
sanjaytkbabu Jan 19, 2024
f7c8f51
Merge remote-tracking branch 'refs/remotes/origin/feat/gwells-2094-2'…
sanjaytkbabu Jan 19, 2024
ccf48be
cleanup
sanjaytkbabu Jan 19, 2024
7612274
Merge pull request #2115 from bcgov/feat/gwells-2094-2
sanjaytkbabu Jan 19, 2024
f095638
clean up
sanjaytkbabu Jan 19, 2024
8addec1
Update app/frontend/src/submissions/components/SubmissionForm/Owner.vue
sanjaytkbabu Jan 22, 2024
4ff4c2c
moved api url to env
sanjaytkbabu Jan 22, 2024
e79f4cc
GEOCODER_ADDRESS_API_BASE added to env dc config
sanjaytkbabu Jan 22, 2024
350a480
.envrc cleaned up
sanjaytkbabu Jan 22, 2024
0e0cfcd
geocoder api added to backend ocp4 env
sanjaytkbabu Jan 22, 2024
6d22c4d
removed redundant else
sanjaytkbabu Jan 22, 2024
04bccbc
fixed geocoder api url to address bug where first search param was be…
acatchpole Jan 23, 2024
bc5a101
fixed a bug in owner.vue that was adding whitespace to auto-filled ci…
acatchpole Jan 23, 2024
e28573d
fixed a bug in location.vue that was adding whitespace to auto-filled…
acatchpole Jan 23, 2024
9f784b6
removed log test statement from owner.vue
acatchpole Jan 23, 2024
a8f891d
altered the conditional in selectAddressSuggestion to better reflect …
acatchpole Jan 23, 2024
8abc6a3
cleaned up the formatting in selectAddressSuggestion
acatchpole Jan 23, 2024
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: 5 additions & 1 deletion app/backend/wells/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,9 @@

# Well Licensing status endpoint from e-Licensing.
url(api_path_prefix() + r'/wells/licensing$',
views.well_licensing, name='well-licensing')
views.well_licensing, name='well-licensing'),

# get geocoder address
url(api_path_prefix() + r'/wells/geocoder$',
views.AddressGeocoder.as_view(), name='address-geocoder'),
]
12 changes: 12 additions & 0 deletions app/backend/wells/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,3 +893,15 @@ def get_queryset(self):
qs = qs.filter(well_tag_number__in=wells)

return qs
class AddressGeocoder(APIView):
def get(self, request,**kwargs):
GEOCODER_ADDRESS_URL = get_env_variable('GEOCODER_ADDRESS_API_BASE') + self.request.query_params.get('searchTag')
response = requests.get(GEOCODER_ADDRESS_URL)
# Check if the request was successful (status code 200)
if response.status_code == 200:
data = response.json()
# Create a Django JsonResponse object and return it
return JsonResponse(data)
else:
# If the request was not successful, return an appropriate HTTP response
return JsonResponse({'error': f"Error: {response.status_code} - {response.text}"}, status=500)
3 changes: 3 additions & 0 deletions app/frontend/src/common/services/ApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ const ApiService = {
incrementFileCount(resource, documentType){
return axios.get(`${resource}/sum`, {params: { inc: true, documentType}})
},
getAddresses(searchTag){
return axios.get(`wells/geocoder`, {params: { searchTag: searchTag }})
},
}

export default ApiService
110 changes: 107 additions & 3 deletions app/frontend/src/submissions/components/SubmissionForm/Location.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,23 @@ Licensed under the Apache License, Version 2.0 (the "License");
id="wellStreetAddress"
type="text"
label="Street address"
@input="fetchAddressSuggestions"
v-on:focus="showList(true)"
v-on:blur="showList(false)"
:errors="errors['street_address']"
:loaded="fieldsLoaded['street_address']"
:disabled="sameAsOwnerAddress"
></form-input>
<!-- Display the address suggestions -->
<div v-if="addressSuggestions.length > 0" class="address-suggestions list-group list-group-flush border" id="location-address-suggestions-list">
<div v-for="(suggestion, index) in addressSuggestions" :key="index">
<button @mousedown="selectAddressSuggestion(suggestion)" class="list-group-item list-group-item-action border-0">{{ suggestion }}</button>
</div>
</div>
<!-- Display a loading indicator while fetching suggestions -->
<div v-if="isLoadingSuggestions" class="loading-indicator">
Loading...
</div>
</b-col>
</b-row>
<b-row>
Expand Down Expand Up @@ -202,10 +215,9 @@ Licensed under the Apache License, Version 2.0 (the "License");
</template>
<script>
import { mapGetters } from 'vuex'

import inputBindingsMixin from '@/common/inputBindingsMixin.js'

import BackToTopLink from '@/common/components/BackToTopLink.vue'
import ApiService from '../../../common/services/ApiService'

export default {
name: 'Location',
Expand Down Expand Up @@ -255,7 +267,10 @@ export default {
data () {
return {
wellAddressHints: [],
sameAsOwnerAddress: false
sameAsOwnerAddress: false,
addressSuggestions: [],
isLoadingSuggestions: false,
streetAddressInput: ''
}
},
computed: {
Expand Down Expand Up @@ -285,6 +300,90 @@ export default {
this.streetAddressInput = String(this.ownerMailingAddress)
this.cityInput = String(this.ownerCity)
}
},
methods: {
/**
* @desc Asynchronously fetches address suggestions based on the owner's address input.
* If no input is provided, it clears the current suggestions.
* On success, it maps the received data to full addresses and updates the addressSuggestions state.
* On failure, it logs the error and clears the current suggestions.
* Finally, sets the loading state to false.
*/
async fetchAddressSuggestions() {
acatchpole marked this conversation as resolved.
Show resolved Hide resolved
const MIN_QUERY_LENGTH = 3;
if (!this.streetAddressInput || this.streetAddressInput.length < MIN_QUERY_LENGTH) {
this.addressSuggestions = [];
return;
}
this.isLoadingSuggestions = true;
const params = {
minScore: 50,
maxResults: 5,
echo: 'false',
brief: true,
autoComplete: true,
addressString: this.streetAddressInput
};

const querystring = require('querystring');
const searchParams = querystring.stringify(params);
try {
ApiService.getAddresses(searchParams).then((response) => {
if (response.data) {
const data = response.data;
if (data && data.features) {
this.addressSuggestions = data.features.map(item => item.properties.fullAddress);
} else {
this.addressSuggestions = [];
}
}
})
} catch (error) {
console.error(error);
this.addressSuggestions = [];
} finally {
this.isLoadingSuggestions = false;
}
},

/**
* @desc Processes the selected address suggestion.
* Splits the suggestion into components and updates the owner's province, city, and address inputs accordingly.
* Clears the address suggestions afterward.
* @param {string} suggestion - The selected address suggestion. ("1234 Street Rd, Name of City, BC")
*/
selectAddressSuggestion(suggestion) {
const wellAddressArray = suggestion.split(',');
switch (wellAddressArray.length) {
case 3: {
this.streetAddressInput = wellAddressArray[0];
this.cityInput = wellAddressArray[1];
break;
}
case 2: {
this.cityInput = wellAddressArray[0];
this.streetAddressInput = '';
break;
}
}
},

/**
* @desc Clears the current list of address suggestions.
*/
clearAddressSuggestions () {
this.addressSuggestions = [];
},

/**
* @desc Shows or hides the address suggestions list in the UI.
* @param {boolean} show - a boolean which indicates whether to show or hide the element
*/
showList(show) {
if(document.getElementById('location-address-suggestions-list')){
document.getElementById('location-address-suggestions-list').style.display = show? 'block' : 'none';
}
}
}
}
</script>
Expand All @@ -293,4 +392,9 @@ export default {
.dropdown-error-border {
border-radius: 5px;
}
.address-suggestions {
list-style-type: none;
position: absolute;
z-index: 10;
}
</style>
137 changes: 130 additions & 7 deletions app/frontend/src/submissions/components/SubmissionForm/Owner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,35 @@ Licensed under the Apache License, Version 2.0 (the "License");

<b-row>
<b-col cols="12" md="6">
<form-input id="ownerFullName" label="Well Owner Name" v-model="ownerFullNameInput" :errors="errors['owner_full_name']" :loaded="fieldsLoaded['owner_full_name']"></form-input>
<form-input
id="ownerFullName"
label="Well Owner Name"
v-model="ownerFullNameInput"
:errors="errors['owner_full_name']"
:loaded="fieldsLoaded['owner_full_name']"
></form-input>
</b-col>
<b-col cols="12" md="6">
<form-input id="ownerMailingAddress" label="Owner Mailing Address" v-model="ownerAddressInput" :errors="errors['owner_mailing_address']" :loaded="fieldsLoaded['owner_mailing_address']"></form-input>
<form-input
id="ownerMailingAddress"
label="Owner Mailing Address"
v-model="ownerAddressInput"
@input="fetchAddressSuggestions"
v-on:focus="showList(true)"
v-on:blur="showList(false)"
:errors="errors['owner_mailing_address']"
:loaded="fieldsLoaded['owner_mailing_address']">
</form-input>
<!-- Display the address suggestions -->
<div v-if="addressSuggestions.length > 0" class="address-suggestions list-group list-group-flush border" id="owner-address-suggestions-list">
<div v-for="(suggestion, index) in addressSuggestions" :key="index">
<button @mousedown="selectAddressSuggestion(suggestion)" class="list-group-item list-group-item-action border-0">{{ suggestion }}</button>
</div>
</div>
<!-- Display a loading indicator while fetching suggestions -->
<div v-if="isLoadingSuggestions" class="loading-indicator">
Loading...
</div>
</b-col>
</b-row>
<b-row>
Expand Down Expand Up @@ -85,11 +110,10 @@ Licensed under the Apache License, Version 2.0 (the "License");

<script>
import { mapGetters } from 'vuex'

import inputBindingsMixin from '@/common/inputBindingsMixin.js'
import inputFormatMixin from '@/common/inputFormatMixin.js'

import BackToTopLink from '@/common/components/BackToTopLink.vue'
import ApiService from '../../../common/services/ApiService'

export default {
mixins: [inputBindingsMixin, inputFormatMixin],
Expand Down Expand Up @@ -127,22 +151,121 @@ export default {
},
fields: {
ownerFullNameInput: 'ownerFullName',
ownerAddressInput: 'ownerMailingAddress',
ownerCityInput: 'ownerCity',
ownerProvinceInput: 'ownerProvinceState',
ownerPostalCodeInput: 'ownerPostalCode',
ownerEmailInput: 'ownerEmail',
ownerTelInput: 'ownerTel'
},
data () {
return {}
return {
addressSuggestions: [],
isLoadingSuggestions: false,
ownerAddressInput: ''
}
},
computed: {
...mapGetters(['codes'])
},
methods: {
/**
* @desc Asynchronously fetches address suggestions based on the owner's address input.
* If no input is provided, it clears the current suggestions.
* On success, it maps the received data to full addresses and updates the addressSuggestions state.
* On failure, it logs the error and clears the current suggestions.
* Finally, sets the loading state to false.
*/
async fetchAddressSuggestions() {
const MIN_QUERY_LENGTH = 3;
if (!this.ownerAddressInput || this.ownerAddressInput.length < MIN_QUERY_LENGTH) {
this.addressSuggestions = [];
return;
} else {
sanjaytkbabu marked this conversation as resolved.
Show resolved Hide resolved
this.isLoadingSuggestions = true;
const params = {
minScore: 50, //accuracy score of results compared to input
maxResults: 5,
echo: 'false',
brief: true,
autoComplete: true,
addressString: this.ownerAddressInput
};

const querystring = require('querystring');
const searchParams = querystring.stringify(params);
try {
ApiService.getAddresses(searchParams).then((response) => {
if (response.data) {
const data = response.data;
if (data && data.features) {
this.addressSuggestions = data.features.map(item => item.properties.fullAddress);
} else {
this.addressSuggestions = [];
}
}
})
} catch (error) {
console.error(error);
this.addressSuggestions = [];
} finally {
this.isLoadingSuggestions = false;
}
}
},

/**
* @desc Processes the selected address suggestion.
* Splits the suggestion into components and updates the owner's province, city, and address inputs accordingly.
* Clears the address suggestions afterward.
* @param {string} suggestion - The selected address suggestion. ("1234 Street Rd, Name of City, BC")
*/
selectAddressSuggestion(suggestion) {
console.log(suggestion);

const ownerAddressArray = suggestion.split(',');
if(ownerAddressArray){

const PROV_ARRAY_INDEX = ownerAddressArray.length -1;
const CITY_ARRAY_INDEX = ownerAddressArray.length -2;
const STREET_ARRAY_INDEX = ownerAddressArray.length -3;
let province = ownerAddressArray[PROV_ARRAY_INDEX].toUpperCase().trim();
if(province === 'BC' || province === 'BRITISH COLUMBIA'){
this.ownerProvinceInput = this.codes.province_codes[0].province_state_code;
this.ownerAddressInput = '';
}
else {
this.ownerProvinceInput = "";
}
this.ownerCityInput = ownerAddressArray[CITY_ARRAY_INDEX];
if(ownerAddressArray[STREET_ARRAY_INDEX]) this.ownerAddressInput = ownerAddressArray[STREET_ARRAY_INDEX];
}
this.clearAddressSuggestions();
},

/**
* @desc Clears the current list of address suggestions.
*/
clearAddressSuggestions () {
this.addressSuggestions = [];
},

/**
* @desc Shows or hides the address suggestions list in the UI.
* @param {boolean} show - a boolean which indicates whether to show or hide the element
*/
showList(show) {
if(document.getElementById('owner-address-suggestions-list')){
document.getElementById('owner-address-suggestions-list').style.display = show? 'block' : 'none';
}
}
}
}
</script>

<style>

.address-suggestions {
list-style-type: none;
position: absolute;
z-index: 10;
}
</style>
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ services:
S3_WELL_EXPORT_BUCKET: gwells
S3_USE_SECURE: 0
EMAIL_NOTIFICATION_RECIPIENT: [email protected]
GEOCODER_ADDRESS_API_BASE: https://geocoder.api.gov.bc.ca/addresses.json?q=
command: /bin/bash -c "
sleep 3 &&
set -x &&
Expand Down
9 changes: 9 additions & 0 deletions openshift/backend.dc.json
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,15 @@
{
"name": "ENFORCE_ENV_VARIABLES",
"value": "False"
},
{
"name": "GEOCODER_ADDRESS_API_BASE",
"valueFrom": {
"configMapKeyRef": {
"key": "GEOCODER_ADDRESS_API_BASE",
"name": "gwells-global-config${NAME_SUFFIX}"
}
}
}
],
"resources": {
Expand Down
Loading
Loading