Skip to content

Commit

Permalink
Merge pull request #42 from biocompute-objects/fix-get-published-bcos
Browse files Browse the repository at this point in the history
Fix URL fetch for BCO Published Objects
  • Loading branch information
HadleyKing authored Dec 13, 2021
2 parents e705d7e + 9a4ff96 commit 3b064ce
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 134 deletions.
4 changes: 4 additions & 0 deletions bco_api/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ class bco(models.Model):
# Field is automatically generated.
last_update = models.DateTimeField()

def __str__(self):
"""String for representing the BCO model (in Admin site etc.)."""
return self.object_id


# Some additional information for Group.
# This information is stored separately from
Expand Down
3 changes: 2 additions & 1 deletion bco_api/api/scripts/method_specific/GET_activate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def GET_activate_account(username,temp_identifier):
p_model_name='new_users',
p_email=username,
p_temp_identifier=temp_identifier
):
) == 1:


# The credentials match, so activate the account.
credential_try = db.activate_account(p_email=username)
Expand Down
116 changes: 77 additions & 39 deletions bco_api/api/scripts/method_specific/GET_published_object_by_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,54 @@
from rest_framework import status
from rest_framework.response import Response

# Below is helper code to deal with how we are allowing non standard versions (i.e. 1.2 instead of 1.2.0, etc).
import re
from semver import VersionInfo as Version
from typing import Optional, Tuple

# TODO: This should be put into a universal place to grab from - also duplicated in POST_api_objects_drafts_token.py

BASEVERSION = re.compile(
r"""[vV]?
(?P<major>0|[1-9]\d*)
(\.
(?P<minor>0|[1-9]\d*)
(\.
(?P<patch>0|[1-9]\d*)
)?
)?
""",
re.VERBOSE,
)


def coerce(version: str) -> Tuple[Version, Optional[str]]:
"""
Convert an incomplete version string into a semver-compatible Version
object
* Tries to detect a "basic" version string (``major.minor.patch``).
* If not enough components can be found, missing components are
set to zero to obtain a valid semver version.
:param str version: the version string to convert
:return: a tuple with a :class:`Version` instance (or ``None``
if it's not a version) and the rest of the string which doesn't
belong to a basic version.
:rtype: tuple(:class:`Version` | None, str)
"""
match = BASEVERSION.search(version)
if not match:
return (None, version)

ver = {
key: 0 if value is None else value for key, value in match.groupdict().items()
}
ver = Version(**ver)
rest = match.string[match.end() :] # noqa:E203
return ver, rest


def GET_published_object_by_id(oi_root):
"""
Get a published object given a root.
Expand All @@ -22,59 +70,49 @@ def GET_published_object_by_id(oi_root):
slash).
"""
oi_root = oi_root.split("_")[0] + '{:06d}'.format(int(oi_root.split("_")[1]))

# Note: This is not needed - removing out the underscore breaks the regex below, leaving in for the moment
# since I'm not sure why it was ever added (maybe there is a reason?)
# oi_root = oi_root.split("_")[0] + '{:06d}'.format(int(oi_root.split("_")[1]))
all_versions = list(
bco.objects.filter(
object_id__regex = rf'(.*?)/{oi_root}/',
state = 'PUBLISHED'
).values_list(
'object_id',
flat = True
)
)
bco.objects.filter(
object_id__regex=rf'(.*?)/{oi_root}/',
state='PUBLISHED'
).values_list(
'object_id',
flat=True
)
)

# Get the latest version for this object if we have any.
if len(all_versions) > 0:

# There was at least one version of the root ID,
# so now perform some logic based on whether or
# not a version was also passed.

# First find the latest version of the object.
latest_major = 0
latest_minor = 0

latest_version = [
i.split('/')[-1:][0] for i in all_versions
]

for i in latest_version:

major_minor_split = i.split('.')

if int(major_minor_split[0]) >= latest_major:
if int(major_minor_split[1]) >= latest_minor:
latest_major = int(major_minor_split[0])
latest_minor = int(major_minor_split[1])

latest_version = [i.split('/')[-1:][0] for i in all_versions]
l_version, _ = coerce(max(latest_version, key=coerce))

# Kick back the latest version.
return Response(
data = bco.objects.filter(
object_id__regex = rf'{oi_root}/{latest_major}.{latest_minor}',
state = 'PUBLISHED'
).values_list(
'contents',
flat = True
),
status = status.HTTP_200_OK
)
data=bco.objects.filter(
object_id__regex=rf'{oi_root}/{l_version.major}.{l_version.minor}?.?{l_version.patch}',
state='PUBLISHED'
).values_list(
'contents',
flat=True
),
status=status.HTTP_200_OK
)

