Skip to content

Commit b41fb3d

Browse files
committed
Added handling of parameters in module creator code
1 parent 1bf7cbc commit b41fb3d

File tree

1 file changed

+177
-12
lines changed

1 file changed

+177
-12
lines changed

gp/modules.py

Lines changed: 177 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,32 @@ def validate(self):
8888
raise ValueError(attr + " is not set")
8989

9090
# Validate name
91-
invalid_chars = set(string.punctuation.replace("_", "").replace(".", ""))
91+
invalid_chars = GPTaskSpec.invalid_chars()
9292
if any(char in invalid_chars for char in self.name):
9393
raise ValueError("module name includes invalid characters: " + self.name)
9494

9595
# Validate LSID
9696
self._valid_lsid()
9797

9898
# Validate categories
99-
if not self._all_strings(self.categories):
99+
if not self.all_strings(self.categories):
100100
raise TypeError("categories contains non-string value: " + str(self.categories))
101101

102102
# Validate file formats
103-
if not self._all_strings(self.file_format):
103+
if not self.all_strings(self.file_format):
104104
raise TypeError("file_format contains non-string value: " + str(self.file_format))
105105

106106
# Validate support files
107-
if not self._all_strings(self.support_files):
107+
if not self.all_strings(self.support_files):
108108
raise TypeError("support_files contains non-string value: " + str(self.support_files))
109109

110-
# Validate parameters
111-
# TODO: Implement
110+
# Validate parameter list
111+
if not self._all_params(self.parameters):
112+
raise TypeError("parameters contains non-GPParamSpec value: " + str(self.parameters))
113+
114+
# Validate individual parameters
115+
for param in self.parameters:
116+
param.validate()
112117

113118
# Return that everything validates
114119
return True
@@ -164,7 +169,7 @@ def write_manifest(self, module_directory=""):
164169

165170
# Write initial attributes
166171
manifest_file.write("JVMLevel=\n")
167-
manifest_file.write("LSID=" + self._escape_colons(self.lsid) + "\n")
172+
manifest_file.write("LSID=" + self.manifest_escape(self.lsid) + "\n")
168173
manifest_file.write("author=" + self._author_line() + "\n")
169174
manifest_file.write("categories=" + ';'.join(self.categories) + "\n")
170175
manifest_file.write("commandLine=" + self.command_line + "\n")
@@ -177,7 +182,8 @@ def write_manifest(self, module_directory=""):
177182
manifest_file.write("os=" + str(self.os.value) + "\n")
178183

179184
# Write parameter attributes
180-
# TODO: Implement
185+
for index, param in enumerate(self.parameters):
186+
manifest_file.write(param.manifest_repr(index+1))
181187

182188
# Write footer attributes
183189
manifest_file.write("privacy=" + str(self.privacy.value) + "\n")
@@ -222,20 +228,26 @@ def _task_type(self):
222228
return ""
223229

224230
@staticmethod
225-
def _escape_colons(string):
231+
def manifest_escape(string):
226232
"""
227-
Escape colon characters for inclusion in manifest file
233+
Escape colon and equals characters for inclusion in manifest file
228234
:param string:
229235
:return: string
230236
"""
231-
return string.replace(':', '\:')
237+
return string.replace(':', '\:').replace('=', '\=')
232238

233239
@staticmethod
234-
def _all_strings(arr):
240+
def all_strings(arr):
235241
if not isinstance([], list):
236242
raise TypeError("non-list value found where list is expected")
237243
return all(isinstance(x, str) for x in arr)
238244

245+
@staticmethod
246+
def _all_params(arr):
247+
if not isinstance([], list):
248+
raise TypeError("non-list value found for parameters")
249+
return all(isinstance(x, GPParamSpec) for x in arr)
250+
239251
def _valid_lsid(self):
240252
if not isinstance(self.lsid, str):
241253
raise TypeError("lsid is not a string, string expected: " + str(self.lsid))
@@ -245,3 +257,156 @@ def _valid_lsid(self):
245257

