1
1
import json
2
2
from json import JSONEncoder , JSONDecoder
3
3
4
- from octue .exceptions import InvalidManifestType , InvalidInput
4
+ from octue .exceptions import InvalidManifestType , InvalidInput , UnexpectedNumberOfResults , ManifestNotFound
5
5
from octue import utils
6
6
7
7
from .data_file import DataFile
@@ -36,21 +36,52 @@ def default(self, obj):
36
36
return json .JSONEncoder .default (self , obj )
37
37
38
38
39
+ class MultiManifest (object ):
40
+ """ A manifest that can contain multiple datasets
41
+ """
42
+ def __init__ (self , manifests ):
43
+ self .manifests = manifests
44
+
45
+ def from_dataset (self , method = 'name_icontains' , filter_value = None ):
46
+ # Search through the input list of files or by default all files in the manifest
47
+
48
+ for man in self .manifests :
49
+ print (man .data_set )
50
+ if method == 'name_icontains' and filter_value .lower () in man .data_set ['name' ].lower ():
51
+ return man
52
+ if method == 'name_contains' and filter_value in man .data_set ['name' ]:
53
+ return man
54
+ if method == 'name_endswith' and man .data_set ['name' ].endswith (filter_value ):
55
+ return man
56
+ if method == 'tag_exact' and filter_value in man .data_set ['tags' ]:
57
+ return man
58
+ if method == 'tag_startswith' :
59
+ for tag in man .data_set ['tags' ]:
60
+ if tag .startswith (filter_value ):
61
+ return man
62
+ if method == 'tag_endswith' :
63
+ for tag in man .data_set ['tags' ]:
64
+ if tag .endswith (filter_value ):
65
+ return man
66
+ # TODO turn DataSet dict into an SDK object
67
+ raise ManifestNotFound ('None of the datasets in the present manifest match this search criterion' )
68
+
69
+
39
70
class Manifest (object ):
40
- """ Manifest of files in one or more datasets
71
+ """ Manifest of files in a dataset
41
72
42
73
A manifest is used to read a list of files (and their associated properties) into octue analysis, or to compile a
43
74
list of output files (results) and their properties that will be sent back to the octue system.
44
75
45
76
"""
46
77
47
- uuid = None
48
- type = None
49
- files = None
50
-
51
78
def __init__ (self , ** kwargs ):
52
79
"""Construct a file Manifest
53
80
"""
81
+ self .uuid = None
82
+ self .type = None
83
+ self .files = None
84
+
54
85
self .__dict__ .update (** kwargs )
55
86
56
87
if self .type not in TYPE_CHOICES :
@@ -87,6 +118,9 @@ def append(self, **kwargs):
87
118
# Append a single file, constructed by passing the arguments through to DataFile()
88
119
self .files .append (DataFile (** kwargs ))
89
120
121
+ def get_dataset_manifest (self ):
122
+ return self
123
+
90
124
def get_files (self , method = 'name_icontains' , files = None , filter_value = None ):
91
125
""" Get a list of data files in a manifest whose name contains the input string
92
126
@@ -122,14 +156,12 @@ def get_files(self, method='name_icontains', files=None, filter_value=None):
122
156
results .append (file )
123
157
break
124
158
if method == 'in_sequence' :
125
- for tag in file .tags :
126
- if tag .startswith ('sequence' ):
127
- results .append (file )
128
- break
159
+ if file .sequence is not None :
160
+ results .append (file )
129
161
130
162
return results
131
163
132
- def get_file_sequence (self , filter_value = None , method = 'name_icontains' , files = None ):
164
+ def get_file_sequence (self , method = 'name_icontains' , filter_value = None , files = None ):
133
165
""" Get an ordered sequence of files matching a criterion
134
166
135
167
Accepts the same search arguments as `get_files`.
@@ -139,15 +171,16 @@ def get_file_sequence(self, filter_value=None, method='name_icontains', files=No
139
171
results = self .get_files (filter_value = filter_value , method = method , files = files )
140
172
results = self .get_files (method = 'in_sequence' , files = results )
141
173
142
- # Take second element for sort
143
174
def get_sequence_number (file ):
144
- for tag in file .tags :
145
- if tag .startswith ('sequence' ):
146
- sequence_number = int (tag .split (':' )[1 ])
175
+ return file .sequence
147
176
148
177
# Sort the results on ascending sequence number
149
178
results .sort (key = get_sequence_number )
150
179
180
+ # TODO check sequence is unique and sequential!!!
181
+ return results
182
+
183
+
151
184
def get_file_by_tag (self , tag_string ):
152
185
""" Gets a data file from a manifest by searching for files with the provided tag(s)\
153
186
@@ -186,11 +219,16 @@ def deserialise(json):
186
219
def as_data_file_list (json_object ):
187
220
files = []
188
221
if 'files' in json_object :
189
- files = [DataFile . deserialise ( data_file_json ) for data_file_json in json_object .pop ('files' )]
222
+ files = [DataFile ( ** data_file_dict ) for data_file_dict in json_object .pop ('files' )]
190
223
191
224
return {** json_object , 'files' : files }
192
-
193
- return Manifest (** JSONDecoder (object_hook = as_data_file_list ).decode (json ))
225
+ decoded = JSONDecoder (object_hook = as_data_file_list ).decode (json )
226
+
227
+ # Handle multi-manifest case
228
+ if 'manifests' in decoded :
229
+ return MultiManifest (manifests = [Manifest (** man ) for man in decoded ['manifests' ]])
230
+ else :
231
+ return Manifest (** decoded )
194
232
195
233
@staticmethod
196
234
def load (file_name = None ):
0 commit comments