Skip to content

Commit

Permalink
Merge pull request #461 from ropable/master
Browse files Browse the repository at this point in the history
Tweak caching config, refactor locations map
  • Loading branch information
ropable authored Sep 10, 2024
2 parents 0208cb8 + c60fcd2 commit f7a75d3
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 69 deletions.
8 changes: 5 additions & 3 deletions itassets/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
from django.http import HttpResponse, HttpResponseServerError


class HealthCheckMiddleware(object):
class HealthCheckMiddleware:
"""Middleware to provide healthcheck HTTP endpoints for the system.
Should be placed at the top of the MIDDLEWARE list so that requests
to healthcheck endpoints short-circuit and return a response without
passing through further middleware classes.
"""

def __init__(self, get_response):
Expand All @@ -18,8 +21,7 @@ def __call__(self, request):
return self.get_response(request)

def liveness(self, request):
"""Returns that the server is alive and able to serve HTTP responses.
"""
"""Returns that the server is alive and able to serve HTTP responses."""
return HttpResponse("OK")

def readiness(self, request):
Expand Down
9 changes: 9 additions & 0 deletions itassets/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.cache.UpdateCacheMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.cache.FetchFromCacheMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"dbca_utils.middleware.SSOLoginMiddleware",
]
Expand Down Expand Up @@ -110,7 +112,14 @@
"LOCATION": REDIS_CACHE_HOST,
}
}
else:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
}
}
API_RESPONSE_CACHE_SECONDS = env("API_RESPONSE_CACHE_SECONDS", 60)
CACHE_MIDDLEWARE_SECONDS = env("CACHE_MIDDLEWARE_SECONDS", 60)

