Skip to content

Commit

Permalink
Biocompute model (#284)
Browse files Browse the repository at this point in the history
* Remove API app
Changes to be committed:
	deleted:    api/__init__.py
	deleted:    api/admin.py
	deleted:    api/apps.py
	deleted:    api/fixtures/bootstrap.json
	deleted:    api/fixtures/metafixtures
	deleted:    api/fixtures/metafixtures.json
	deleted:    api/keys.sh
	deleted:    api/migrations/0001_initial.py
	deleted:    api/migrations/0002_auto_20220124_2356.py
	deleted:    api/migrations/0003_rename_meta_table_prefix_table.py
	deleted:    api/migrations/0004_rename_group_info_groupinfo.py
	deleted:    api/migrations/0005_rename_prefixes_prefix.py
	deleted:    api/migrations/0006_delete_new_users.py
	deleted:    api/migrations/__init__.py
	deleted:    api/model/__init__.py
	deleted:    api/model/groups.py
	deleted:    api/model/prefix.py
	deleted:    api/models.py
	deleted:    api/permissions.py
	deleted:    api/rdb.sh
	deleted:    api/request_definitions/GET.schema
	deleted:    api/request_definitions/POST.schema
	deleted:    api/request_definitions/templates/DELETE_delete_object_by_id.schema
	deleted:    api/request_definitions/templates/GET_activate_account.schema
	deleted:    api/request_definitions/templates/GET_get_object_by_id.schema
	deleted:    api/request_definitions/templates/GET_retrieve_available_schema.schema
	deleted:    api/request_definitions/templates/POST_convert_existing_object_between_schemas.schema
	deleted:    api/request_definitions/templates/POST_convert_payload_to_schema.schema
	deleted:    api/request_definitions/templates/POST_new_account.schema
	deleted:    api/request_definitions/templates/POST_object_listing_by_token.schema
	deleted:    api/request_definitions/templates/POST_objects_draft.schema
	deleted:    api/request_definitions/templates/POST_objects_publish.schema
	deleted:    api/request_definitions/templates/POST_read_object.schema
	deleted:    api/request_definitions/templates/POST_validate_payload_against_schema.schema
	deleted:    api/scripts/__init__.py
	deleted:    api/scripts/method_specific/GET_draft_object_by_id.py
	deleted:    api/scripts/method_specific/GET_published_object_by_id.py
	deleted:    api/scripts/method_specific/GET_published_object_by_id_with_version.py
	deleted:    api/scripts/method_specific/GET_retrieve_available_schema.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_create.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_delete.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_modify.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_permissions.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_permissions_set.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_publish.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_read.py
	deleted:    api/scripts/method_specific/POST_api_objects_drafts_token.py
	deleted:    api/scripts/method_specific/POST_api_objects_publish.py
	deleted:    api/scripts/method_specific/POST_api_objects_published.py
	deleted:    api/scripts/method_specific/POST_api_objects_search.py
	deleted:    api/scripts/method_specific/POST_api_objects_token.py
	deleted:    api/scripts/method_specific/POST_validate_payload_against_schema.py
	deleted:    api/scripts/method_specific/__init__.py
	deleted:    api/scripts/utilities/DbUtils.py
	deleted:    api/scripts/utilities/FileUtils.py
	deleted:    api/scripts/utilities/JsonUtils.py
	deleted:    api/scripts/utilities/RequestUtils.py
	deleted:    api/scripts/utilities/ResponseUtils.py
	deleted:    api/scripts/utilities/SettingsUtils.py
	deleted:    api/scripts/utilities/UserUtils.py
	deleted:    api/scripts/utilities/__init__.py
	deleted:    api/serializers.py
	deleted:    api/signals.py
	deleted:    api/templates/api/account_activation_message.html
	deleted:    api/urls.py
	deleted:    api/validation_definitions/IEEE/2791object.json
	deleted:    api/validation_definitions/IEEE/description_domain.json
	deleted:    api/validation_definitions/IEEE/error_domain.json
	deleted:    api/validation_definitions/IEEE/execution_domain.json
	deleted:    api/validation_definitions/IEEE/io_domain.json
	deleted:    api/validation_definitions/IEEE/parametric_domain.json
	deleted:    api/validation_definitions/IEEE/provenance_domain.json
	deleted:    api/validation_definitions/IEEE/usability_domain.json
	deleted:    api/validation_definitions/IEEE_sub/IEEE2791-2020.schema
	deleted:    api/validation_definitions/IEEE_sub/domains/description_domain.json
	deleted:    api/validation_definitions/IEEE_sub/domains/error_domain.json
	deleted:    api/validation_definitions/IEEE_sub/domains/execution_domain.json
	deleted:    api/validation_definitions/IEEE_sub/domains/io_domain.json
	deleted:    api/validation_definitions/IEEE_sub/domains/parametric_domain.json
	deleted:    api/validation_definitions/IEEE_sub/domains/provenance_domain.json
	deleted:    api/validation_definitions/IEEE_sub/domains/usability_domain.json
	deleted:    api/validation_definitions/uri_external
	deleted:    api/views.py
	new file:   config/settings.py
	modified:   config/urls.py

Changes not staged for commit:
	modified:   authentication/apis.py
	modified:   authentication/migrations/0001_initial.py
	deleted:    authentication/migrations/0002_newuser.py
	modified:   authentication/services.py
	modified:   docs/refactor.md
	modified:   search/selectors.py
	modified:   tests/fixtures/test_data.json
	deleted:    tests/test_database.py
	deleted:    tests/test_fixtures.py
	deleted:    tests/test_models
	modified:   tests/test_views/test_api_account_activate.py
	deleted:    tests/test_views/test_api_accounts_describe.py
	modified:   tests/test_views/test_api_auth_add.py
	modified:   tests/test_views/test_api_auth_reset_token.py
	deleted:    tests/test_views/test_api_groups_group_info.py
	deleted:    tests/test_views/test_api_groups_modify.py
	deleted:    tests/test_views/test_api_objects.py
	deleted:    tests/test_views/test_api_objects_drafts_create.py
	deleted:    tests/test_views/test_api_objects_drafts_modify.py
	deleted:    tests/test_views/test_api_objects_drafts_publish.py
	deleted:    tests/test_views/test_api_objects_search.py
	deleted:    tests/test_views/test_api_objects_validate.py
	deleted:    tests/test_views/test_api_prefixes_create.py
	deleted:    tests/test_views/test_api_prefixes_token.py
	deleted:    tests/test_views/test_get_object_id_draft.py
	deleted:    tests/test_views/test_get_objectid.py
	deleted:    tests/test_views/test_published_object_by_id.py
	modified:   token.json

* Framework for BioCompute model

Changes to be committed:
	new file:   biocompute/__init__.py
	new file:   biocompute/admin.py
	new file:   biocompute/apis.py
	new file:   biocompute/migrations/__init__.py
	new file:   biocompute/models.py
	new file:   biocompute/selectors.py
	new file:   biocompute/services.py
	new file:   biocompute/urls.py

* Added files for Prefix model

Changes to be committed:
	new file:   prefix/__init__.py
	new file:   prefix/admin.py
	new file:   prefix/apis.py
	new file:   prefix/apps.py
	new file:   prefix/migrations/__init__.py
	new file:   prefix/models.py
	new file:   prefix/selectors.py
	new file:   prefix/services.py
	new file:   prefix/urls.py

* Tests for authentication

Changes to be committed:
	modified:   .gitignore
	modified:   authentication/apis.py
	modified:   authentication/migrations/0001_initial.py
	deleted:    authentication/migrations/0002_newuser.py
	modified:   authentication/services.py
	new file:   biocompute/migrations/0001_initial.py
	modified:   docs/refactor.md
	new file:   prefix/migrations/0001_initial.py
	modified:   search/selectors.py
	new file:   test.json
	new file:   tests/fixtures/old_test_data.json
	modified:   tests/fixtures/test_data.json
	deleted:    tests/test_database.py
	deleted:    tests/test_fixtures.py
	deleted:    tests/test_models
	modified:   tests/test_views/test_api_account_activate.py
	renamed:    tests/test_views/test_api_accounts_describe.py -> tests/test_views/test_api_account_describe.py
	modified:   tests/test_views/test_api_auth_add.py
	modified:   tests/test_views/test_api_auth_reset_token.py
	deleted:    tests/test_views/test_api_groups_group_info.py
	deleted:    tests/test_views/test_api_groups_modify.py
	deleted:    tests/test_views/test_api_objects.py
	deleted:    tests/test_views/test_api_objects_drafts_create.py
	deleted:    tests/test_views/test_api_objects_drafts_modify.py
	deleted:    tests/test_views/test_api_objects_drafts_publish.py
	deleted:    tests/test_views/test_api_objects_search.py
	deleted:    tests/test_views/test_api_objects_validate.py
	deleted:    tests/test_views/test_api_prefixes_create.py
	deleted:    tests/test_views/test_api_prefixes_token.py
	deleted:    tests/test_views/test_get_object_id_draft.py
	deleted:    tests/test_views/test_get_objectid.py
	deleted:    tests/test_views/test_published_object_by_id.py
	modified:   token.json

* Move Swagger files and implement BCO draft create

Changes to be committed:
	modified:   biocompute/apis.py
	modified:   biocompute/models.py
	modified:   biocompute/services.py
	modified:   biocompute/urls.py
	new file:   config/services.py
	modified:   config/urls.py
	modified:   docs/refactor.md
	modified:   prefix/urls.py
	new file:   tests/fixtures/example_bco.py
	new file:   tests/test_views/test_api_objects_drafts_create.py

* Doc fix in Bco.models
Changes to be committed:
	modified:   biocompute/models.py

* Updated biocompute.model and prefix.model

Added the DraftsCreateApi and tests.
This included serializers for BCO creation
Changes to be committed:
	modified:   biocompute/apis.py
	modified:   biocompute/services.py
	modified:   config/services.py
	modified:   prefix/models.py
	modified:   prefix/services.py
	modified:   test.json
	modified:   tests/fixtures/test_data.json
	new file:   tests/test_views/test_api_objects_drafts_create.py
  • Loading branch information
HadleyKing authored Mar 18, 2024
1 parent 5103418 commit 301693a
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 47 deletions.
77 changes: 52 additions & 25 deletions biocompute/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@

from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from django.db import utils
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from tests.fixtures.example_bco import BCO_000001
from config.services import legacy_api_converter
from config.services import legacy_api_converter, response_constructor
from biocompute.services import BcoDraftSerializer

class DraftsCreateApi(APIView):
"""
Create BCO Draft
Create BCO Draft [Bulk Enabled]
--------------------
Expand Down Expand Up @@ -63,49 +64,75 @@ class DraftsCreateApi(APIView):
@swagger_auto_schema(
request_body=request_body,
responses={
200: "Creation of BCO draft is successful.",
300: "Some requests failed and some succeeded.",
400: "Bad request.",
200: "All requests were accepted.",
207: "Some requests failed and some succeeded. Each object submitted"
" will have it's own response object with it's own status"
" code and message.\n",
400: "All requests were rejected.",
403: "Invalid token.",
},
tags=["BCO Management"],
)

def post(self, request) -> Response:
response_data = {}
response_data = []
owner = request.user
data = request.data
all_good = True
rejected_requests = False
accepted_requests = False
if 'POST_api_objects_draft_create' in request.data:
data = legacy_api_converter(request.data)

for index, object in enumerate(data):
list_id = object.get("object_id", index)
response_id = object.get("object_id", index)
bco = BcoDraftSerializer(data=object, context={'request': request})

if bco.is_valid():
bco.create(bco.validated_data)
response_data[list_id] = "bco valid"
try:
bco.create(bco.validated_data)
response_data.append(response_constructor(
identifier=bco['object_id'].value,
status = "SUCCESS",
code= 200,
message= f"BCO {bco['object_id'].value} created",
))
accepted_requests = True

except Exception as err:
print(err)
response_data.append(response_constructor(
identifier=bco['object_id'].value,
status = "SERVER ERROR",
code= 500,
message= f"BCO {bco['object_id'].value} failed",
))

else:
response_data[list_id] = bco.errors
all_good = False
response_data.append(response_constructor(
identifier=response_id,
status = "REJECTED",
code= 400,
message= f"BCO {response_id} rejected",
data=bco.errors
))
rejected_requests = True

print(accepted_requests, rejected_requests )

if all_good is False:
if accepted_requests is False and rejected_requests == True:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data=response_data
)

if accepted_requests is True and rejected_requests is True:
return Response(
status=status.HTTP_207_MULTI_STATUS,
data=response_data
)

return Response(status=status.HTTP_200_OK, data=response_data)

# def create(self, validated_data):
# # Custom creation logic here, if needed
# return Bco.objects.create(**validated_data)

# def update(self, instance, validated_data):
# # Custom update logic here, if needed
# for attr, value in validated_data.items():
# setattr(instance, attr, value)
# instance.save()
# return instance
if accepted_requests is True and rejected_requests is False:
return Response(
status=status.HTTP_200_OK,
data=response_data
)
84 changes: 68 additions & 16 deletions biocompute/services.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#!/usr/bin/env python3
# biocopmute/services.py

import re
from urllib.parse import urlparse
from django.conf import settings
from django.db import transaction
from django.utils import timezone
from biocompute.models import Bco
from prefix.models import Prefix
from prefix.services import prefix_counter_increment
from django.contrib.auth.models import Group, User
from rest_framework import serializers

Expand All @@ -13,6 +17,8 @@
Service functions for working with BCOs
"""

HOSTNAME = settings.PUBLIC_HOSTNAME

class BcoDraftSerializer(serializers.Serializer):
object_id = serializers.URLField(required=False)
contents = serializers.JSONField()
Expand All @@ -21,6 +27,9 @@ class BcoDraftSerializer(serializers.Serializer):
authorized_users = serializers.ListField(child=serializers.CharField(), required=False)

def validate(self, attrs):
"""BCO Draft Validator
"""

errors = {}
request = self.context.get('request')
attrs["owner"] = request.user
Expand All @@ -43,36 +52,38 @@ def validate(self, attrs):

# Validate Prefix
try:
attrs['prefix_instance'] = Prefix.objects.get(prefix=attrs['prefix'])
#set a name and instance for Prefix
attrs['prefix'] = Prefix.objects.get(prefix=attrs['prefix'])
attrs['prefix_name'] = attrs['prefix'].prefix
except Prefix.DoesNotExist as err:
errors['prefix'] = 'Invalid prefix.'
raise serializers.ValidationError(errors)

# Validate object_id match
if 'object_id' in attrs and attrs['object_id'] != attrs['contents'].get('object_id', ''):
errors["object_id"] = "object_id does not match object_id in contents."

# Validate that object_id is unique
object_id = attrs['contents'].get('object_id', '')

if not Bco.objects.filter(object_id=object_id).exists():
pass
# Validate or create object_id
if 'object_id' in attrs:
id_errors = validate_bco_object_id(
attrs['object_id'],
attrs['prefix_name']
)
if id_errors != 0:
errors["object_id"] = id_errors
else:
errors["object_id"] = f"That object_id, {attrs['object_id']}, already exists."
attrs['object_id'] = create_bco_id(attrs['prefix'])

# If erros exist than raise and exception and return it, otherwise
# return validated data
if errors:
raise serializers.ValidationError(errors)

return attrs

@transaction.atomic
def create(self, validated_data):
# Remove the non-model field 'prefix' and use 'prefix_instance' instead
prefix_instance = validated_data.pop('prefix_instance', None)
validated_data.pop('prefix')
# Remove the non-model field 'prefix_name' and use 'prefix' instance instead
validated_data.pop('prefix_name')
authorized_group_names = validated_data.pop('authorized_groups', [])
authorized_usernames = validated_data.pop('authorized_users', [])

bco_instance = Bco.objects.create(**validated_data, prefix=prefix_instance, last_update=timezone.now())
bco_instance = Bco.objects.create(**validated_data, last_update=timezone.now())

# Set ManyToMany relations
if authorized_group_names:
Expand All @@ -84,3 +95,44 @@ def create(self, validated_data):
bco_instance.authorized_users.set(authorized_users)

return bco_instance


def validate_bco_object_id(object_id: str, prefix_name: str):
"""Validate BCO object ID
Function to validate a proposed BCO object_id. Will reject the ID if the
following constraints are not met:
1. Correct hostname for this BCODB instance
2. Prefix submitted is not in the object_id
3. The object_id already exists
"""
errors = []

if HOSTNAME not in object_id:
errors.append("Object ID does not conform to the required format. "\
+ f"The hostname {HOSTNAME} is not in {object_id}")
if prefix_name not in object_id:
errors.append(f"Object ID, {object_id}, does not contain the "\
+ f"submitted prefix, {prefix_name}.")

if not Bco.objects.filter(object_id=object_id).exists():
pass
else:
errors.append(f"That object_id, {object_id}, already exists.")

if errors:
return errors
return 0

def create_bco_id(prefix: Prefix) -> str:
"""Create BCO object_id
Function to construct BCO object_id. Takes a Prefix model instance and
returns a bco.object_id.
"""

count = prefix_counter_increment(prefix)
bco_identifier = format(count, "06d")
bco_id = f"{HOSTNAME}/{prefix}_{bco_identifier}/DRAFT"

return bco_id
25 changes: 24 additions & 1 deletion config/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,27 @@ def legacy_api_converter(data:dict) ->dict:
"""

_, new_data = data.popitem()
return new_data
return new_data

def response_constructor(
identifier: str,
status: str,
code: str,
message: str=None,
data: dict= None
)-> dict:
"""Response Data Proccessing
"""
response_object = {
identifier: {
"request_status": status,
"status_code": code
}
}

if data is not None:
response_object[identifier]["data"] = data
if message is not None:
response_object[identifier]["message"] = message

return response_object
22 changes: 19 additions & 3 deletions prefix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,26 @@ class Prefix(models.Model):

prefix = models.CharField(primary_key=True, max_length=5)
certifying_key = models.TextField(blank=True, null=True)
created = models.DateTimeField(default=timezone.now, blank=True, null=True)
created = models.DateTimeField(
default=timezone.now,
blank=True,
null=True
)
description = models.TextField(blank=True, null=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, to_field="username")
authorized_groups = models.ManyToManyField(Group, blank=True, related_name='authorized_prefix')
owner = models.ForeignKey(
User,
on_delete=models.CASCADE,
to_field="username"
)
authorized_groups = models.ManyToManyField(
Group,
blank=True,
related_name='authorized_prefix'
)
counter = models.IntegerField(
default=0,
help_text="Counter for object_id asignment"
)

def __str__(self):
"""String for representing the BCO model (in Admin site etc.)."""
Expand Down
26 changes: 26 additions & 0 deletions prefix/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
# prefix/services.py

import re
from urllib.parse import urlparse
from django.conf import settings
from django.utils import timezone
from biocompute.models import Bco
from prefix.models import Prefix
from django.db.models import F

"""Prefix Services
Service functions for working with BCO Prefixes
"""

def prefix_counter_increment(prefix: Prefix) -> int:
"""Prefix Counter Increment
Simple incrementing function.
Counter for BCO object_id asignment.
"""

Prefix.objects.update(counter=F("counter") + 1)
count = prefix.counter
return count
3 changes: 2 additions & 1 deletion test.json
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,8 @@
"created": "2024-03-14T13:53:59Z",
"description": "Default prefix for all BioCompute Objects",
"owner": "AnonymousUser",
"authorized_groups": []
"authorized_groups": [],
"counter": 0
}
}
]
3 changes: 2 additions & 1 deletion tests/fixtures/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,8 @@
"created": "2024-03-14T13:53:59Z",
"description": "Default prefix for all BioCompute Objects",
"owner": "AnonymousUser",
"authorized_groups": []
"authorized_groups": [],
"counter": 0
}
}
]
Loading

0 comments on commit 301693a

Please sign in to comment.