Skip to content

Commit 3b064ce

Browse files
authored
Merge pull request #42 from biocompute-objects/fix-get-published-bcos
Fix URL fetch for BCO Published Objects
2 parents e705d7e + 9a4ff96 commit 3b064ce

File tree

8 files changed

+279
-134
lines changed

8 files changed

+279
-134
lines changed

bco_api/api/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class bco(models.Model):
9595
# Field is automatically generated.
9696
last_update = models.DateTimeField()
9797

98+
def __str__(self):
99+
"""String for representing the BCO model (in Admin site etc.)."""
100+
return self.object_id
101+
98102

99103
# Some additional information for Group.
100104
# This information is stored separately from

bco_api/api/scripts/method_specific/GET_activate_account.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ def GET_activate_account(username,temp_identifier):
3939
p_model_name='new_users',
4040
p_email=username,
4141
p_temp_identifier=temp_identifier
42-
):
42+
) == 1:
43+
4344

4445
# The credentials match, so activate the account.
4546
credential_try = db.activate_account(p_email=username)

bco_api/api/scripts/method_specific/GET_published_object_by_id.py

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,54 @@
55
from rest_framework import status
66
from rest_framework.response import Response
77

8+
# Below is helper code to deal with how we are allowing non standard versions (i.e. 1.2 instead of 1.2.0, etc).
9+
import re
10+
from semver import VersionInfo as Version
11+
from typing import Optional, Tuple
12+
13+
# TODO: This should be put into a universal place to grab from - also duplicated in POST_api_objects_drafts_token.py
14+
15+
BASEVERSION = re.compile(
16+
r"""[vV]?
17+
(?P<major>0|[1-9]\d*)
18+
(\.
19+
(?P<minor>0|[1-9]\d*)
20+
(\.
21+
(?P<patch>0|[1-9]\d*)
22+
)?
23+
)?
24+
""",
25+
re.VERBOSE,
26+
)
27+
28+
29+
def coerce(version: str) -> Tuple[Version, Optional[str]]:
30+
"""
31+
Convert an incomplete version string into a semver-compatible Version
32+
object
33+
34+
* Tries to detect a "basic" version string (``major.minor.patch``).
35+
* If not enough components can be found, missing components are
36+
set to zero to obtain a valid semver version.
37+
38+
:param str version: the version string to convert
39+
:return: a tuple with a :class:`Version` instance (or ``None``
40+
if it's not a version) and the rest of the string which doesn't
41+
belong to a basic version.
42+
:rtype: tuple(:class:`Version` | None, str)
43+
"""
44+
match = BASEVERSION.search(version)
45+
if not match:
46+
return (None, version)
47+
48+
ver = {
49+
key: 0 if value is None else value for key, value in match.groupdict().items()
50+
}
51+
ver = Version(**ver)
52+
rest = match.string[match.end() :] # noqa:E203
53+
return ver, rest
54+
55+
856
def GET_published_object_by_id(oi_root):
957
"""
1058
Get a published object given a root.
@@ -22,59 +70,49 @@ def GET_published_object_by_id(oi_root):
2270
slash).
2371
2472
"""
25-
oi_root = oi_root.split("_")[0] + '{:06d}'.format(int(oi_root.split("_")[1]))
73+
74+
# Note: This is not needed - removing out the underscore breaks the regex below, leaving in for the moment
75+
# since I'm not sure why it was ever added (maybe there is a reason?)
76+
# oi_root = oi_root.split("_")[0] + '{:06d}'.format(int(oi_root.split("_")[1]))
2677
all_versions = list(
27-
bco.objects.filter(
28-
object_id__regex = rf'(.*?)/{oi_root}/',
29-
state = 'PUBLISHED'
30-
).values_list(
31-
'object_id',
32-
flat = True
33-
)
34-
)
78+
bco.objects.filter(
79+
object_id__regex=rf'(.*?)/{oi_root}/',
80+
state='PUBLISHED'
81+
).values_list(
82+
'object_id',
83+
flat=True
84+
)
85+
)
3586

3687
# Get the latest version for this object if we have any.
3788
if len(all_versions) > 0:
38-
89+
3990
# There was at least one version of the root ID,
4091
# so now perform some logic based on whether or
4192
# not a version was also passed.
42-
93+
4394
# First find the latest version of the object.
44-
latest_major = 0
45-
latest_minor = 0
46-
47-
latest_version = [
48-
i.split('/')[-1:][0] for i in all_versions
49-
]
50-
51-
for i in latest_version:
52-
53-
major_minor_split = i.split('.')
54-
55-
if int(major_minor_split[0]) >= latest_major:
56-
if int(major_minor_split[1]) >= latest_minor:
57-
latest_major = int(major_minor_split[0])
58-
latest_minor = int(major_minor_split[1])
59-
95+
latest_version = [i.split('/')[-1:][0] for i in all_versions]
96+
l_version, _ = coerce(max(latest_version, key=coerce))
97+
6098
# Kick back the latest version.
6199
return Response(
62-
data = bco.objects.filter(
63-
object_id__regex = rf'{oi_root}/{latest_major}.{latest_minor}',
64-
state = 'PUBLISHED'
65-
).values_list(
66-
'contents',
67-
flat = True
68-
),
69-
status = status.HTTP_200_OK
70-
)
100+
data=bco.objects.filter(
101+
object_id__regex=rf'{oi_root}/{l_version.major}.{l_version.minor}?.?{l_version.patch}',
102+
state='PUBLISHED'
103+
).values_list(
104+
'contents',
105+
flat=True
106+
),
107+
status=status.HTTP_200_OK
108+
)
71109

