Skip to content

Commit

Permalink
feat: updated cql filters, code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Jan 18, 2024
1 parent cf6ac78 commit 150c989
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 126 deletions.
2 changes: 2 additions & 0 deletions app/backend/wells/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ class WellsConfig(AppConfig):

def ready(self):
post_migrate.connect(post_migration_callback, sender=self)
import wells.signals #noqa
import wells.utils #noqa
66 changes: 3 additions & 63 deletions app/backend/wells/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@
"""

import uuid
import math
import reversion
from decimal import Decimal

from django.core.validators import MinValueValidator
from django.utils import timezone
from django.dispatch import receiver
from django.db.models.signals import pre_save

from django.contrib.gis.db import models
from django.contrib.gis.gdal import SpatialReference, CoordTransform


from gwells.models import AuditModel, ProvinceStateCode, ScreenIntakeMethodCode, ScreenMaterialCode,\
ScreenOpeningCode, ScreenBottomCode, ScreenTypeCode, ScreenAssemblyTypeCode, CodeTableModel,\
Expand All @@ -33,8 +31,7 @@
LithologyMaterialCode, BedrockMaterialCode, BedrockMaterialDescriptorCode, LithologyStructureCode,
LithologyMoistureCode, SurficialMaterialCode)
from gwells.db_comments.patch_fields import patch_fields
from wells.utils import calculate_geocode_distance, calculate_pid_distance_for_well, \
calculate_score_address, calculate_score_city, calculate_natural_resource_region_for_well


# from aquifers.models import Aquifer

Expand Down Expand Up @@ -1302,63 +1299,6 @@ def longitude(self):
}


@receiver(pre_save, sender=Well)
def update_utm(sender, instance, **kwargs):
if instance.geom and (-180 < instance.geom.x < 180): # only update utm when geom is valid
utm_zone = math.floor((instance.geom.x + 180) / 6) + 1
coord_transform = CoordTransform(SpatialReference(4326), SpatialReference(32600 + utm_zone))
utm_point = instance.geom.transform(coord_transform, clone=True)

instance.utm_zone_code = utm_zone
# We round to integers because easting/northing is only precise to 1m. The DB column is also an integer type.
instance.utm_easting = round(utm_point.x)
instance.utm_northing = round(utm_point.y)


@receiver(pre_save, sender=Well)
def update_well(sender, instance, **kwargs):
"""
Signal receiver that triggers before a Well instance is saved.
For new Well instances, it calculates and sets various geographical and scoring fields.
For existing Well instances, it recalculates these fields if the geographical location (geom) has changed.
Parameters:
sender (Model Class): The model class that sent the signal. Should always be the Well model.
instance (Well instance): The instance of Well being saved.
kwargs: Additional keyword arguments. Not used in this function.
"""

def is_valid_geom(geom):
"""
Helper function to check if the geom attribute is valid.
A valid geom should be non-null and must have both latitude and longitude.
"""
return geom and hasattr(geom, 'latitude') and hasattr(geom, 'longitude')

try:
if instance._state.adding and not instance.pk:
# Handling new instance creation
if is_valid_geom(instance.geom):
instance.geocode_distance = calculate_geocode_distance(instance)
instance.distance_to_pid = calculate_pid_distance_for_well(instance)
instance.score_address = calculate_score_address(instance)
instance.score_city = calculate_score_city(instance)
instance.natural_resource_region = calculate_natural_resource_region_for_well(instance)
else:
# Handling updates to existing instances
original_instance = sender.objects.get(pk=instance.pk)
if original_instance.geom != instance.geom:
if is_valid_geom(instance.geom):
instance.geocode_distance = calculate_geocode_distance(instance)
instance.distance_to_pid = calculate_pid_distance_for_well(instance)
instance.score_address = calculate_score_address(instance)
instance.score_city = calculate_score_city(instance)
instance.natural_resource_region = calculate_natural_resource_region_for_well(instance)
except Exception as e:
print(f"Error in update_well for Well ID {instance.pk}: {str(e)}")


class CasingMaterialCode(CodeTableModel):
"""
The material used for casing a well, e.g., Cement, Plastic, Steel.
Expand Down
72 changes: 72 additions & 0 deletions app/backend/wells/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import math
from django.dispatch import receiver
from django.db.models.signals import pre_save
from django.contrib.gis.gdal import SpatialReference, CoordTransform
from wells.models import Well
from wells.utils import calculate_geocode_distance, calculate_pid_distance_for_well, \
calculate_score_address, calculate_score_city, calculate_natural_resource_region_for_well, \
reverse_geocode

