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-1890-http-api-schenario-bundle-scenario-dataset #1894

Draft
wants to merge 11 commits into
base: develop
Choose a base branch
from
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ venv*/
apache*
/oep-django-5


.DS_Store

# Deployment files
Expand Down
50 changes: 50 additions & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from re import match
from uuid import UUID

from django.urls import reverse
from rest_framework import serializers

Expand Down Expand Up @@ -53,3 +56,50 @@ class Meta:
model = Table
# fields = ["id", "model_name", "acronym", "url"]
fields = ["id", "name", "human_readable_name", "url"]


class DatasetSerializer(serializers.Serializer):
name = serializers.CharField(max_length=255, required=True) # Dataset table name
type = serializers.ChoiceField(
choices=["input", "output"], required=True
) # Type: input or output

# Custom validation for 'name'
def validate_name(self, value):
# Use regex to allow alphanumeric characters and underscores
if not match(r"^[\w]+$", value):
raise serializers.ValidationError(
"Dataset name should contain only"
"alphanumeric characters and underscores."
)
# Add any additional custom validation logic here
return value


class ScenarioBundleScenarioDatasetSerializer(serializers.Serializer):
scenario = serializers.UUIDField(required=True) # Validate the scenario UUID
dataset = serializers.ListField(
child=DatasetSerializer(), required=True
) # List of datasets with 'name' and 'type'

# Custom validation for 'scenario'
def validate_scenario(self, value):
try:
UUID(str(value))
except ValueError:
raise serializers.ValidationError("Invalid UUID format for scenario.")
# Add any additional custom validation logic here
return value

# Custom validation for the entire dataset list
def validate_dataset(self, value):
if not value:
raise serializers.ValidationError("The dataset list cannot be empty.")

# Check for duplicates in dataset names
dataset_names = [dataset["name"] for dataset in value]
if len(dataset_names) != len(set(dataset_names)):
raise serializers.ValidationError("Dataset names must be unique.")

# Add any additional custom validation logic here
return value
49 changes: 49 additions & 0 deletions api/sparql_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import requests

from factsheet.oekg.connection import update_endpoint


def add_datasets_to_scenario(scenario_uuid, dataset_name, dataset_type):
"""
Function to add datasets to a scenario bundle in Jena Fuseki.
"""

sparql_query = f"""
PREFIX oeo: <http://example.org/ontology#>
INSERT DATA {{
GRAPH <http://example.org/scenario/{scenario_uuid}> {{
oeo:{dataset_name} a oeo:{dataset_type}Dataset .
}}
}}
"""
response = send_sparql_update(sparql_query)
if not response.ok:
return False # Return False if any query fails
return True


def remove_datasets_from_scenario(scenario_uuid, dataset_name, dataset_type):
"""
Function to remove datasets from a scenario bundle in Jena Fuseki.
"""
sparql_query = f"""
PREFIX oeo: <http://example.org/ontology#>
DELETE DATA {{
GRAPH <http://example.org/scenario/{scenario_uuid}> {{
oeo:{dataset_name} a oeo:{dataset_type}Dataset .
}}
}}
"""
response = send_sparql_update(sparql_query)
if not response.ok:
return False # Return False if any query fails
return True


def send_sparql_update(query):
"""
Helper function to send a SPARQL update query to Fuseki.
"""
headers = {"Content-Type": "application/sparql-update"}
response = requests.post(update_endpoint, data=query, headers=headers)
return response
71 changes: 71 additions & 0 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
from omi.dialects.oep.compiler import JSONCompiler
from omi.structure import OEPMetadata
from rest_framework import generics, status
from rest_framework.permissions import IsAuthenticated

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