72110
else:
73111

74112
# If all_versions has 0 length, then the
75113
# the root ID does not exist at all.
76114
print('No objects were found for the root ID provided.')
77115
return Response(
78-
data = 'No objects were found for the root ID provided.',
79-
status = status.HTTP_400_BAD_REQUEST
80-
)
116+
data='No objects were found for the root ID provided.',
117+
status=status.HTTP_400_BAD_REQUEST
118+
)

bco_api/api/scripts/method_specific/GET_published_object_by_id_with_version.py

Lines changed: 80 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,77 @@
55
from rest_framework import status
66
from rest_framework.response import Response
77

8+
89
def GET_published_object_by_id_with_version(oi_root, oi_version):
910
"""
10-
11+
Fetch a published BCO by the PREFIX, BCO NAME, and VERSION ID
1112
"""
1213

14+
####
15+
# We are assuming the oi_root looks something like this
16+
# BCO_28
17+
# Where
18+
# `BCO` is the prefix
19+
# and `28` is the object name
20+
####
21+
22+
# Split by '_'
23+
underscores = oi_root.count("_")
24+
if underscores < 1:
25+
# ERROR - there should be an underscore separating the prefix and the bco name
26+
return Response(
27+
data='This API requires that the prefix and the BCO name be separated by an underscore \'_\' in the object_id_root PATH variable.',
28+
status=status.HTTP_400_BAD_REQUEST
29+
)
30+
31+
# TODO: This allows BCO Names to support underscores - not sure if that is valid though
32+
# This can be 'fixed' by adding in a check for > 1 above
33+
# Might be a better idea to split prefix, bco name, and version into a three part get
34+
bco_prefix, bco_name = oi_root.split("_", maxsplit=1)
35+
36+
# retrieved = list(
37+
# bco.objects.filter(
38+
# # contents__search=bco_name,
39+
# prefix=bco_prefix,
40+
# contents__provenance_domain__name=bco_name,
41+
# contents__provenance_domain__version=oi_version,
42+
# state='PUBLISHED'
43+
# ).values_list(
44+
# 'contents',
45+
# flat=True
46+
# )
47+
# )
1348
# The object ID either exists or it does not.
1449
retrieved = list(
15-
bco.objects.filter(
16-
object_id__regex = rf'(.*?)/{oi_root}/{oi_version}',
17-
state = 'PUBLISHED'
18-
).values_list(
19-
'contents',
20-
flat = True
21-
)
22-
)
50+
bco.objects.filter(
51+
object_id__regex=rf'(.*?)/{oi_root}/{oi_version}',
52+
state='PUBLISHED'
53+
).values_list(
54+
'contents',
55+
flat=True
56+
)
57+
)
2358
# Was the object found?
2459
if len(retrieved) > 0:
25-
2660
# Kick it back.
27-
return Response(
28-
data = retrieved,
29-
status = status.HTTP_200_OK
30-
)
31-
61+
return Response(data=retrieved, status=status.HTTP_200_OK)
3262
else:
33-
3463
# If all_versions has 0 length, then the
3564
# the root ID does not exist at all.
3665
print('No objects were found for the root ID and version provided.')
3766
return Response(
38-
data = 'No objects were found for the root ID and version provided.',
39-
status = status.HTTP_400_BAD_REQUEST
40-
)
41-
42-
43-
44-
45-
46-
47-
48-
49-
50-
51-
52-
53-
54-
55-
56-
57-
58-
67+
data='No objects were found for the root ID and version provided.',
68+
status=status.HTTP_400_BAD_REQUEST
69+
)
5970

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

6376
# First, get the table based on the requested published object.
6477
table_name = (
65-
oi_root.split('_')[0] + '_publish'
78+
oi_root.split('_')[0] + '_publish'
6679
).lower()
6780

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

7487
# Construct the object ID.
7588
constructed = object_id = settings.PUBLIC_HOSTNAME + '/' + oi_root + '/' + oi_version
76-
89+
7790
# Does the object exist in the table?
7891
if apps.get_model(
79-
app_label = 'api',
80-
model_name = table_name
81-
).objects.filter(
82-
object_id = constructed
83-
).exists():
92+
app_label='api',
93+
model_name=table_name
94+
).objects.filter(
95+
object_id=constructed
96+
).exists():
8497

8598
# Get the object, then check the permissions.
8699
objected = apps.get_model(
87-
app_label = 'api',
88-
model_name = table_name
89-
).objects.get(
90-
object_id = constructed
91-
)
92-
100+
app_label='api',
101+
model_name=table_name
102+
).objects.get(
103+
object_id=constructed
104+
)
105+
93106
return Response(
94-
data = serializers.serialize(
95-
'json',
96-
[ objected, ]
97-
),
98-
status = status.HTTP_200_OK
99-
)
100-
107+
data=serializers.serialize(
108+
'json',
109+
[objected, ]
110+
),
111+
status=status.HTTP_200_OK
112+
)
113+
101114
else:
102115

103-
return(
104-
Response(
105-
status = status.HTTP_400_BAD_REQUEST
106-
)
116+
return (
117+
Response(
118+
status=status.HTTP_400_BAD_REQUEST
119+
)
107120
)
108-
121+
109122
else:
110123

111-
return(
112-
Response(
113-
status = status.HTTP_400_BAD_REQUEST
114-
)
115-
)
124+
return (
125+
Response(
126+
status=status.HTTP_400_BAD_REQUEST
127+
)
128+
)

0 commit comments

Comments
 (0)