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

Improve dataset API #299

Merged
merged 8 commits into from
Sep 3, 2024
Merged
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ devweb-load-demo-data:
@docker compose $(ARGS) exec -T dev bash -c "python manage.py loaddata core/fixtures/demo/1.core.json"
@docker compose $(ARGS) exec -T dev bash -c "python manage.py loaddata core/fixtures/demo/2.geosight_georepo.json"
@docker compose $(ARGS) exec -T dev bash -c "python manage.py loaddata core/fixtures/demo/3.geosight_data.json"
@docker compose $(ARGS) exec -T dev bash -c "python manage.py refresh_materialized_views"

devweb-test:
@echo
Expand Down
36 changes: 36 additions & 0 deletions django_project/core/api/refresh_materialized_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# coding=utf-8
"""
GeoSight is UNICEF's geospatial web-based business intelligence platform.

Contact : [email protected]

.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

"""
__author__ = '[email protected]'
__date__ = '02/10/2024'
__copyright__ = ('Copyright 2023, Unicef')

from rest_framework.response import Response
from rest_framework.views import APIView

from core.permissions import AdminAuthenticationPermission
from geosight.data.models.indicator.indicator_value import (
IndicatorValueWithGeo
)


class RefreshMaterializedViewApi(APIView):
"""Refresh materialized view API."""

permission_classes = (AdminAuthenticationPermission,)

def post(self, request, *args, **kwargs):
"""Get access request list."""
view = request.data['view']
if view == IndicatorValueWithGeo._meta.db_table:
IndicatorValueWithGeo.refresh_materialized_views()
return Response(status=201)
36 changes: 36 additions & 0 deletions django_project/core/models/materialized_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# coding=utf-8
"""
GeoSight is UNICEF's geospatial web-based business intelligence platform.

Contact : [email protected]

.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

"""
__author__ = '[email protected]'
__date__ = '02/10/2024'
__copyright__ = ('Copyright 2023, Unicef')

from django.db import connection

from core.utils import child_classes


class MaterializeViewModel:
"""Materialized view model."""

@classmethod
def refresh_materialized_views(cls):
"""Refresh materialized views."""
with connection.cursor() as cursor:
query = f'REFRESH MATERIALIZED VIEW {cls._meta.db_table}'
print(query)
cursor.execute(query)

@staticmethod
def child_classes():
"""Return child classes."""
return child_classes(MaterializeViewModel)
24 changes: 24 additions & 0 deletions django_project/core/static/admin/refresh-materialized-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
function refreshMaterializedView(view, button) {
button.disabled = true;
fetch('/api/refresh-materialized-view',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value
},
method: "POST",
body: JSON.stringify({
view: view
})
})
.then(_ => {
button.disabled = false;
window.location.reload();
})
.catch(error => {
console.error(error);
button.disabled = false;
});

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% extends "admin/change_list.html" %}
{% load static %}

{% block extrahead %}
{{ block.super }}
<script src="{% static 'admin/refresh-materialized-view.js' %}"></script>
<script>
// static/admin_custom.js
document.addEventListener('DOMContentLoaded', function () {
const button = document.getElementById('refresh-materialized-view');
if (button) {
button.addEventListener('click', function () {
refreshMaterializedView('mv_indicator_value_geo', button)
});
}
});
</script>
{% endblock %}

{% block object-tools %}
{{ block.super }}
<div class="button-group" style="width: 100%; text-align: right">
<button id="refresh-materialized-view"
class="button"
style="right: 0;"
>Refresh materialized view
</button>
</div>
{% endblock %}
5 changes: 5 additions & 0 deletions django_project/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
)
from core.api.maintenance import MaintenanceAPI
from core.api.proxy import ProxyView
from core.api.refresh_materialized_view import RefreshMaterializedViewApi
from core.api.sentry import trigger_error
from core.api.user import UserListAPI, UserDetailAPI, UserApiKey