@receiver(pre_save, sender=Well)
def update_utm(sender, instance, **kwargs):
if instance.geom and (-180 < instance.geom.x < 180): # only update utm when geom is valid
utm_zone = math.floor((instance.geom.x + 180) / 6) + 1
coord_transform = CoordTransform(SpatialReference(4326), SpatialReference(32600 + utm_zone))
utm_point = instance.geom.transform(coord_transform, clone=True)

instance.utm_zone_code = utm_zone
# We round to integers because easting/northing is only precise to 1m. The DB column is also an integer type.
instance.utm_easting = round(utm_point.x)
instance.utm_northing = round(utm_point.y)


@receiver(pre_save, sender=Well)
def update_well(sender, instance, **kwargs):
"""
Signal receiver that triggers before a Well instance is saved.
For new Well instances, it calculates and sets various geographical and scoring fields.
For existing Well instances, it recalculates these fields if the geographical location (geom) has changed.
Parameters:
sender (Model Class): The model class that sent the signal. Should always be the Well model.
instance (Well instance): The instance of Well being saved.
kwargs: Additional keyword arguments. Not used in this function.
"""

def is_valid_geom(geom):
"""
Helper function to check if the geom attribute is valid.
A valid geom should be non-null and must have both latitude and longitude.
"""
return geom and hasattr(geom, 'x') and hasattr(geom, 'y')

try:
if instance._state.adding and not instance.pk:
# Handling new instance creation
if is_valid_geom(instance.geom):
set_well_attributes(instance)
else:
# Handling updates to existing instances
original_instance = sender.objects.get(pk=instance.pk)
if original_instance.geom != instance.geom and is_valid_geom(instance.geom):
set_well_attributes(instance)
except Exception as e:
print(f"Error in update_well for Well ID {instance.pk}: {str(e)}")


def set_well_attributes(instance):
"""
Set attributes for a Well instance based on its geographical location.
Parameters:
instance (Well instance): The instance of Well being processed.
"""
geocoded_address = reverse_geocode(instance.longitude, instance.latitude)
print("geocoded_address")
print(geocoded_address)
instance.geocode_distance = calculate_geocode_distance(geocoded_address)
instance.distance_to_pid = calculate_pid_distance_for_well(instance)
instance.score_address = calculate_score_address(instance, geocoded_address)
instance.score_city = calculate_score_city(instance, geocoded_address)
instance.natural_resource_region = calculate_natural_resource_region_for_well(instance)
44 changes: 35 additions & 9 deletions app/backend/wells/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
from shapely.geometry import Point
from wells.constants import ADDRESS_COLUMNS, GEOCODER_ENDPOINT
from thefuzz import fuzz
from django.db.models import Case, When, Value, DateField, F
from wells.models import Well

WELL_STATUS_CODE_CONSTRUCTION = 'CONSTRUCTION'
WELL_STATUS_CODE_ALTERATION = 'ALTERATION'
WELL_STATUS_CODE_DECOMMISSION = 'DECOMMISSION'