import api.parser
Expand All @@ -43,12 +47,15 @@
from api.serializers import (
EnergyframeworkSerializer,
EnergymodelSerializer,
ScenarioBundleScenarioDatasetSerializer,
ScenarioDataTablesSerializer,
)
from api.sparql_helpers import add_datasets_to_scenario, remove_datasets_from_scenario
from dataedit.models import Embargo
from dataedit.models import Schema as DBSchema
from dataedit.models import Table as DBTable
from dataedit.views import get_tag_keywords_synchronized_metadata, schema_whitelist
from factsheet.permission_decorator import only_if_user_is_owner_of_scenario_bundle
from modelview.models import Energyframework, Energymodel
from oeplatform.settings import PLAYGROUNDS, UNVERSIONED_SCHEMAS, USE_LOEP, USE_ONTOP

Expand Down Expand Up @@ -1575,3 +1582,67 @@ class ScenarioDataTablesListAPIView(generics.ListAPIView):
topic = "scenario"
queryset = DBTable.objects.filter(schema__name=topic)
serializer_class = ScenarioDataTablesSerializer


class ManageScenarioDatasets(APIView):
permission_classes = [IsAuthenticated] # Require authentication

@only_if_user_is_owner_of_scenario_bundle
def post(self, request):
serializer = ScenarioBundleScenarioDatasetSerializer(data=request.data)
if serializer.is_valid():
scenario_uuid = serializer.validated_data["scenario"]
datasets = serializer.validated_data["dataset"]

# Iterate over each dataset to process it properly
for dataset in datasets:
dataset_name = dataset["name"]
dataset_type = dataset["type"]

# Add datasets to the scenario in the bundle (implementation depends
# on your model)
success = add_datasets_to_scenario(
scenario_uuid, dataset_name, dataset_type
)
if not success:
return Response(
{"error": "Failed to add datasets"},
status=status.HTTP_400_BAD_REQUEST,
)

return Response(
{"message": "Datasets added successfully"},
status=status.HTTP_200_OK,
)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@only_if_user_is_owner_of_scenario_bundle
def delete(self, request):
serializer = ScenarioBundleScenarioDatasetSerializer(data=request.data)
if serializer.is_valid():
scenario_uuid = serializer.validated_data["scenario"]
datasets = serializer.validated_data["dataset"]

# Iterate over each dataset to process it properly
for dataset in datasets:
dataset_name = dataset["name"]
dataset_type = dataset["type"]

# Remove the dataset from the scenario in the bundle
success = remove_datasets_from_scenario(
scenario_uuid, dataset_name, dataset_type
)

if not success:
return Response(
{"error": f"Failed to remove dataset {dataset_name}"},
status=status.HTTP_400_BAD_REQUEST,
)

return Response(
{"message": "Datasets removed successfully"},
status=status.HTTP_200_OK,
)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
6 changes: 0 additions & 6 deletions dataedit/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2355,9 +2355,6 @@ def post(self, request, schema, table, review_id):
Handle POST requests for contributor's review. Merges and updates
the review data in the PeerReview table.

Missing parts:
- merge contributor field review and reviewer field review

Args:
request (HttpRequest): The incoming HTTP POST request.
schema (str): The schema of the table.
Expand All @@ -2367,9 +2364,6 @@ def post(self, request, schema, table, review_id):
Returns:
HttpResponse: Rendered HTML response for contributor review.

Note:
This method has some missing parts regarding the merging of contributor
and reviewer field review.
"""

context = {}
Expand Down
8 changes: 4 additions & 4 deletions sparql_query/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponseBadRequest, JsonResponse
from django.shortcuts import render
from django.views.decorators.http import require_GET
from django.views.decorators.http import require_POST

from oeplatform.settings import OEKG_SPARQL_ENDPOINT_URL
from sparql_query.utils import validate_sparql_query
Expand All @@ -14,9 +14,9 @@ def main_view(request):
return response


@require_GET
@require_POST
def sparql_endpoint(request):
sparql_query = request.GET.get("query", "")
sparql_query = request.POST.get("query", "")

if not sparql_query:
return HttpResponseBadRequest("Missing 'query' parameter.")
Expand All @@ -28,7 +28,7 @@ def sparql_endpoint(request):

headers = {"Accept": "application/sparql-results+json"}

response = requests.get(
response = requests.post(
endpoint_url, params={"query": sparql_query}, headers=headers
)

Expand Down
Loading