Skip to content
Draft
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
27 changes: 27 additions & 0 deletions components/remote_executor/geoapp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import time
import os
from urllib3.util.retry import Retry
from datetime import datetime, timezone, timedelta

RETRY_INTERVAL = 2880 # num of intervals -> every minute
RETRY_LIMIT_SECONDS = 172800.0 # 2 days in seconds
Expand Down Expand Up @@ -33,9 +34,29 @@ def __init__(self, geoap_creds):
)

self.api_endpoint = geoap_creds_data["API_ENDPOINT"]
self.main_domain = geoap_creds_data["MAIN_DOMAIN"]
self.main_key = geoap_creds_data["MAIN_KEY"]
self.http = http
self.log = log

def update_estimated_finish_time(self, duration):
request_id = os.getenv('REQUEST_ID', 980)
try:
response = self.http.request(
"PATCH",
f"{self.main_domain}/api/request/{request_id}",
body=json.dumps({"finished_in": duration}),
headers={
"Authorization": f"Token {self.main_key}",
"Content-Type": "application/json"
})
if response.status == 200:
self.log.info(f"Request with time update sent, duration: {duration}, request_id: {request_id}")
else:
self.log.info(f"Request with time update failed, {response.status}")
except urllib3.exceptions.RequestError as e:
self.log.info(f"Request failed: {e}")

def get_component_id(self, name):
url = self.api_endpoint + "api/notebook"
response = self.http.request("GET", url)
Expand Down Expand Up @@ -77,6 +98,12 @@ def wait_for_request_success_finish(self, request_id):
while time.time() < start_time + RETRY_LIMIT_SECONDS:
response = self.http.request("GET", url)
curr_request = json.loads(response.data.decode())
if curr_request.get("estimated_finish_time"):
self.log.info(f"Estimated finish time: {curr_request.get('estimated_finish_time')}")
estimated_finish_dt = datetime.fromisoformat(curr_request.get("estimated_finish_time")
.rstrip("Z")).replace(tzinfo=timezone.utc)
duration = (estimated_finish_dt - datetime.now(timezone.utc)).total_seconds()
self.update_estimated_finish_time(str(timedelta(seconds=duration)))
if curr_request.get("finished_at"):
if not curr_request.get("calculated"):
return False, curr_request.get("error")
Expand Down
4 changes: 4 additions & 0 deletions webapplication/aoi/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
class AoIIsOwnerPermission(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.user == request.user

class IsAdminUserOverride(BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_staff
17 changes: 16 additions & 1 deletion webapplication/aoi/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class RequestSerializer(serializers.ModelSerializer):
notebook = serializers.PrimaryKeyRelatedField(source='component', many=False, queryset=Component.objects,
label="Component id")

finished_in = serializers.DurationField(write_only=True, required=False)

def create(self, validated_data):
if validated_data.get("aoi"):
validated_data.update({'polygon': validated_data["aoi"].polygon})
Expand All @@ -65,6 +67,10 @@ def validate(self, attrs):
- If chosen notebook that require period then date_from and date_to in request
are required as well
"""
request = self.context.get("request")
if request and request.method == "PATCH":
return super().validate(attrs)

if attrs['component'].period_required and (not 'date_from' in attrs or not 'date_to' in attrs):
exception_details = {}
if not 'date_from' in attrs:
Expand All @@ -76,6 +82,14 @@ def validate(self, attrs):
raise serializers.ValidationError(exception_details)
return super().validate(attrs)

def update(self, instance, validated_data):
finished_in = validated_data.pop("finished_in", None)
if finished_in:
instance.estimated_finish_time = timezone.now() + finished_in
instance.save()
return instance


def to_representation(self, instance):
data = super().to_representation(instance)
if instance.estimated_finish_time:
Expand All @@ -90,4 +104,5 @@ class Meta:
model = Request
fields = ('id', 'user', 'aoi', 'notebook', 'notebook_name',
'date_from', 'date_to', 'started_at', 'finished_at', 'error', 'calculated', 'success', 'polygon',
'additional_parameter', 'additional_parameter2', 'pre_submit', 'request_origin', 'user_readable_errors')
'additional_parameter', 'additional_parameter2', 'pre_submit', 'request_origin',
'user_readable_errors', 'estimated_finish_time', 'finished_in')
4 changes: 2 additions & 2 deletions webapplication/aoi/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.urls import path
from .views import (AoIListCreateAPIView, AoIRetrieveUpdateDestroyAPIView, AOIResultsListAPIView,
ComponentListCreateAPIView, ComponentRetrieveUpdateDestroyAPIView,
RequestListCreateAPIView, RequestRetrieveAPIView, AOIRequestListAPIView)
RequestListCreateAPIView, RequestRetrieveUpdateAPIView, AOIRequestListAPIView)

app_name = 'aoi'
urlpatterns = [
Expand All @@ -14,5 +14,5 @@
path('notebook/<int:pk>', ComponentRetrieveUpdateDestroyAPIView.as_view(), name='notebook'),

path('request', RequestListCreateAPIView.as_view(), name='request_list_or_create'),
path('request/<int:pk>', RequestRetrieveAPIView.as_view(), name='request'),
path('request/<int:pk>', RequestRetrieveUpdateAPIView.as_view(), name='request'),
]
10 changes: 5 additions & 5 deletions webapplication/aoi/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
from rest_framework import status
from rest_framework.serializers import ValidationError, as_serializer_error
from rest_framework.response import Response
from rest_framework.generics import ListAPIView, RetrieveUpdateDestroyAPIView, ListCreateAPIView, RetrieveAPIView
from rest_framework.generics import ListAPIView, RetrieveUpdateDestroyAPIView, ListCreateAPIView, RetrieveUpdateAPIView
from rest_framework.generics import get_object_or_404
from publisher.serializers import ResultSerializer
from publisher.models import Result
from publisher.filters import ResultsByACLFilterBackend
from .models import AoI, Component, Request
from .serializers import AoISerializer, ComponentSerializer, RequestSerializer
from user.permissions import ModelPermissions, IsOwnerPermission
from .permissions import AoIIsOwnerPermission
from .permissions import AoIIsOwnerPermission, IsAdminUserOverride
from user.models import User, Transaction
from allauth.account import app_settings
from allauth.utils import build_absolute_uri
Expand Down Expand Up @@ -237,7 +237,7 @@ def create(self, request, *args, **kwargs):
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)


class RequestRetrieveAPIView(RetrieveAPIView):
class RequestRetrieveUpdateAPIView(RetrieveUpdateAPIView):
"""
Reads Request fields.
Accepts: GET method.
Expand All @@ -246,10 +246,10 @@ class RequestRetrieveAPIView(RetrieveAPIView):
'finished_at', 'error', 'calculated', 'success', 'polygon', 'additional_parameter'.
Returns: RequestModel fields.
"""
permission_classes = (ModelPermissions, IsOwnerPermission)
permission_classes = (IsAdminUserOverride | (ModelPermissions & IsOwnerPermission),)
queryset = Request.objects.all()
serializer_class = RequestSerializer
http_method_names = ("get", )
http_method_names = ("get", "patch")


class AOIRequestListAPIView(ListAPIView):
Expand Down