else:

# If all_versions has 0 length, then the
# the root ID does not exist at all.
print('No objects were found for the root ID provided.')
return Response(
data = 'No objects were found for the root ID provided.',
status = status.HTTP_400_BAD_REQUEST
)
data='No objects were found for the root ID provided.',
status=status.HTTP_400_BAD_REQUEST
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,77 @@
from rest_framework import status
from rest_framework.response import Response


def GET_published_object_by_id_with_version(oi_root, oi_version):
"""
Fetch a published BCO by the PREFIX, BCO NAME, and VERSION ID
"""

####
# We are assuming the oi_root looks something like this
# BCO_28
# Where
# `BCO` is the prefix
# and `28` is the object name
####

# Split by '_'
underscores = oi_root.count("_")
if underscores < 1:
# ERROR - there should be an underscore separating the prefix and the bco name
return Response(
data='This API requires that the prefix and the BCO name be separated by an underscore \'_\' in the object_id_root PATH variable.',
status=status.HTTP_400_BAD_REQUEST
)

# TODO: This allows BCO Names to support underscores - not sure if that is valid though
# This can be 'fixed' by adding in a check for > 1 above
# Might be a better idea to split prefix, bco name, and version into a three part get
bco_prefix, bco_name = oi_root.split("_", maxsplit=1)

# retrieved = list(
# bco.objects.filter(
# # contents__search=bco_name,
# prefix=bco_prefix,
# contents__provenance_domain__name=bco_name,
# contents__provenance_domain__version=oi_version,
# state='PUBLISHED'
# ).values_list(
# 'contents',
# flat=True
# )
# )
# The object ID either exists or it does not.
retrieved = list(
bco.objects.filter(
object_id__regex = rf'(.*?)/{oi_root}/{oi_version}',
state = 'PUBLISHED'
).values_list(
'contents',
flat = True
)
)
bco.objects.filter(
object_id__regex=rf'(.*?)/{oi_root}/{oi_version}',
state='PUBLISHED'
).values_list(
'contents',
flat=True
)
)
# Was the object found?
if len(retrieved) > 0:

# Kick it back.
return Response(
data = retrieved,
status = status.HTTP_200_OK
)

return Response(data=retrieved, status=status.HTTP_200_OK)
else:

# If all_versions has 0 length, then the
# the root ID does not exist at all.
print('No objects were found for the root ID and version provided.')
return Response(
data = 'No objects were found for the root ID and version provided.',
status = status.HTTP_400_BAD_REQUEST
)


















data='No objects were found for the root ID and version provided.',
status=status.HTTP_400_BAD_REQUEST
)

# TODO: This code from here on down appears to be unreachable? The above if/else will always return the request
# Maybe this is placeholder code for something?
# Instantiate any necessary imports.
db = DbUtils.DbUtils()

# First, get the table based on the requested published object.
table_name = (
oi_root.split('_')[0] + '_publish'
oi_root.split('_')[0] + '_publish'
).lower()

# Does the table exist?
Expand All @@ -73,43 +86,43 @@ def GET_published_object_by_id_with_version(oi_root, oi_version):

# Construct the object ID.
constructed = object_id = settings.PUBLIC_HOSTNAME + '/' + oi_root + '/' + oi_version

# Does the object exist in the table?
if apps.get_model(
app_label = 'api',
model_name = table_name
).objects.filter(
object_id = constructed
).exists():
app_label='api',
model_name=table_name
).objects.filter(
object_id=constructed
).exists():

# Get the object, then check the permissions.
objected = apps.get_model(
app_label = 'api',
model_name = table_name
).objects.get(
object_id = constructed
)
app_label='api',
model_name=table_name
).objects.get(
object_id=constructed
)

return Response(
data = serializers.serialize(
'json',
[ objected, ]
),
status = status.HTTP_200_OK
)
data=serializers.serialize(
'json',
[objected, ]
),
status=status.HTTP_200_OK
)

else:

return(
Response(
status = status.HTTP_400_BAD_REQUEST
)
return (
Response(
status=status.HTTP_400_BAD_REQUEST
)
)

else:

return(
Response(
status = status.HTTP_400_BAD_REQUEST
)
)
return (
Response(
status=status.HTTP_400_BAD_REQUEST
)
)
Loading

0 comments on commit 3b064ce

Please sign in to comment.