Expand Down Expand Up @@ -158,6 +159,10 @@ def get_schema(self, request=None, public=False):
url(r'^user/', include(user_api)),
url(r'^user/', include(user_api)),
url(r'^access/', include(request_access_api)),
url(
r'^refresh-materialized-view$', RefreshMaterializedViewApi.as_view(),
name='refresh-materialized-view'
),
]
urlpatterns += [
url(r'^tinymce/', include('tinymce.urls')),
Expand Down
41 changes: 41 additions & 0 deletions django_project/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,44 @@ def set_query_parameter(url, params):
url_new_query = urlencode(url_dict)
url_parse = url_parse._replace(query=url_new_query)
return urlunparse(url_parse)


def child_classes(Class):
"""Return child classes."""
# If has subclasses
if not len(Class.__subclasses__()):
return [Class]

# If has subclasses
classes = []
for _class in Class.__subclasses__():
if len(_class.__subclasses__()):
for child in _class.__subclasses__():
classes += child_classes(child)
else:
classes.append(_class)
return classes


class temp_disconnect_signal(object):
"""Temporarily disconnect a model from a signal."""

def __init__(self, signal, receiver, sender):
"""Initialise the temporary disconnect signal."""
self.signal = signal
self.receiver = receiver
self.sender = sender

def __enter__(self):
"""Enter the temporary disconnect signal."""
self.signal.disconnect(
receiver=self.receiver,
sender=self.sender
)

def __exit__(self, type, value, traceback):
"""Exit the temporary disconnect signal."""
self.signal.connect(
receiver=self.receiver,
sender=self.sender
)
41 changes: 25 additions & 16 deletions django_project/frontend/src/pages/Admin/AdminListPagination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ export const AdminListPagination = forwardRef(

// Other attributes
const pageSize = 25;
const [parameters, setParameters] = useState({
page: 0,
page_size: pageSize
})
let initParameters = getParameters({})
initParameters = {
...{
page: 0,
page_size: pageSize
}, ...initParameters
}
const [parameters, setParameters] = useState(initParameters)
const [data, setData] = useState([])
const [rowSize, setRowSize] = useState(0)
const [error, setError] = useState(null);
Expand Down Expand Up @@ -105,7 +109,14 @@ export const AdminListPagination = forwardRef(

try {
const data = await fetchJSON(url, {}, false)
// Checking quick data

// Set the data
if (prev.urlRequest === url) {
setRowSize(data.count)
setData(data.results)
}

// Fetch quick data for filter
if (props.quickDataChanged) {
const quickParams = jsonToUrlParams(
dictDeepCopy(
Expand All @@ -115,20 +126,18 @@ export const AdminListPagination = forwardRef(
if (!quickParams) {
props.quickDataChanged({})
} else {
const quickData = await fetchJSON(
urlData + 'data' + '?' + quickParams, {}, false
)
if (prev.urlRequest === url) {
props.quickDataChanged(quickData)
try {
const quickData = await fetchJSON(
urlData + 'data' + '?' + quickParams, {}, false
)
if (prev.urlRequest === url) {
props.quickDataChanged(quickData)
}
} catch (e) {
console.log(`Quick data is error ${e}`)
}
}
}

// Set the data
if (prev.urlRequest === url) {
setRowSize(data.count)
setData(data.results)
}
} catch (error) {
if (error.message === 'Invalid page.') {
setParameters({ ...parameters, page: 0 })
Expand Down
2 changes: 2 additions & 0 deletions django_project/frontend/src/pages/Admin/DataBrowser/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default function DataBrowserAdmin() {
geographies: defaultFilters.geographies ? splitParams(defaultFilters.geographies) : [],
fromTime: defaultFilters.fromTime ? defaultFilters.fromTime : null,
toTime: defaultFilters.toTime ? defaultFilters.toTime : null,
detail: true,
})
const [updatedData, setUpdatedData] = useState([]);
const [disabled, setDisabled] = useState(false)
Expand Down Expand Up @@ -271,6 +272,7 @@ export default function DataBrowserAdmin() {
} else {
delete parameters['date__lte']
}
parameters['detail'] = filters.detail
return parameters
}

Expand Down
6 changes: 4 additions & 2 deletions django_project/frontend/src/pages/Admin/Dataset/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default function DatasetAdmin() {
indicators: defaultFilters.indicators ? splitParams(defaultFilters.indicators) : [],
datasets: defaultFilters.datasets ? splitParams(defaultFilters.datasets, false) : [],
levels: defaultFilters.levels ? splitParams(defaultFilters.levels) : [],
detail: true,
})
const [disabled, setDisabled] = useState(false)
const [isInit, setIsInit] = useState(true)
Expand Down Expand Up @@ -98,8 +99,8 @@ export default function DatasetAdmin() {
// COLUMNS
const COLUMNS = [
{ field: 'id', headerName: 'id', hide: true },
{ field: 'indicator_name', headerName: 'Indicator', flex: 1 },
{ field: 'reference_layer_name', headerName: 'View', flex: 0.5 },
{ field: 'indicator_name', headerName: 'Indicator', flex: 0.5 },
{ field: 'reference_layer_name', headerName: 'View', flex: 1 },
{ field: 'admin_level', headerName: 'Level', width: 80 },
{ field: 'start_date', headerName: 'Start date', width: 130 },
{ field: 'end_date', headerName: 'End date', width: 130 },
Expand Down Expand Up @@ -199,6 +200,7 @@ export default function DatasetAdmin() {
} else {
delete parameters['admin_level__in']
}
parameters['detail'] = filters.detail
return parameters
}

Expand Down
26 changes: 25 additions & 1 deletion django_project/geosight/data/admin/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

from geosight.data.models.indicator import (
Indicator, IndicatorGroup,
IndicatorValue, IndicatorRule, IndicatorExtraValue
IndicatorValue, IndicatorRule, IndicatorExtraValue,
IndicatorValueWithGeo
)


Expand Down Expand Up @@ -69,6 +70,29 @@ class IndicatorGroupAdmin(admin.ModelAdmin):
list_display = ('name',)


@admin.register(IndicatorValueWithGeo)
class IndicatorValueWithGeoAdmin(admin.ModelAdmin):
"""Admin for checking indicator values with geometry."""

change_list_template = 'admin/indicator_value_with_geo_admin.html'
list_display = (
'reference_layer_name', 'reference_layer_uuid', 'indicator_name',
'date', 'value'
)

def has_add_permission(self, request):
"""Return True if the user has add permission."""
return False

def has_change_permission(self, request, obj=None):
"""Return True if the user has change permission."""
return False

def has_delete_permission(self, request, obj=None):
"""Return True if the user has delete permission."""
return False


admin.site.register(IndicatorGroup, IndicatorGroupAdmin)
admin.site.register(IndicatorValue, IndicatorValueAdmin)
admin.site.register(Indicator, IndicatorAdmin)
Loading
Loading