def calculate_pid_distance_for_well(well):
"""
Expand Down Expand Up @@ -59,15 +65,17 @@ def calculate_natural_resource_region_for_well(well):
"service": "WFS",
"version": "2.0.0",
"request": "GetFeature",
"typeName": "WHSE_ADMIN_BOUNDARIES.ADM_NR_DISTRICTS_SPG",
"typeName": "WHSE_ADMIN_BOUNDARIES.ADM_NR_REGIONS_SPG",
"outputFormat": "json",
"srsName": "EPSG:4326",
"CQL_FILTER": f"CONTAINS(geometry, POINT({well.longitude} {well.latitude}))"
"CQL_FILTER": f"CONTAINS(SHAPE, POINT({well.longitude} {well.latitude}))"
}

# Construct the request URL
request_url = f"{base_url}?{'&'.join([f'{k}={v}' for k, v in params.items()])}"

print(request_url)

# Make the request
response = requests.get(request_url)
if response.status_code != 200:
Expand Down Expand Up @@ -132,20 +140,38 @@ def reverse_geocode(
return empty_result


def calculate_geocode_distance(well):
response = reverse_geocode(well.longitude, well.latitude)
return response.get('distance', None) if response else None
def calculate_geocode_distance(geocoded_address):
if not geocoded_address:
return None
return geocoded_address.get('distance', None)


def calculate_score_address(well):
geocoded_address = reverse_geocode(well.longitude, well.latitude)
def calculate_score_address(well, geocoded_address):
if not geocoded_address:
return None
return fuzz.token_set_ratio(well.street_address.lower(), geocoded_address.get('fullAddress', '').lower())


def calculate_score_city(well):
geocoded_address = reverse_geocode(well.longitude, well.latitude)
def calculate_score_city(well, geocoded_address):
if not geocoded_address:
return None
return fuzz.token_set_ratio(well.city.lower(), geocoded_address.get('localityName', '').lower())


def get_annotated_well_queryset():
return Well.objects.select_related('well_status').annotate(
work_start_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_start_date')),
default=Value(None),
output_field=DateField()
),
work_end_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_end_date')),
default=Value(None),
output_field=DateField()
)
)
60 changes: 6 additions & 54 deletions app/backend/wells/views_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from aquifers.permissions import HasAquiferEditRole
from wells.views import WellDetail as WellDetailV1
from wells.constants import MAX_EXPORT_COUNT, MAX_LOCATION_COUNT
from wells.utils import get_annotated_well_queryset

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -601,23 +602,7 @@ def get_queryset(self):
for the currently authenticated user.
"""
queryset = Well.objects.all()

queryset = Well.objects.select_related('well_status').annotate(
work_start_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_start_date')),
default=Value(None),
output_field=DateField()
),
work_end_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_end_date')),
default=Value(None),
output_field=DateField()
)
)
queryset = get_annotated_well_queryset()

return queryset

Expand All @@ -640,23 +625,7 @@ def get_queryset(self):
Retrieves wells that are missing information in any of the specified fields.
"""
queryset = Well.objects.all()

queryset = Well.objects.select_related('well_status').annotate(
work_start_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_start_date')),
default=Value(None),
output_field=DateField()
),
work_end_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_end_date')),
default=Value(None),
output_field=DateField()
)
)
queryset = get_annotated_well_queryset()

# Filtering for records missing any of the specified fields
missing_info_filter = (
Expand All @@ -674,9 +643,8 @@ def get_queryset(self):
Q(person_responsible__isnull=True) |
Q(company_of_person_responsible__isnull=True) |
Q(create_date__isnull=True) |
Q(create_user__isnull=True)
# Q(natural_resource_region__isnull=True) |
# Q(internal_comments__isnull=True)
Q(create_user__isnull=True) |
Q(natural_resource_region__isnull=True)
)

queryset = queryset.filter(missing_info_filter)
Expand Down Expand Up @@ -712,23 +680,7 @@ def get_queryset(self):
in their internal_comments.
"""
queryset = Well.objects.all()

queryset = Well.objects.select_related('well_status').annotate(
work_start_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_start_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_start_date')),
default=Value(None),
output_field=DateField()
),
work_end_date=Case(
When(well_status__well_status_code=WELL_STATUS_CODE_CONSTRUCTION, then=F('construction_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_ALTERATION, then=F('alteration_end_date')),
When(well_status__well_status_code=WELL_STATUS_CODE_DECOMMISSION, then=F('decommission_end_date')),
default=Value(None),
output_field=DateField()
)
)
queryset = get_annotated_well_queryset()

search_terms = ["x-ref'd", "x-ref", "cross-ref", "cross r", "cross-r", "ref'd", "referenced", "refd", "xref", "x-r", "x r"]

Expand Down

0 comments on commit 150c989

Please sign in to comment.