ADMIN_EMAILS = env("ADMIN_EMAILS", "[email protected]").split(",")
SERVICE_DESK_EMAIL = env("SERVICE_DESK_EMAIL", "[email protected]")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ kind: CronJob
metadata:
name: itassets-cronjob
spec:
# AWST: 10/40 min past the hour, 08:00-19:00, Mon-Fri
schedule: "10,40 0-10 * * 1-5"
# AWST: 10/40 min past the hour, 07:00-19:00, Mon-Fri
schedule: "10,40 0-10,23 * * 1-5"
jobTemplate:
spec:
activeDeadlineSeconds: 600
Expand Down
5 changes: 5 additions & 0 deletions kustomize/overlays/prod/deployment_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,8 @@ spec:
secretKeyRef:
name: itassets-env-prod
key: REDIS_CACHE_HOST
- name: API_RESPONSE_CACHE_SECONDS
valueFrom:
secretKeyRef:
name: itassets-env-prod
key: API_RESPONSE_CACHE_SECONDS
2 changes: 1 addition & 1 deletion kustomize/overlays/prod/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ patches:
- path: postgres_fdw_service_patch.yaml
images:
- name: ghcr.io/dbca-wa/it-assets
newTag: 2.4.28
newTag: 2.4.29
5 changes: 5 additions & 0 deletions kustomize/overlays/uat/deployment_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,8 @@ spec:
secretKeyRef:
name: itassets-env-uat
key: REDIS_CACHE_HOST
- name: API_RESPONSE_CACHE_SECONDS
valueFrom:
secretKeyRef:
name: itassets-env-uat
key: API_RESPONSE_CACHE_SECONDS
1 change: 1 addition & 0 deletions organisation/ascender.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ def ascender_user_import_all():
user.ascender_data = job
user.ascender_data_updated = timezone.localtime()
user.update_from_ascender_data() # This method calls save()
LOGGER.info(f"Updated existing user {user}")
elif not DepartmentUser.objects.filter(employee_id=employee_id).exists():
# Ascender record does not exist in our database; conditionally create a new
# Azure AD account and DepartmentUser instance for them.
Expand Down
74 changes: 49 additions & 25 deletions organisation/static/js/location_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,61 @@ const mapboxStreets = L.tileLayer(

// Define overlay tile layers.
const dbcaRegions = L.tileLayer(
geoserver_wmts_url_overlay + "&layer=cddp:dbca_regions",
geoserver_wmts_url_overlay + "&layer=cddp:kaartdijin-boodja-public_CPT_DBCA_REGIONS",
{
tileSize: 1024,
zoomOffset: -2,
},
);
const dbcaDistricts = L.tileLayer(
geoserver_wmts_url_overlay + "&layer=public:dbca_districts_public",
geoserver_wmts_url_overlay + "&layer=cddp:kaartdijin-boodja-public_CPT_DBCA_DISTRICTS",
{
tileSize: 1024,
zoomOffset: -2,
},
);

// Function to define hover effect for location points.
function locationHover(feature, layer) {
layer.bindTooltip(
feature.properties.name,
{ className: "leaflet-tooltip-wide" }
);
layer.bindPopup(`<a href="?q=${feature.properties.ascender_desc}">${feature.properties.name}</a><br>
${feature.properties.ascender_desc}<br>
${feature.properties.phone}`);
}

// Define a clustered layer for DBCA locations, and a GeoJSON layer to contain the data.
const locationsClustered = L.markerClusterGroup();
const dbcaLocations = L.geoJSON(
null, // Initially empty.
{
onEachFeature: locationHover
},
);

// Function to get location data and populate the layer.
function queryLocationsData() {
$.ajax({
dataType: "json",
url: location_features_url,
data: { format: "geojson" },
success: function (data) {
// Add the device data to the GeoJSON layer.
dbcaLocations.addData(data);
// Add DBCA locations layer to the map display and zoom to their bounds.
locationsClustered.addLayer(dbcaLocations);
map.addLayer(locationsClustered);
map.fitBounds(dbcaLocations.getBounds());
},
});
};
// Immediately run the function once to get data.
queryLocationsData();

// Define map.
const map = L.map('map', {
const map = L.map("map", {
crs: L.CRS.EPSG4326, // WGS 84
center: [-31.96, 115.87],
minZoom: 4,
Expand All @@ -54,26 +93,11 @@ L.control.layers(baseMaps, overlayMaps).addTo(map);
// Define scale bar
L.control.scale({ maxWidth: 500, imperial: false }).addTo(map);

// Function to define hover effect for location points.
function locationHover(feature, layer) {
layer.bindTooltip(
feature.properties.name,
{ className: 'leaflet-tooltip-wide' }
);
layer.bindPopup(`
<a href="?q=${feature.properties.ascender_desc}">${feature.properties.name}</a><br>
${feature.properties.ascender_desc}<br>
${feature.properties.phone}`);
}

// Add locations to the map display and zoom to their bounds.
const locationsLayer = L.geoJson(locationFeatures, {
onEachFeature: locationHover
// Move the map div inside the #locations-tab-pane div and redraw it.
// https://stackoverflow.com/a/63319084/14508
const mapDiv = document.getElementById("map");
const mapResizeObserver = new ResizeObserver(() => {
map.invalidateSize();
});
const locations = L.markerClusterGroup();
locations.addLayer(locationsLayer);
map.addLayer(locations);
map.fitBounds(locations.getBounds());

// Move the map div inside the #locations-tab-pane div.
document.getElementById("locations-tab-pane").appendChild(document.getElementById("map"));
document.getElementById("locations-tab-pane").appendChild(mapDiv);
mapResizeObserver.observe(mapDiv);
14 changes: 10 additions & 4 deletions organisation/templates/organisation/address_book.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ <h1>{{ page_title }}</h1>
</div>
</div>


<div class="row">
<div class="col">
<!-- .map element is initially placed outside the tab-pane due to Leaflet rendering weirdness. -->
<div id="map"></div>
</div>
</div>

<div class="row">
<div class="col">
<ul class="nav nav-tabs nav-fill" id="tab_labels" role="tablist">
Expand Down Expand Up @@ -125,21 +133,19 @@ <h1>{{ page_title }}</h1>

<div class="tab-pane fade" id="locations-tab-pane" role="tabpanel" aria-labelledby="locations-tab" tabindex="0"></div>
</div><!-- .tab-content -->

<!-- .map element is initially placed outside the tab-pane due to Leaflet rendering weirdness. -->
<div id="map"></div>
</div>
</div>
</div>
{% endblock page_content %}

{% block extra_js %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" integrity="sha512-puJW3E/qXDqYp9IfhAI54BJEaWIfloJ7JWs7OeD5i6ruC9JZL1gERT1wjtwXFlh7CjE7ZJ+/vcRZRkIYIb6p4g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/leaflet.markercluster.min.js" integrity="sha512-TiMWaqipFi2Vqt4ugRzsF8oRoGFlFFuqIi30FFxEPNw58Ov9mOy6LgC05ysfkxwLE0xVeZtmr92wVg9siAFRWA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const geoserver_url = "{{ geoserver_url }}";
const locationFeatures = JSON.parse("{{ locations_geojson|escapejs }}");
const location_features_url = "{% url 'location_api_resource' %}";
</script>
<script src="{% static 'js/location_map.js' %}"></script>
{% endblock extra_js %}
17 changes: 10 additions & 7 deletions organisation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ def get_context_data(self, **kwargs):
context["previous_pages"] = get_previous_pages(context["page_obj"])
context["next_pages"] = get_next_pages(context["page_obj"])
context["geoserver_url"] = settings.GEOSERVER_URL
context["locations_geojson"] = serialize(
"geojson",
Location.objects.filter(active=True, point__isnull=False, ascender_desc__isnull=False),
geometry_field="point",
srid=4283,
fields=["name", "phone", "ascender_desc"],
)
return context

def get_queryset(self):
Expand Down Expand Up @@ -203,6 +196,16 @@ def get(self, request, *args, **kwargs):
# Tailor the API response.
if "selectlist" in request.GET: # Smaller response, for use in HTML select lists.
locations = [{"id": location.pk, "text": location.name} for location in queryset]
elif "format" in request.GET and request.GET["format"] == "geojson":
# Return the API response in GeoJSON format.
locations = serialize(
"geojson",
Location.objects.filter(active=True, point__isnull=False, ascender_desc__isnull=False),
geometry_field="point",
srid=4283,
fields=["id", "name", "address", "phone", "ascender_desc"],
)
return HttpResponse(content=locations, content_type="application/json")
else:
locations = [
{
Expand Down
48 changes: 24 additions & 24 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f7a75d3

Please sign in to comment.