246258
if self.lsid.split(':')[0].lower() != 'urn':
247259
raise ValueError("lsid does not begin with urn: " + str(self.lsid))
260+
261+
@staticmethod
262+
def invalid_chars():
263+
"""
264+
Returns a set of characters which are not valid in module or parameter names
265+
:return:
266+
"""
267+
return set(string.punctuation.replace("_", "").replace(".", "") + string.whitespace)
268+
269+
270+
class Type(Enum):
271+
FILE = "FILE"
272+
TEXT = "TEXT"
273+
INTEGER = "Integer"
274+
FLOATING_POINT = "Floating Point"
275+
DIRECTORY = "DIRECTORY"
276+
PASSWORD = "PASSWORD"
277+
278+
279+
class JavaType(Enum):
280+
FILE = "java.io.File"
281+
TEXT = "java.lang.String"
282+
INTEGER = "java.lang.Integer"
283+
FLOATING_POINT = "java.lang.Float"
284+
DIRECTORY = "DIRECTORY"
285+
PASSWORD = "PASSWORD"
286+
287+
288+
class Optional(Enum):
289+
REQUIRED = ""
290+
OPTIONAL = "on"
291+
292+
293+
class GPParamSpec:
294+
"""
295+
Specification needed to create a parameter for a new GenePattern module
296+
"""
297+
def __init__(self, name=None, description="", optional=Optional.REQUIRED,
298+
type=Type.TEXT, choices={}, value="", default_value="",
299+
file_format=[], min_values=0, max_values=1,
300+
flag="", prefix_when_specified=False):
301+
302+
self.name = name
303+
self.description = description
304+
self.optional = optional
305+
306+
self.type = type
307+
self.choices = choices
308+
self.value = value
309+
self.default_value = default_value
310+
311+
self.file_format = file_format
312+
self.min_values = min_values
313+
self.max_values = max_values
314+
315+
self.flag = flag
316+
self.prefix_when_specified = prefix_when_specified
317+
318+
def validate(self):
319+
# Check all values for None, only max_values is allowed to be None
320+
for attr in self.__dict__:
321+
if self.__dict__[attr] is None and attr != "max_values":
322+
raise ValueError(attr + " is not set")
323+
324+
# Validate name
325+
invalid_chars = GPTaskSpec.invalid_chars()
326+
if any(char in invalid_chars for char in self.name):
327+
raise ValueError("parameter name includes invalid characters: " + self.name)
328+
329+
# Validate min_values
330+
if not isinstance(self.min_values, int):
331+
raise ValueError("min_values not an int in: " + self.name)
332+
333+
# Validate max_values
334+
if not isinstance(self.max_values, int) and self.max_values is not None and self.max_values != float("inf"):
335+
raise ValueError("max_values not an int, None or infinity in: " + self.name)
336+
337+
# Validate file formats
338+
if not GPTaskSpec.all_strings(self.file_format):
339+
raise TypeError("file_format contains non-string value in parameter: " + self.name)
340+
341+
# Validate choices dict
342+
if not isinstance(self.choices, dict):
343+
raise TypeError("choices is not dict in parameter: " + self.name)
344+
345+
# Return that everything validates
346+
return True
347+
348+
def manifest_repr(self, p_num):
349+
"""
350+
Builds a manifest string representation of the parameters and returns it
351+
:param p_num: int
352+
:return: string
353+
"""
354+
# Build the parameter prefix
355+
prefix = "p" + str(p_num) + "_"
356+
357+
# Generate the manifest string
358+
manifest = prefix + "MODE=" + ("IN" if self.type == Type.FILE else "") + "\n"
359+
manifest += prefix + "TYPE=" + str(self.type.value) + "\n"
360+
if self.type == Type.FILE and len(self.choices) > 0:
361+
manifest += prefix + "choices=" + self._choices() + "\n"
362+
manifest += prefix + "default_value=" + self.default_value + "\n"
363+
manifest += prefix + "description=" + GPTaskSpec.manifest_escape(self.description) + "\n"
364+
manifest += prefix + "fileFormat=" + ';'.join(self.file_format) + "\n"
365+
manifest += prefix + "flag=" + self.flag + "\n"
366+
manifest += prefix + "name=" + self.name + "\n"
367+
manifest += prefix + "numValues=" + self._num_values() + "\n"
368+
manifest += prefix + "optional=" + str(self.optional.value) + "\n"
369+
manifest += prefix + "prefix=" + (self.flag if self.prefix_when_specified else "") + "\n"
370+
manifest += prefix + "prefix_when_specified=" + (self.flag if self.prefix_when_specified else "") + "\n"
371+
manifest += prefix + "type=" + self._java_type() + "\n"
372+
manifest += prefix + "value=" + (self._choices() if self.type != Type.FILE and len(self.choices) > 0 else "") + "\n"
373+
374+
# Return the manifest string
375+
return manifest
376+
377+
def _choices(self):
378+
"""
379+
Generate a string of choices as key/value pairs
380+
:return: string
381+
"""
382+
# Generate key/value strings
383+
pairs = []
384+
for key, value in self.choices.items():
385+
pairs.append(str(value) + "=" + str(key))
386+
387+
# Assemble into overall string and escape
388+
return GPTaskSpec.manifest_escape(";".join(pairs))
389+
390+
def _num_values(self):
391+
"""
392+
Generate a valid num_values string based off min_values and max_values
393+
:return: string
394+
"""
395+
# Add min_values to string
396+
num_values = str(self.min_values) if self.min_values else "0"
397+
398+
# Handle infinite max_values or finite max_values
399+
if self.max_values is None or self.max_values == float("inf"):
400+
num_values += "+"
401+
else:
402+
num_values += ".." + str(self.max_values)
403+
404+
# Return the num_values string
405+
return num_values
406+
407+
def _java_type(self):
408+
"""
409+
Translates GenePattern type string to Java type string
410+
:return: string
411+
"""
412+
return JavaType[self.type.name].value

0 commit comments

Comments
 (0)