ONE-api 3.1.1__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {one_api-3.1.1 → one_api-3.2.0/ONE_api.egg-info}/PKG-INFO +1 -1
- {one_api-3.1.1/ONE_api.egg-info → one_api-3.2.0}/PKG-INFO +1 -1
- {one_api-3.1.1 → one_api-3.2.0}/one/__init__.py +1 -1
- {one_api-3.1.1 → one_api-3.2.0}/one/alf/path.py +210 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/api.py +2 -2
- {one_api-3.1.1 → one_api-3.2.0}/one/webclient.py +1 -1
- {one_api-3.1.1 → one_api-3.2.0}/LICENSE +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/MANIFEST.in +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/ONE_api.egg-info/SOURCES.txt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/ONE_api.egg-info/dependency_links.txt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/ONE_api.egg-info/requires.txt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/ONE_api.egg-info/top_level.txt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/README.md +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/alf/__init__.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/alf/cache.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/alf/exceptions.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/alf/io.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/alf/spec.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/converters.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/params.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/registration.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/remote/__init__.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/remote/aws.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/remote/base.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/remote/globus.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/datasets.pqt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/params/.caches +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/params/.test.alyx.internationalbrainlab.org +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/1f187d80fd59677b395fcdb18e68e4401bfa1cc9 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/3f51aa2e0baa42438467906f56a457c91a221898 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/47893cf67c985e6361cdee009334963f49fb0746 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/535d0e9a1e2c1efbdeba0d673b131e00361a2edb +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/5618bea3484a52cd893616f07903f0e49e023ba1 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/6dc96f7e9bcc6ac2e7581489b9580a6cd3f28293 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/db1731fb8df0208944ae85f76718430813a8bf50 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/dcce48259bb929661f60a02a48563f70aa6185b3 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/rest_responses/f530d6022f61cdc9e38cc66beb3cb71f3003c9a1 +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/sessions.pqt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/test_dbs.json +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/test_img.png +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/one/util.py +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/pyproject.toml +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/requirements.txt +0 -0
- {one_api-3.1.1 → one_api-3.2.0}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""The Open Neurophysiology Environment (ONE) API."""
|
|
2
|
-
__version__ = '3.
|
|
2
|
+
__version__ = '3.2.0'
|
|
@@ -859,6 +859,36 @@ class PureALFPath(pathlib.PurePath): # py3.12 supports direct subclassing
|
|
|
859
859
|
"""tuple of str: the full ALF path parts, with empty strings for missing parts."""
|
|
860
860
|
return tuple(p or '' for p in self.parse_alf_path(as_dict=False))
|
|
861
861
|
|
|
862
|
+
@property
|
|
863
|
+
def lab(self):
|
|
864
|
+
"""str: The lab part of the ALF path, or an empty str if not present."""
|
|
865
|
+
return self.session_parts[0]
|
|
866
|
+
|
|
867
|
+
@property
|
|
868
|
+
def subject(self):
|
|
869
|
+
"""str: The subject part of the ALF path, or an empty str if not present."""
|
|
870
|
+
return self.session_parts[1]
|
|
871
|
+
|
|
872
|
+
@property
|
|
873
|
+
def date(self):
|
|
874
|
+
"""str: The date part of the ALF path, or an empty str if not present."""
|
|
875
|
+
return self.session_parts[2]
|
|
876
|
+
|
|
877
|
+
@property
|
|
878
|
+
def sequence(self):
|
|
879
|
+
"""str: The number part of the ALF path, or an empty str if not present."""
|
|
880
|
+
return self.session_parts[3]
|
|
881
|
+
|
|
882
|
+
@property
|
|
883
|
+
def collection(self):
|
|
884
|
+
"""str: The collection part of the ALF path, or an empty str if not present."""
|
|
885
|
+
return self.alf_parts[4]
|
|
886
|
+
|
|
887
|
+
@property
|
|
888
|
+
def revision(self):
|
|
889
|
+
"""str: The revision part of the ALF path, or an empty str if not present."""
|
|
890
|
+
return self.alf_parts[5]
|
|
891
|
+
|
|
862
892
|
@property
|
|
863
893
|
def namespace(self):
|
|
864
894
|
"""str: The namespace part of the ALF name, or and empty str if not present."""
|
|
@@ -884,6 +914,134 @@ class PureALFPath(pathlib.PurePath): # py3.12 supports direct subclassing
|
|
|
884
914
|
"""str: The extra part of the ALF name, or and empty str if not present."""
|
|
885
915
|
return self.dataset_name_parts[4]
|
|
886
916
|
|
|
917
|
+
def with_lab(self, lab, strict=False):
|
|
918
|
+
"""Return a new path with the ALF lab changed.
|
|
919
|
+
|
|
920
|
+
Parameters
|
|
921
|
+
----------
|
|
922
|
+
lab : str
|
|
923
|
+
An ALF lab name part to use.
|
|
924
|
+
strict : bool, optional
|
|
925
|
+
If True, the lab part must be present in the path, otherwise the lab/Subjects/ part
|
|
926
|
+
is added if not present.
|
|
927
|
+
|
|
928
|
+
Returns
|
|
929
|
+
-------
|
|
930
|
+
PureALFPath
|
|
931
|
+
The same file path but with the lab part replaced with the input.
|
|
932
|
+
|
|
933
|
+
Raises
|
|
934
|
+
------
|
|
935
|
+
ValueError
|
|
936
|
+
The lab name is invalid.
|
|
937
|
+
ALFInvalid
|
|
938
|
+
The path is not a valid ALF session path, or the lab part is not present in the path
|
|
939
|
+
when strict is True.
|
|
940
|
+
|
|
941
|
+
"""
|
|
942
|
+
if not (lab and spec.regex('^{lab}$').match(lab)):
|
|
943
|
+
raise ValueError(f'Invalid lab name: {lab}')
|
|
944
|
+
if not self.subject or (strict and not self.lab): # FIXME check logic
|
|
945
|
+
raise ALFInvalid(str(self))
|
|
946
|
+
|
|
947
|
+
pattern = spec.regex(SESSION_SPEC)
|
|
948
|
+
repl = fr'{lab}/Subjects/\g<subject>/\g<date>/\g<number>'
|
|
949
|
+
return self.__class__(pattern.sub(repl, self.as_posix(), count=1))
|
|
950
|
+
|
|
951
|
+
def with_subject(self, subject):
|
|
952
|
+
"""Return a new path with the ALF subject changed.
|
|
953
|
+
|
|
954
|
+
Parameters
|
|
955
|
+
----------
|
|
956
|
+
subject : str
|
|
957
|
+
An ALF subject name part to use.
|
|
958
|
+
|
|
959
|
+
Returns
|
|
960
|
+
-------
|
|
961
|
+
PureALFPath
|
|
962
|
+
The same file path but with the subject part replaced with the input.
|
|
963
|
+
|
|
964
|
+
Raises
|
|
965
|
+
------
|
|
966
|
+
ValueError
|
|
967
|
+
The subject name is invalid.
|
|
968
|
+
ALFInvalid
|
|
969
|
+
The path is not a valid ALF session path.
|
|
970
|
+
|
|
971
|
+
"""
|
|
972
|
+
if not (subject and spec.regex('^{subject}$').match(subject)):
|
|
973
|
+
raise ValueError(f'Invalid subject name: {subject}')
|
|
974
|
+
if not self.subject:
|
|
975
|
+
raise ALFInvalid(str(self))
|
|
976
|
+
|
|
977
|
+
pattern = spec.regex('{subject}/{date}/{number}')
|
|
978
|
+
repl = fr'{subject}/\g<date>/\g<number>'
|
|
979
|
+
return self.__class__(pattern.sub(repl, self.as_posix()), count=1)
|
|
980
|
+
|
|
981
|
+
def with_date(self, date):
|
|
982
|
+
"""Return a new path with the ALF date changed.
|
|
983
|
+
|
|
984
|
+
Parameters
|
|
985
|
+
----------
|
|
986
|
+
date : str, datetime.datetime, datetime.date
|
|
987
|
+
An ALF date part to use, in YYYY-MM-DD format.
|
|
988
|
+
|
|
989
|
+
Returns
|
|
990
|
+
-------
|
|
991
|
+
PureALFPath
|
|
992
|
+
The same file path but with the date part replaced with the input.
|
|
993
|
+
|
|
994
|
+
Raises
|
|
995
|
+
------
|
|
996
|
+
ValueError
|
|
997
|
+
The date is not in YYYY-MM-DD format.
|
|
998
|
+
ALFInvalid
|
|
999
|
+
The path is not a valid ALF session path.
|
|
1000
|
+
|
|
1001
|
+
"""
|
|
1002
|
+
if date and not isinstance(date, str):
|
|
1003
|
+
date = str(date)[:10]
|
|
1004
|
+
if not (date and spec.regex('^{date}$').match(date)):
|
|
1005
|
+
raise ValueError(f'Invalid date: {date}')
|
|
1006
|
+
if not self.date:
|
|
1007
|
+
raise ALFInvalid(str(self))
|
|
1008
|
+
|
|
1009
|
+
pattern = spec.regex('{subject}/{date}/{number}')
|
|
1010
|
+
repl = fr'\g<subject>/{date}/\g<number>'
|
|
1011
|
+
return self.__class__(pattern.sub(repl, self.as_posix()), count=1)
|
|
1012
|
+
|
|
1013
|
+
def with_sequence(self, number):
|
|
1014
|
+
"""Return a new path with the ALF number changed.
|
|
1015
|
+
|
|
1016
|
+
Parameters
|
|
1017
|
+
----------
|
|
1018
|
+
number : str, int
|
|
1019
|
+
An ALF number part to use, as a string or integer.
|
|
1020
|
+
|
|
1021
|
+
Returns
|
|
1022
|
+
-------
|
|
1023
|
+
PureALFPath
|
|
1024
|
+
The same file path but with the number part replaced with the input.
|
|
1025
|
+
|
|
1026
|
+
Raises
|
|
1027
|
+
------
|
|
1028
|
+
ValueError
|
|
1029
|
+
The number is not a valid ALF number.
|
|
1030
|
+
ALFInvalid
|
|
1031
|
+
The path is not a valid ALF session path.
|
|
1032
|
+
|
|
1033
|
+
"""
|
|
1034
|
+
if isinstance(number, str):
|
|
1035
|
+
number = int(number.strip())
|
|
1036
|
+
if number is None or not spec.regex('^{number}$').match(str(number)):
|
|
1037
|
+
raise ValueError(f'Invalid number: {number}')
|
|
1038
|
+
if not self.sequence:
|
|
1039
|
+
raise ALFInvalid(str(self))
|
|
1040
|
+
|
|
1041
|
+
pattern = spec.regex('{subject}/{date}/{number}')
|
|
1042
|
+
repl = fr'\g<subject>/\g<date>/{number:03d}'
|
|
1043
|
+
return self.__class__(pattern.sub(repl, self.as_posix()), count=1)
|
|
1044
|
+
|
|
887
1045
|
def with_object(self, obj):
|
|
888
1046
|
"""Return a new path with the ALF object changed.
|
|
889
1047
|
|
|
@@ -1084,6 +1242,58 @@ class PureALFPath(pathlib.PurePath): # py3.12 supports direct subclassing
|
|
|
1084
1242
|
"""
|
|
1085
1243
|
return padded_sequence(path)
|
|
1086
1244
|
|
|
1245
|
+
def with_collection(self, collection):
|
|
1246
|
+
"""Return a new path with the ALF collection part added/changed.
|
|
1247
|
+
|
|
1248
|
+
NB: The ALFPath must include the session parts (subject/date/number) for this to work.
|
|
1249
|
+
|
|
1250
|
+
Parameters
|
|
1251
|
+
----------
|
|
1252
|
+
collection : str
|
|
1253
|
+
An ALF collection part to use.
|
|
1254
|
+
|
|
1255
|
+
Returns
|
|
1256
|
+
-------
|
|
1257
|
+
PureALFPath
|
|
1258
|
+
The same file path but with the collection part added or replaced with the input.
|
|
1259
|
+
|
|
1260
|
+
Raises
|
|
1261
|
+
------
|
|
1262
|
+
ValueError
|
|
1263
|
+
The collection name is invalid.
|
|
1264
|
+
The path does not include the session parts (subject/date/number).
|
|
1265
|
+
ALFInvalid
|
|
1266
|
+
The path is not a valid ALF session path.
|
|
1267
|
+
|
|
1268
|
+
"""
|
|
1269
|
+
collection = pathlib.PurePath(collection or '').as_posix().strip('/')
|
|
1270
|
+
if not (collection and spec.regex('^{collection}$').match(collection)):
|
|
1271
|
+
raise ValueError(f'Invalid collection name: {collection}')
|
|
1272
|
+
# Check path contains session parts
|
|
1273
|
+
if not self.session_path():
|
|
1274
|
+
raise ValueError(
|
|
1275
|
+
'Cannot add collection to a path without session parts, e.g. subject/date/number'
|
|
1276
|
+
)
|
|
1277
|
+
# If the path is a session path, simply append the collection to it
|
|
1278
|
+
if self.is_session_path():
|
|
1279
|
+
return self.joinpath(collection)
|
|
1280
|
+
# Otherwise substitute the collection with regex
|
|
1281
|
+
string = self.as_posix()
|
|
1282
|
+
if not self.is_dataset():
|
|
1283
|
+
cpat = spec.regex(f'^{COLLECTION_SPEC}$')
|
|
1284
|
+
if cpat.match(self.relative_to_session().as_posix() + '/'):
|
|
1285
|
+
string += '/' # ensure trailing slash for matching folder paths
|
|
1286
|
+
else:
|
|
1287
|
+
raise ALFInvalid(str(self))
|
|
1288
|
+
# Replace the collection part in the path
|
|
1289
|
+
# NB: We don't use SESSION_SPEC here to avoid handling optional lab part
|
|
1290
|
+
pattern = spec.regex('{subject}/{date}/{number}/' + COLLECTION_SPEC)
|
|
1291
|
+
match = pattern.search(string)
|
|
1292
|
+
repl = fr'\g<subject>/\g<date>/\g<number>/{collection}/'
|
|
1293
|
+
if match.groupdict()['revision']:
|
|
1294
|
+
repl += r'#\g<revision>#/'
|
|
1295
|
+
return self.__class__(pattern.sub(repl, string), count=1)
|
|
1296
|
+
|
|
1087
1297
|
def with_revision(self, revision):
|
|
1088
1298
|
"""Return a new path with the ALF revision part added/changed.
|
|
1089
1299
|
|
|
@@ -39,7 +39,7 @@ from one import util
|
|
|
39
39
|
|
|
40
40
|
_logger = logging.getLogger(__name__)
|
|
41
41
|
__all__ = ['ONE', 'One', 'OneAlyx']
|
|
42
|
-
SAVE_ON_DELETE = (os.environ.get('ONE_SAVE_ON_DELETE') or '
|
|
42
|
+
SAVE_ON_DELETE = (os.environ.get('ONE_SAVE_ON_DELETE') or '0').casefold() in ('true', '1')
|
|
43
43
|
"""bool: Whether to save modified cache tables on delete."""
|
|
44
44
|
|
|
45
45
|
_logger.debug('ONE_SAVE_ON_DELETE: %s', SAVE_ON_DELETE)
|
|
@@ -221,7 +221,7 @@ class One(ConversionMixin):
|
|
|
221
221
|
created = created.isoformat(sep=' ', timespec='minutes')
|
|
222
222
|
meta['raw'][table]['date_created'] = created
|
|
223
223
|
|
|
224
|
-
with FileLock(save_dir, log=_logger, timeout=TIMEOUT, timeout_action='delete'):
|
|
224
|
+
with FileLock(save_dir / '.ONE', log=_logger, timeout=TIMEOUT, timeout_action='delete'):
|
|
225
225
|
_logger.info('Saving cache tables...')
|
|
226
226
|
for table in filter(lambda x: not x[0] == '_', caches.keys()):
|
|
227
227
|
metadata = meta['raw'].get(table, {})
|
|
@@ -77,7 +77,7 @@ class _JSONEncoder(json.JSONEncoder):
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
def _cache_response(method):
|
|
80
|
-
"""Decorator for the generic request method for caching REST
|
|
80
|
+
"""Decorator for the generic request method for caching REST responses.
|
|
81
81
|
|
|
82
82
|
Caches the result of the query and on subsequent calls, returns cache instead of hitting the
|
|
83
83
|
database.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{one_api-3.1.1 → one_api-3.2.0}/one/tests/fixtures/params/.test.alyx.internationalbrainlab.org
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|