1 #!/bin/env python 2 # File: MiscUtil.py 3 # Author: Manish Sud <msud@san.rr.com> 4 # 5 # Copyright (C) 2024 Manish Sud. All rights reserved. 6 # 7 # This file is part of MayaChemTools. 8 # 9 # MayaChemTools is free software; you can redistribute it and/or modify it under 10 # the terms of the GNU Lesser General Public License as published by the Free 11 # Software Foundation; either version 3 of the License, or (at your option) any 12 # later version. 13 # 14 # MayaChemTools is distributed in the hope that it will be useful, but without 15 # any warranty; without even the implied warranty of merchantability of fitness 16 # for a particular purpose. See the GNU Lesser General Public License for more 17 # details. 18 # 19 # You should have received a copy of the GNU Lesser General Public License 20 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 21 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 22 # Boston, MA, 02111-1307, USA. 23 # 24 25 from __future__ import print_function 26 27 import os 28 import sys 29 import time 30 import re 31 import csv 32 import textwrap 33 import glob 34 import base64 35 import pickle 36 import multiprocessing as mp 37 38 __all__ = ["CheckFileExt", "CheckTextValue", "DoesSMILESFileContainTitleLine", "ExpandFileNames", "GetExamplesTextFromDocOptText", "GetExcelStyleColumnLabel", "GetMayaChemToolsLibDataPath", "GetMayaChemToolsVersion", "GetTextLines", "GetTextLinesWords", "GetWallClockAndProcessorTime", "GetFormattedElapsedTime", "GetFormattedFileSize", "IsEmpty", "IsFloat", "IsInteger", "IsNumber", "JoinWords", "ObjectFromBase64EncodedString", "ObjectToBase64EncodedString", "ParseFileName", "PrintError", "PrintInfo", "PrintWarning", "ProcessOptionConformerGenerator", "ProcessOptionConformerParameters", "ProcessOptionInfileParameters", "ProcessOptionMultiprocessingParameters", "ProcessOptionNameValuePairParameters", "ProcessOptionOutfileParameters", "ProcessOptionPyMOLCubeFileViewParameters", "ProcessOptionSeabornPlotParameters", "ReplaceHTMLEntitiesInText", "ValidateOptionsDistinctFileNames", "ValidateOptionFileExt", "ValidateOptionFilePath", "ValidateOptionFloatValue", "ValidateOptionIntegerValue", "ValidateOptionNumberValue", "ValidateOptionNumberValues", "ValidateOptionsOutputFileOverwrite", "ValidateOptionTextValue", "TruncateText", "WrapText"] 39 40 def CheckFileExt(FileName, FileExts): 41 """Check file type based on the specified file extensions delimited by spaces. 42 43 Arguments: 44 FileName (str): Name of a file. 45 FileExts (str): Space delimited string containing valid file extensions. 46 47 Returns: 48 bool : True, FileName contains a valid file extension; Otherwise, False. 49 50 """ 51 52 for FileExt in FileExts.split(): 53 if re.search(r"\.%s$" % FileExt, FileName, re.IGNORECASE): 54 return True 55 56 return False 57 58 def CheckTextValue(Value, ValidValues): 59 """Check text value based on the specified valid values delimited by spaces. 60 61 Arguments: 62 Value (str): Text value 63 ValidValues (str): Space delimited string containing valid values. 64 65 Returns: 66 bool : True, Value is valid; Otherwise, False. 67 68 """ 69 70 ValidValues = re.sub(' ', '|', ValidValues) 71 if re.match("^(%s)$" % ValidValues, Value, re.IGNORECASE): 72 return True 73 74 return False 75 76 def GetTextLinesWords(TextFilePath, Delimiter, QuoteChar, IgnoreHeaderLine): 77 """Parse lines in the specified text file into words in a line and return a list containing 78 list of parsed line words. 79 80 Arguments: 81 TextFilePath (str): Text file name including file path. 82 Delimiter (str): Delimiter for parsing text lines. 83 QuoteChar (str): Quote character for line words. 84 IgnoreHeaderLine (bool): A flag indicating whether to ignore first 85 valid data line corresponding to header line. 86 87 Returns: 88 list : A list of lists containing parsed words for lines. 89 90 Notes: 91 The lines starting with # or // are considered comment lines and are 92 ignored during parsing along with any empty lines. 93 94 """ 95 if not os.path.exists(TextFilePath): 96 PrintError("The text file file, %s, doesn't exist.\n" % (TextFilePath)) 97 98 TextFile = open(TextFilePath, "r") 99 if TextFile is None: 100 PrintError("Couldn't open text file: %s.\n" % (TextFilePath)) 101 102 # Collect text lines... 103 TextLines = [] 104 FirstValidLine = True 105 for Line in TextFile: 106 Line = Line.rstrip() 107 108 # Ignore empty lines... 109 if not len(Line): 110 continue 111 112 # Ignore comments... 113 if re.match("^(#|\/\/)", Line, re.I): 114 continue 115 116 # Ignore header line... 117 if FirstValidLine: 118 FirstValidLine = False 119 if IgnoreHeaderLine: 120 continue 121 122 TextLines.append(Line) 123 124 TextFile.close() 125 126 # Parse text lines... 127 TextLinesWords = [] 128 129 TextLinesReader = csv.reader(TextLines, delimiter = Delimiter, quotechar = QuoteChar) 130 for LineWords in TextLinesReader: 131 TextLinesWords.append(LineWords) 132 133 return TextLinesWords 134 135 def GetTextLines(TextFilePath): 136 """Read text lines from input file, remove new line characters and return a list containing 137 stripped lines. 138 139 Arguments: 140 TextFilePath (str): Text file name including file path. 141 142 Returns: 143 list : A list lines. 144 145 """ 146 TextFile = open(TextFilePath, "r") 147 if TextFile is None: 148 PrintError("Couldn't open text file: %s.\n" % (TextFilePath)) 149 150 # Collect text lines... 151 TextLines = [Line.rstrip() for Line in TextFile] 152 153 TextFile.close() 154 155 return TextLines 156 157 def DoesSMILESFileContainTitleLine(FileName): 158 """Determine whether the SMILES file contain a title line based on the presence 159 of a string SMILES, Name or ID in the first line. 160 161 Arguments: 162 FileName (str): Name of a file. 163 164 Returns: 165 bool : True, File contains title line; Otherwise, False. 166 167 """ 168 169 Infile = open(FileName, "r") 170 if Infile is None: 171 return False 172 173 Line = Infile.readline() 174 Infile.close() 175 176 if re.search("(SMILES|Name|ID)", Line, re.I): 177 return True 178 179 return False 180 181 def ExpandFileNames(FilesSpec, Delimiter = ","): 182 """Expand files specification using glob module to process any * or ? wild 183 cards in file names and return a list of expanded file names. 184 185 Arguments: 186 FilesSpec (str): Files specifications 187 Delimiter (str): Delimiter for file specifications 188 189 Returns: 190 list : List of expanded file names 191 192 """ 193 FileNames = [] 194 if not len(FilesSpec): 195 return FileNames 196 197 for FileSpec in FilesSpec.split(Delimiter): 198 FileSpec = FileSpec.strip() 199 if re.search("(\*|\?)", FileSpec, re.I): 200 FileNames.extend(glob.glob(FileSpec)) 201 else: 202 FileNames.append(FileSpec) 203 204 return FileNames 205 206 def GetExamplesTextFromDocOptText(DocOptText): 207 """Get script usage example lines from a docopt doc string. The example text 208 line start from a line containing `Examples:` keyword at the beginning of the line. 209 210 Arguments: 211 DocOptText (str): Doc string containing script usage examples lines starting with 212 a line marked by `Examples:` keyword at the beginning of a line. 213 214 Returns: 215 str : A string containing text lines retrieved from the examples section of 216 DocOptText parameter. 217 218 """ 219 220 ExamplesStart = re.compile("^Examples:", re.IGNORECASE) 221 ExamplesEnd = re.compile("^(Author:|See also:|Copyright:)", re.IGNORECASE) 222 223 ExamplesText = 'Examples text is not available' 224 ExamplesTextFound = False 225 226 for Line in DocOptText.splitlines(): 227 if ExamplesStart.match(Line): 228 ExamplesText = 'Examples:' 229 ExamplesTextFound = True 230 continue 231 232 if ExamplesEnd.match(Line): 233 break 234 235 if ExamplesTextFound: 236 ExamplesText += "\n" + Line 237 238 return ExamplesText 239 240 def GetExcelStyleColumnLabel(ColNum): 241 """Return Excel style column label for a colum number. 242 243 Arguments: 244 ColNum (int): Column number 245 246 Returns: 247 str : Excel style column label. 248 249 """ 250 Letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 251 252 ColLabelList = [] 253 while ColNum: 254 ColNum, SubColNum = divmod(ColNum - 1, 26) 255 ColLabelList[:0] = Letters[SubColNum] 256 257 return ''.join(ColLabelList) 258 259 def GetWallClockAndProcessorTime(): 260 """Get wallclock and processor times in seconds. 261 262 Returns: 263 float : Wallclock time. 264 float : Processor time. 265 266 """ 267 return (time.time(), _GetProcessorTime()) 268 269 def _GetProcessorTime(): 270 """Get processor time """ 271 272 if sys.version_info[0] >= 3 and sys.version_info[1] >= 3: 273 ProcessorTime = time.process_time() 274 else: 275 ProcessorTime = time.clock() 276 277 return ProcessorTime 278 279 def GetMayaChemToolsVersion(): 280 """Get version number for MayaChemTools from PackageInfo.csv 281 file in MayaChemTool lib data directory. 282 283 Returns: 284 str : Version number 285 286 """ 287 VersionNumber = "NA" 288 289 PackageInfoFilePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "data", "PackageInfo.csv") 290 if not os.path.exists(PackageInfoFilePath): 291 return VersionNumber 292 293 Delimiter = ',' 294 QuoteChar = '"' 295 IgnoreHeaderLine = True 296 297 for LineWords in GetTextLinesWords(PackageInfoFilePath, Delimiter, QuoteChar, IgnoreHeaderLine): 298 KeyName, KeyValue = LineWords 299 if re.match("^VersionNumber$", KeyName, re.I): 300 VersionNumber = KeyValue 301 break 302 303 return VersionNumber 304 305 def GetMayaChemToolsLibDataPath(): 306 """Get location of MayaChemTools lib data directory. 307 308 Returns: 309 str : Location of MayaChemTools lib data directory. 310 311 Notes: 312 The location of MayaChemTools lib data directory is determined relative to 313 MayaChemTools python lib directory name available through sys.path. 314 315 """ 316 MayaChemToolsDataPath = "" 317 318 for PathEntry in sys.path: 319 if re.search("MayaChemTools", PathEntry, re.I) and re.search("Python", PathEntry, re.I): 320 MayaChemToolsDataPath = os.path.join(PathEntry, "..", "data") 321 break 322 323 if not len(MayaChemToolsDataPath): 324 PrintWarning("MayaChemTools lib directory location doesn't appear to exist in system search path specified by sys.path...") 325 326 return MayaChemToolsDataPath 327 328 def GetFormattedElapsedTime(StartingWallClockTime, StartingProcessorTime): 329 """Get elapsed wallclock and processor times as a string in the following 330 format: Wallclock: %s days, %d hrs, %d mins, %d secs (Process: %d days, 331 %d hrs, %d mins, %.2f secs). 332 333 Arguments: 334 StartingWallClockTime (float): Starting wallclock time in seconds. 335 StartingProcessorTime (float): Starting processor time in seconds. 336 337 Returns: 338 str : Elapsed time formatted as: 339 Wallclock: %s days, %d hrs, %d mins, %d secs (Process: %d days, 340 %d hrs, %d mins, %.2f secs) 341 342 """ 343 344 ElapsedWallClockTime = time.time() - StartingWallClockTime 345 ElapsedProcessorTime = _GetProcessorTime() - StartingProcessorTime 346 347 ElapsedTime = "Wallclock: %s (Process: %s)" % (_FormatTimeInSecondsAsText(ElapsedWallClockTime), _FormatTimeInSecondsAsText(ElapsedProcessorTime, Precision = 2)) 348 349 return ElapsedTime 350 351 def _FormatTimeInSecondsAsText(Seconds, Precision = 0): 352 """Get time in seconds in the following format: days, hrs, mins, secs.""" 353 354 SecondsInDay = 24 * 60 *60 355 SecondsInHour = 60 * 60 356 SecondsInMinute = 60 357 358 Days = Seconds//SecondsInDay 359 Hours = (Seconds - Days*SecondsInDay)//SecondsInHour 360 Minutes = (Seconds - Days*SecondsInDay - Hours*SecondsInHour)//SecondsInMinute 361 Seconds = Seconds - Days*SecondsInDay - Hours*SecondsInHour - Minutes*SecondsInMinute 362 363 TimeWords = [] 364 365 if Days: 366 TimeWords.append("%d day%s" % (Days, "s" if Days > 1 else "")) 367 368 if Days or Hours: 369 TimeWords.append("%d hr%s" % (Hours, "s" if Hours > 1 else "")) 370 371 if Days or Hours or Minutes: 372 TimeWords.append("%d min%s" % (Minutes, "s" if Minutes > 1 else "")) 373 374 Seconds = "%.*f" % (Precision, Seconds) 375 TimeWords.append("%s sec%s" % (Seconds, "s" if float(Seconds) > 1 else "")) 376 377 return ", ".join(TimeWords) 378 379 def GetFormattedFileSize(FileName, Precision = 1): 380 """Get file size as a string in the following format: %.*f <bytes, KB, MB, 381 GB> 382 383 Arguments: 384 FileName (str): File path. 385 Precision (int): File size precision. 386 387 Returns: 388 str : File size formatted as: %.2f <bytes, KB, MB, GB> 389 390 """ 391 392 Size = os.path.getsize(FileName) 393 394 if Size < 1024: 395 SizeDenominator = 1 396 SizeSuffix = "bytes" 397 elif Size < (1024*1024): 398 SizeDenominator = 1024 399 SizeSuffix = "KB" 400 elif Size < (1024*1024*1024): 401 SizeDenominator = 1024*1024 402 SizeSuffix = "MB" 403 elif Size < (1024*1024*1024*1024): 404 SizeDenominator = 1024*1024*1024 405 SizeSuffix = "GB" 406 else: 407 SizeDenominator = 1 408 SizeSuffix = "bytes" 409 410 Size /= SizeDenominator 411 412 FormattedSize = "%.*f %s" % (Precision, Size, SizeSuffix) 413 414 return FormattedSize 415 416 def IsEmpty(Value): 417 """Determine whether the specified value is empty after converting 418 it in to a string and removing all leading and trailing white spaces. A value 419 of type None is considered empty. 420 421 Arguments: 422 Value (str, int or float): Text or a value 423 424 Returns: 425 bool : True, Text string is empty; Otherwsie, False. 426 427 """ 428 429 if Value is None: 430 return True 431 432 TextValue = "%s" % Value 433 TextValue = TextValue.strip() 434 435 return False if len(TextValue) else True 436 437 def IsFloat(Value): 438 """Determine whether the specified value is a float by converting it 439 into a float. 440 441 Arguments: 442 Value (str, int or float): Text 443 444 Returns: 445 bool : True, Value is a float; Otherwsie, False. 446 447 """ 448 449 return IsNumber(Value) 450 451 def IsInteger(Value): 452 """Determine whether the specified value is an integer by converting it 453 into an int. 454 455 Arguments: 456 Value (str, int or float): Text 457 458 Returns: 459 bool : True, Value is an integer; Otherwsie, False. 460 461 """ 462 463 Status = True 464 465 if Value is None: 466 return False 467 468 try: 469 Value = int(Value) 470 Status = True 471 except ValueError: 472 Status = False 473 474 return Status 475 476 def IsNumber(Value): 477 """Determine whether the specified value is a number by converting it 478 into a float. 479 480 Arguments: 481 Value (str, int or float): Text 482 483 Returns: 484 bool : True, Value is a number; Otherwsie, False. 485 486 """ 487 488 Status = True 489 490 if Value is None: 491 return Status 492 493 try: 494 Value = float(Value) 495 Status = True 496 except ValueError: 497 Status = False 498 499 return Status 500 501 def JoinWords(Words, Delimiter, Quote = False): 502 """Join words in a list using specified delimiter with optional quotes around words. 503 504 Arguments: 505 Words (list): List containing words to join. 506 Delimiter (string): Delimiter for joining words. 507 Quote (bool): Put quotes around words. 508 509 Returns: 510 str : String containing joined words. 511 512 """ 513 514 if Quote: 515 JoinedWords = Delimiter.join('"{0}"'.format(Word) for Word in Words) 516 else: 517 JoinedWords = Delimiter.join(Words) 518 519 return JoinedWords 520 521 def ObjectToBase64EncodedString(Object): 522 """Encode Python object into base64 encoded string. The object is 523 pickled before encoding. 524 525 Arguments: 526 object: Python object. 527 528 Returns: 529 str : Base64 encode object string or None. 530 531 """ 532 533 return None if Object is None else base64.b64encode(pickle.dumps(Object)).decode() 534 535 def ObjectFromBase64EncodedString(EncodedObject): 536 """Generate Python object from a bas64 encoded and pickled 537 object string. 538 539 Arguments: 540 str: Base64 encoded and pickled object string. 541 542 Returns: 543 object : Python object or None. 544 545 """ 546 547 return None if EncodedObject is None else pickle.loads(base64.b64decode(EncodedObject)) 548 549 def ParseFileName(FilePath): 550 """Parse specified file path and return file dir, file name, and file extension. 551 552 Arguments: 553 FilePath (str): Name of a file with complete file path. 554 555 Returns: 556 str : File directory. 557 str : File name without file extension. 558 str : File extension. 559 560 """ 561 FileDir, FileBaseName = os.path.split(FilePath) 562 FileName, FileExt = os.path.splitext(FileBaseName) 563 564 if re.match("^\.", FileExt): 565 FileExt = re.sub("^\.", "", FileExt) 566 567 return (FileDir, FileName, FileExt) 568 569 def PrintError(Msg, Status=1): 570 """Print message to stderr along with flushing stderr and exit with a specified 571 status. An `Error` prefix is placed before the message. 572 573 Arguments: 574 Msg (str): Text message. 575 Status (int): Exit status. 576 577 """ 578 579 PrintInfo("Error: %s" % Msg) 580 sys.exit(Status) 581 582 def PrintInfo(Msg=''): 583 """Print message to stderr along with flushing stderr. 584 585 Arguments: 586 Msg (str): Text message. 587 588 """ 589 590 print(Msg, sep=' ', end='\n', file=sys.stderr) 591 sys.stderr.flush() 592 593 def PrintWarning(msg): 594 """Print message to stderr along with flushing stderr. An `Warning` prefix 595 is placed before the message. 596 597 Arguments: 598 Msg (str): Text message. 599 600 """ 601 602 PrintInfo("Warning: %s" % msg) 603 604 def ValidateOptionFileExt(OptionName, FileName, FileExts): 605 """Validate file type based on the specified file extensions delimited by spaces. 606 607 Arguments: 608 OptionName (str): Command line option name. 609 FileName (str): Name of a file. 610 FileExts (str): Space delimited string containing valid file extensions. 611 612 Notes: 613 The function exits with an error message for a file name containing 614 invalid file extension. 615 616 """ 617 618 if not CheckFileExt(FileName, FileExts): 619 PrintError("The file name specified , %s, for option \"%s\" is not valid. Supported file formats: %s\n" % (FileName, OptionName, FileExts)) 620 621 def ValidateOptionFilePath(OptionName, FilePath): 622 """Validate presence of the file. 623 624 Arguments: 625 OptionName (str): Command line option name. 626 FilePath (str): Name of a file with complete path. 627 628 Notes: 629 The function exits with an error message for a file path that doesn't exist. 630 631 """ 632 633 if not os.path.exists(FilePath): 634 PrintError("The file specified, %s, for option \"%s\" doesn't exist.\n" % (FilePath, OptionName)) 635 636 def ValidateOptionFloatValue(OptionName, OptionValue, CmpOpValueMap): 637 """Validate option value using comparison operater and value pairs in specified in 638 a map. 639 640 Arguments: 641 OptionName (str): Command line option name. 642 OptionValue (float or str): Command line option value. 643 CmpOpValueMap (dictionary): Comparison operator key and value pairs to 644 validate values specified in OptionValue. 645 646 Notes: 647 The function exits with an error message for an invalid option values specified 648 in OptionValue. 649 650 Examples: 651 652 ValidateOptionNumberValue("-b, --butinaSimilarityCutoff", 653 Options["--butinaSimilarityCutoff"], 654 {">": 0.0, "<=" : 1.0}) 655 656 """ 657 658 if not IsFloat(OptionValue): 659 PrintError("The value specified, %s, for option \"%s\" must be a float." % (OptionValue, OptionName)) 660 661 return ValidateOptionNumberValue(OptionName, float(OptionValue), CmpOpValueMap) 662 663 def ValidateOptionIntegerValue(OptionName, OptionValue, CmpOpValueMap): 664 """Validate option value using comparison operater and value pairs in specified in 665 a map. 666 667 Arguments: 668 OptionName (str): Command line option name. 669 OptionValue (int or str): Command line option value. 670 CmpOpValueMap (dictionary): Comparison operator key and value pairs to 671 validate values specified in OptionValue. 672 673 Notes: 674 The function exits with an error message for an invalid option values specified 675 in OptionValue. 676 677 Examples: 678 679 ValidateOptionIntegerValue("--maxConfs", Options["--maxConfs"], 680 {">": 0}) 681 682 """ 683 684 if not IsInteger(OptionValue): 685 PrintError("The value specified, %s, for option \"%s\" must be an integer." % (OptionValue, OptionName)) 686 687 return ValidateOptionNumberValue(OptionName, int(OptionValue), CmpOpValueMap) 688 689 def ValidateOptionNumberValue(OptionName, OptionValue, CmpOpValueMap): 690 """Validate option value using comparison operater and value pairs in specified in 691 a map. 692 693 Arguments: 694 OptionName (str): Command line option name. 695 OptionValue (int or float): Command line option value. 696 CmpOpValueMap (dictionary): Comparison operator key and value pairs to 697 validate values specified in OptionValue. 698 699 Notes: 700 The function exits with an error message for an invalid option values specified 701 in OptionValue. 702 703 Examples: 704 705 ValidateOptionNumberValue("--maxConfs", int(Options["--maxConfs"]), 706 {">": 0}) 707 ValidateOptionNumberValue("-b, --butinaSimilarityCutoff", 708 float(Options["--butinaSimilarityCutoff"]), 709 {">": 0.0, "<=" : 1.0}) 710 711 """ 712 713 Status = True 714 for CmpOp in CmpOpValueMap: 715 Value = CmpOpValueMap[CmpOp] 716 if re.match("^>$", CmpOp, re.I): 717 if OptionValue <= Value: 718 Status = False 719 break 720 elif re.match("^>=$", CmpOp, re.I): 721 if OptionValue < Value: 722 Status = False 723 break 724 elif re.match("^<$", CmpOp, re.I): 725 if OptionValue >= Value: 726 Status = False 727 break 728 elif re.match("^<=$", CmpOp, re.I): 729 if OptionValue > Value: 730 Status = False 731 break 732 else: 733 PrintError("The specified comparison operator, %s, for function ValidateOptionNumberValue is not supported\n" % (CmpOp)) 734 735 if not Status: 736 FirstValue = True 737 SupportedValues = "" 738 for CmpOp in CmpOpValueMap: 739 Value = CmpOpValueMap[CmpOp] 740 if FirstValue: 741 FirstValue = False 742 SupportedValues = "%s %s" % (CmpOp, Value) 743 else: 744 SupportedValues = "%s and %s %s" % (SupportedValues, CmpOp, Value) 745 746 PrintError("The value specified, %s, for option \"%s\" is not valid. Supported value(s): %s " % (OptionValue, OptionName, SupportedValues)) 747 748 def ValidateOptionNumberValues(OptionName, OptionValueString, OptionValueCount, OptionValueDelimiter, OptionValueType, CmpOpValueMap): 749 """Validate numerical option values using option value string, delimiter, value type, 750 and a specified map containing comparison operator and value pairs. 751 752 Arguments: 753 OptionName (str): Command line option name. 754 OptionValueString (str): Command line option value. 755 OptionValueCount (int): Number of values in OptionValueString. 756 OptionValueDelimiter (str): Delimiter used for values in OptionValueString. 757 OptionValueType (str): Valid number types (integer or float) 758 CmpOpValueMap (dictionary): Comparison operator key and value pairs to 759 validate values specified in OptionValueString. 760 761 Notes: 762 The function exits with an error message for invalid option values specified 763 in OptionValueString 764 765 Examples: 766 767 ValidateOptionNumberValues("-m, --molImageSize", 768 Options["--molImageSize"], 2, ",", "integer", {">": 0}) 769 770 """ 771 if not CheckTextValue(OptionValueType, "integer float"): 772 PrintError("The option value type specified, %s, for function ValidateOptionNumberValues is not valid. Supported value: integer float " % (OptionValueType)) 773 774 Values = OptionValueString.split(OptionValueDelimiter) 775 if OptionValueCount > 0 and len(Values) != OptionValueCount: 776 PrintError("The value specified, %s, for option \"%s\" is not valid. It must contain %d %s values separated by \"%s\"" % (OptionValueString, OptionName, OptionValueCount, OptionValueType, OptionValueDelimiter)) 777 778 IsIntergerValue = True 779 if re.match("^float$", OptionValueType, re.I): 780 IsIntergerValue = False 781 782 for Value in Values: 783 if IsIntergerValue: 784 if not IsInteger(Value): 785 PrintError("The value specified, %s, for option \"%s\" in string \"%s\" must be an integer." % (Value, OptionName, OptionValueString)) 786 Value = int(Value) 787 else: 788 if not IsFloat(Value): 789 PrintError("The value specified, %s, for option \"%s\" in string \"%s\" must be a float." % (Value, OptionName, OptionValueString)) 790 Value = float(Value) 791 ValidateOptionNumberValue(OptionName, Value, CmpOpValueMap) 792 793 def ValidateOptionTextValue(OptionName, OptionValue, ValidValues): 794 """Validate option value based on the valid specified values separated by spaces. 795 796 Arguments: 797 OptionName (str): Command line option name. 798 OptionValue (str): Command line option value. 799 ValidValues (str): Space delimited string containing valid values. 800 801 Notes: 802 The function exits with an error message for an invalid option value. 803 804 """ 805 806 if not CheckTextValue(OptionValue, ValidValues): 807 PrintError("The value specified, %s, for option \"%s\" is not valid. Supported value(s): %s " % (OptionValue, OptionName, ValidValues)) 808 809 def ValidateOptionsOutputFileOverwrite(OptionName, FilePath, OverwriteOptionName, OverwriteStatus): 810 """Validate overwriting of output file. 811 812 Arguments: 813 OptionName (str): Command line option name. 814 FilePath (str): Name of a file with complete file path. 815 OverwriteOptionName (str): Overwrite command line option name. 816 OverwriteStatus (bool): True, overwrite 817 818 Notes: 819 The function exits with an error message for a file that is present and is not allowed 820 to be written as indicated by value of OverwriteStatus. 821 822 """ 823 824 if os.path.exists(FilePath): 825 if not OverwriteStatus: 826 if len(OverwriteOptionName) > 4: 827 ShortOverwriteOptionName = OverwriteOptionName[:4] 828 else: 829 ShortOverwriteOptionName = OverwriteOptionName 830 831 PrintError("The file specified, %s, for option \"%s\" already exist. Use option \"%s\" or \"%s\" and try again.\n" % (FilePath, OptionName, ShortOverwriteOptionName, OverwriteOptionName)) 832 833 def ValidateOptionsDistinctFileNames(OptionName1, FilePath1, OptionName2, FilePath2): 834 """Validate two distinct file names. 835 836 Arguments: 837 OptionName1 (str): Command line option name. 838 FilePath1 (str): Name of a file with complete file path. 839 OptionName2 (str): Command line option name. 840 FilePath2 (str): Name of a file with complete file path. 841 842 Notes: 843 The function exits with an error message for two non distinct file names. 844 845 """ 846 847 FilePath1Pattern = r"^" + re.escape(FilePath1) + r"$" 848 if re.match(FilePath1Pattern, FilePath2, re.I): 849 PrintError("The file name specified, %s, for options \"%s\" and \"%s\" must be different.\n" % (FilePath1, OptionName1, OptionName2)) 850 851 def ProcessOptionConformerGenerator(OptionName, OptionValue): 852 """Process conformer generator option and return a map containing 853 paramater name and values for generatiing conformers. 854 855 Arguments: 856 ParamOptionName (str): Command line conformer generator option name 857 ParamOptionValue (str): Command line conformer generator option value 858 859 Returns: 860 dictionary: Conformer generation parameter name and value pairs. 861 862 """ 863 864 ParamsInfo = {} 865 866 if re.match("^SDG$", OptionValue, re.I): 867 ConformerGenerator = "SDG" 868 SkipConformerGeneration = False 869 UseExpTorsionAnglePrefs = False 870 ETVersion = 1 871 UseBasicKnowledge = False 872 elif re.match("^KDG$", OptionValue, re.I): 873 ConformerGenerator = "KDG" 874 SkipConformerGeneration = False 875 UseExpTorsionAnglePrefs = False 876 ETVersion = 1 877 UseBasicKnowledge = True 878 elif re.match("^ETDG$", OptionValue, re.I): 879 ConformerGenerator = "ETDG" 880 SkipConformerGeneration = False 881 UseExpTorsionAnglePrefs = True 882 ETVersion = 1 883 UseBasicKnowledge = False 884 elif re.match("^ETKDG$", OptionValue, re.I): 885 ConformerGenerator = "ETKDG" 886 SkipConformerGeneration = False 887 UseExpTorsionAnglePrefs = True 888 ETVersion = 1 889 UseBasicKnowledge = True 890 elif re.match("^ETKDGv2$", OptionValue, re.I): 891 ConformerGenerator = "ETKDG" 892 SkipConformerGeneration = False 893 UseExpTorsionAnglePrefs = True 894 ETVersion = 2 895 UseBasicKnowledge = True 896 elif re.match("^None$", OptionValue, re.I): 897 ConformerGenerator = "None" 898 SkipConformerGeneration = True 899 UseExpTorsionAnglePrefs = None 900 ETVersion = None 901 UseBasicKnowledge = None 902 else: 903 PrintError("The value, %s, specified using \"%s\" option is not a valid value. Supported values: SDG, KDG, ETDG, ETKDG, ETKDGv2, or None" % (OptionValue, OptionName)) 904 905 ParamsInfo["ConformerGenerator"] = ConformerGenerator 906 ParamsInfo["SkipConformerGeneration"] = SkipConformerGeneration 907 ParamsInfo["UseExpTorsionAnglePrefs"] = UseExpTorsionAnglePrefs 908 ParamsInfo["ETVersion"] = ETVersion 909 ParamsInfo["UseBasicKnowledge"] = UseBasicKnowledge 910 911 return ParamsInfo 912 913 def ProcessOptionConformerParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 914 """Process parameters for conformer generation and return a map containing processed 915 parameter names and values. 916 917 Arguments: 918 ParamsOptionName (str): Command line conformer generation parameters 919 option name. 920 ParamsOptionValue (str): Comma delimited list of parameter name and value pairs. 921 ParamsDefaultInfo (dict): Default values to override for selected parameters. 922 923 Returns: 924 dictionary: Processed parameter name and value pairs. 925 926 Notes: 927 The parameter name and values specified in ParamsOptionValues are validated before 928 returning them in a dictionary. 929 930 """ 931 932 ParamsInfo = {"ConfMethod": "ETKDGv2", "ForceField": "MMFF", "ForceFieldMMFFVariant": "MMFF94", "EnforceChirality": True, "EmbedRMSDCutoff": 0.5, "AlignConformers": True, "MaxConfs": 50, "MaxConfsTorsions": 50, "MaxIters": 250, "RandomSeed": "auto", "UseTethers": True} 933 934 # Setup a canonical paramater names... 935 ValidParamNames = [] 936 CanonicalParamNamesMap = {} 937 for ParamName in sorted(ParamsInfo): 938 ValidParamNames.append(ParamName) 939 CanonicalParamNamesMap[ParamName.lower()] = ParamName 940 941 # Update default values... 942 if ParamsDefaultInfo is not None: 943 for ParamName in ParamsDefaultInfo: 944 if ParamName not in ParamsInfo: 945 PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessOptionConformerParameters is not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 946 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 947 948 if re.match("^auto$", ParamsOptionValue, re.I): 949 # No specific parameters to process except for parameters with possible auto value... 950 _ProcessOptionConformerAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 951 return ParamsInfo 952 953 ParamsOptionValue = ParamsOptionValue.strip() 954 if not ParamsOptionValue: 955 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 956 957 ParamsOptionValueWords = ParamsOptionValue.split(",") 958 if len(ParamsOptionValueWords) % 2: 959 PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 960 961 # Validate paramater name and value pairs... 962 for Index in range(0, len(ParamsOptionValueWords), 2): 963 Name = ParamsOptionValueWords[Index].strip() 964 Value = ParamsOptionValueWords[Index + 1].strip() 965 966 CanonicalName = Name.lower() 967 if CanonicalName not in CanonicalParamNamesMap: 968 PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 969 970 ParamName = CanonicalParamNamesMap[CanonicalName] 971 ParamValue = Value 972 973 if re.match("^ConfMethod$", ParamName, re.I): 974 if not re.match("^(SDG|ETDG|KDG|ETKDG|ETKDGv2)$", Value, re.I): 975 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: SDG, KDG, ETDG, ETKDG or ETKDGv2" % (Value, Name, ParamsOptionName)) 976 ParamValue = Value 977 elif re.match("^ForceField$", ParamName, re.I): 978 if not re.match("^(UFF|MMFF)$", Value, re.I): 979 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: UFF or MMFF" % (Value, Name, ParamsOptionName)) 980 ParamValue = Value 981 elif re.match("^ForceFieldMMFFVariant$", ParamName, re.I): 982 if not re.match("^(MMFF94|MMFF94s)$", Value, re.I): 983 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: MMFF94 or MMFF94s" % (Value, Name, ParamsOptionName)) 984 ParamValue = Value 985 elif re.match("^(EnforceChirality|AlignConformers|UseTethers)$", ParamName, re.I): 986 if re.match("^(yes|true)$", Value, re.I): 987 Value = True 988 elif re.match("^(no|false)$", Value, re.I): 989 Value = False 990 else: 991 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: yes, no, true, or false" % (Value, Name, ParamsOptionName)) 992 ParamValue = Value 993 elif re.match("^(MaxConfs|MaxConfsTorsions|MaxIters)$", ParamName, re.I): 994 if not IsInteger(Value): 995 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" must be an integer." % (Value, Name, ParamsOptionName)) 996 Value = int(Value) 997 if Value <= 0: 998 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 999 ParamValue = Value 1000 elif re.match("^(EmbedRMSDCutoff)$", ParamName, re.I): 1001 if not re.match("^(auto|none)$", Value, re.I): 1002 if not IsFloat(Value): 1003 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" must be a float." % (Value, Name, ParamsOptionName)) 1004 Value = float(Value) 1005 if Value <= 0: 1006 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1007 ParamValue = Value 1008 elif re.match("^(RandomSeed)$", ParamName, re.I): 1009 if not re.match("^auto$", Value, re.I): 1010 if not IsInteger(Value): 1011 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" must be an integer." % (Value, Name, ParamsOptionName)) 1012 ParamValue = Value 1013 else: 1014 ParamValue = Value 1015 1016 # Set value... 1017 ParamsInfo[ParamName] = ParamValue 1018 1019 # Handle paramaters with possible auto values... 1020 _ProcessOptionConformerAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 1021 1022 return ParamsInfo 1023 1024 def _ProcessOptionConformerAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue): 1025 """Process parameters with possible auto values. 1026 """ 1027 1028 # Random seed parameter... 1029 ParamValue = "%s" % ParamsInfo["RandomSeed"] 1030 if re.match("^auto$", ParamValue, re.I): 1031 ParamValue = -1 1032 else: 1033 ParamValue = int(ParamValue) 1034 ParamsInfo["RandomSeed"] = ParamValue 1035 1036 # Random seed parameter... 1037 ParamValue = "%s" % ParamsInfo["EmbedRMSDCutoff"] 1038 if re.match("^(auto|none)$", ParamValue, re.I): 1039 ParamValue = -1.0 1040 else: 1041 ParamValue = float(ParamValue) 1042 ParamsInfo["EmbedRMSDCutoff"] = ParamValue 1043 1044 # Setup derived parameters to facilitate conformer generations and minimization... 1045 UseExpTorsionAnglePrefs = False 1046 UseBasicKnowledge = False 1047 if re.match("^SDG$", ParamsInfo["ConfMethod"], re.I): 1048 ETVersion = 1 1049 UseExpTorsionAnglePrefs = False 1050 UseBasicKnowledge = False 1051 elif re.match("^KDG$", ParamsInfo["ConfMethod"], re.I): 1052 ETVersion = 1 1053 UseExpTorsionAnglePrefs = False 1054 UseBasicKnowledge = True 1055 elif re.match("^ETDG$", ParamsInfo["ConfMethod"], re.I): 1056 ETVersion = 1 1057 UseExpTorsionAnglePrefs = True 1058 UseBasicKnowledge = False 1059 elif re.match("^ETKDG$", ParamsInfo["ConfMethod"], re.I): 1060 ETVersion = 1 1061 UseExpTorsionAnglePrefs = True 1062 UseBasicKnowledge = True 1063 elif re.match("^ETKDGv2$", ParamsInfo["ConfMethod"], re.I): 1064 ETVersion = 2 1065 UseExpTorsionAnglePrefs = True 1066 UseBasicKnowledge = True 1067 else: 1068 ETVersion = None 1069 UseExpTorsionAnglePrefs = None 1070 UseBasicKnowledge = None 1071 ParamsInfo["UseExpTorsionAnglePrefs"] = UseExpTorsionAnglePrefs 1072 ParamsInfo["ETVersion"] = ETVersion 1073 ParamsInfo["UseBasicKnowledge"] = UseBasicKnowledge 1074 1075 if re.match("^UFF$", ParamsInfo["ForceField"], re.I): 1076 UseUFF = True 1077 UseMMFF = False 1078 elif re.match("^MMFF$", ParamsInfo["ForceField"], re.I): 1079 UseUFF = False 1080 UseMMFF = True 1081 else: 1082 UseUFF = None 1083 UseMMFF = None 1084 ParamsInfo["UseUFF"] = UseExpTorsionAnglePrefs 1085 ParamsInfo["UseMMFF"] = UseBasicKnowledge 1086 1087 def ProcessOptionPyMOLCubeFileViewParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 1088 """Process PyMOl parameters for cube file views and return a map containing 1089 processed parameter names and values. 1090 1091 ParamsOptionValue is a comma delimited list of parameter name and value pairs 1092 for setting up PyMOL views. 1093 1094 The supported parameter names along with their default and possible 1095 values are shown below: 1096 1097 ContourColor1, red, ContourColor2, blue, 1098 ContourLevel1, -0.02, ContourLevel2, 0.02, 1099 ContourLevel, 0.02, 1100 ContourLevelAutoAt, 0.5, 1101 ESPRampValues, -1.0 0 1.0, 1102 ESPRampColors, red white blue, 1103 HideHydrogens, yes, DisplayESP, OnSurface, 1104 DisplayMolecule, BallAndStick, 1105 DisplaySphereScale, 0.3, DisplayStickRadius, 0.2, 1106 MeshQuality,2, MeshWidth, 0.5, 1107 SurfaceQualuty, 2, SurfaceTransparency, 0.25, 1108 VolumeColorRamp, auto, VolumeColorRampOpacity, 0.2, 1109 VolumeContourWindowFactor, 0.05 1110 1111 Arguments: 1112 ParamsOptionName (str): Command line PyMOL view option name. 1113 ParamsOptionValues (str): Comma delimited list of parameter name and value pairs. 1114 ParamsDefaultInfo (dict): Default values to override for selected parameters. 1115 1116 Returns: 1117 dictionary: Processed parameter name and value pairs. 1118 1119 """ 1120 1121 ParamsInfo = {"ContourColor1": "red", "ContourColor2": "blue", "ContourLevel1": -0.02, "ContourLevel2": 0.02, "ContourLevel": 0.02, "ContourLevelAutoAt": 0.5, "ESPRampValues": "-1.0 0 1.0", "ESPRampColors": "red white blue", "HideHydrogens": True, "DisplayESP": "OnSurface", "DisplayMolecule": "BallAndStick", "DisplaySphereScale": 0.3, "DisplayStickRadius": 0.2, "MeshQuality": 2, "MeshWidth": 0.5, "SurfaceQuality": 2, "SurfaceTransparency": 0.25, "VolumeColorRamp": "auto", "VolumeColorRampOpacity": 0.2, "VolumeContourWindowFactor": 0.05} 1122 1123 # Setup a canonical paramater names... 1124 ValidParamNames = [] 1125 CanonicalParamNamesMap = {} 1126 for ParamName in sorted(ParamsInfo): 1127 ValidParamNames.append(ParamName) 1128 CanonicalParamNamesMap[ParamName.lower()] = ParamName 1129 1130 # Update default values... 1131 if ParamsDefaultInfo is not None: 1132 for ParamName in ParamsDefaultInfo: 1133 if ParamName not in ParamsInfo: 1134 PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessOptionPyMOLViewParametersForCubeFiles not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 1135 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 1136 1137 if re.match("^auto$", ParamsOptionValue, re.I): 1138 # No specific parameters to process except for parameters with possible auto value... 1139 _ProcessOptionPyMOLCubeFileViewAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 1140 return ParamsInfo 1141 1142 ParamsOptionValue = ParamsOptionValue.strip() 1143 if not ParamsOptionValue: 1144 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 1145 1146 ParamsOptionValueWords = ParamsOptionValue.split(",") 1147 if len(ParamsOptionValueWords) % 2: 1148 PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 1149 1150 # Validate paramater name and value pairs... 1151 for Index in range(0, len(ParamsOptionValueWords), 2): 1152 Name = ParamsOptionValueWords[Index].strip() 1153 Value = ParamsOptionValueWords[Index + 1].strip() 1154 1155 CanonicalName = Name.lower() 1156 if CanonicalName not in CanonicalParamNamesMap: 1157 PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 1158 1159 ParamName = CanonicalParamNamesMap[CanonicalName] 1160 ParamValue = Value 1161 ParamValueStr = "%s" % Value 1162 1163 if re.match("^(MeshWidth|SurfaceTransparency|DisplaySphereScale|DisplayStickRadius)$", ParamName, re.I): 1164 if not IsFloat(Value): 1165 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1166 Value = float(Value) 1167 if Value <= 0: 1168 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1169 ParamValue = Value 1170 elif re.match("^(MeshQuality|SurfaceQuality)$", ParamName, re.I): 1171 if not IsInteger(Value): 1172 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be an integer." % (Value, Name, ParamsOptionName)) 1173 Value = int(Value) 1174 if Value <= 0: 1175 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1176 ParamValue = Value 1177 elif re.match("^ContourLevel1$", ParamName, re.I) and not re.match("^auto$", ParamValueStr, re.I): 1178 if not IsFloat(Value): 1179 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1180 Value = float(Value) 1181 if Value >= 0: 1182 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: < 0" % (Value, Name, ParamsOptionName)) 1183 ParamValue = Value 1184 elif re.match("^ContourLevel2$", ParamName, re.I) and not re.match("^auto$", ParamValueStr, re.I): 1185 if not IsFloat(Value): 1186 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1187 Value = float(Value) 1188 if Value <= 0: 1189 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1190 ParamValue = Value 1191 elif re.match("^ContourLevel$", ParamName, re.I) and not re.match("^auto$", ParamValueStr, re.I): 1192 if not IsFloat(Value): 1193 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1194 ParamValue = float(Value) 1195 elif re.match("^ContourLevelAutoAt$", ParamName, re.I): 1196 if not IsFloat(Value): 1197 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1198 Value = float(Value) 1199 if Value <= 0 or Value >= 1: 1200 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0 and < 1" % (Value, Name, ParamsOptionName)) 1201 ParamValue = Value 1202 elif re.match("^VolumeColorRampOpacity$", ParamName, re.I): 1203 if not IsFloat(Value): 1204 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1205 Value = float(Value) 1206 if Value < 0 or Value > 1: 1207 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: >= 0 and <= 1" % (Value, Name, ParamsOptionName)) 1208 ParamValue = Value 1209 elif re.match("^VolumeContourWindowFactor$", ParamName, re.I): 1210 if not IsFloat(Value): 1211 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1212 Value = float(Value) 1213 if Value <= 0: 1214 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1215 ParamValue = Value 1216 elif re.match("^HideHydrogens$", ParamName, re.I): 1217 if not re.match("^(Yes|No|True|False)$", Value, re.I): 1218 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: Yes No True False" % (Value, Name, ParamsOptionName)) 1219 ParamValue = True 1220 if re.match("^(No|False)$", Value, re.I): 1221 ParamValue = False 1222 elif re.match("^DisplayMolecule$", ParamName, re.I): 1223 if re.match("^Sticks$", Value, re.I): 1224 ParamValue = "Sticks" 1225 elif re.match("^BallAndStick$", Value, re.I): 1226 ParamValue = "BallAndStick" 1227 else: 1228 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: Sticks or BallAndStick" % (Value, Name, ParamsOptionName)) 1229 elif re.match("^DisplayESP$", ParamName, re.I): 1230 if re.match("^OnTotalDensity$", Value, re.I): 1231 ParamValue = "OnTotalDensity" 1232 elif re.match("^OnSurface$", Value, re.I): 1233 ParamValue = "OnSurface" 1234 else: 1235 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: OnTotalDensity or OnSurface" % (Value, Name, ParamsOptionName)) 1236 else: 1237 ParamValue = Value 1238 1239 # Set value... 1240 ParamsInfo[ParamName] = ParamValue 1241 1242 # Handle paramaters with possible auto values... 1243 _ProcessOptionPyMOLCubeFileViewAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 1244 1245 return ParamsInfo 1246 1247 def _ProcessOptionPyMOLCubeFileViewAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue): 1248 """Process parameters with possible auto values. 1249 """ 1250 1251 # Setup ParamsInfo to indicate "auto" values... 1252 ParamNames = ["VolumeColorRamp", "ContourLevel1", "ContourLevel2", "ContourLevel", "ESPRampColors", "ESPRampValues"] 1253 for ParamName in ParamNames: 1254 ParamValue = "%s" % ParamsInfo[ParamName] 1255 ParamValueAuto = True if re.match("^auto$", ParamValue, re.I) else False 1256 ParamNameAuto = "%sAuto" % ParamName 1257 ParamsInfo[ParamNameAuto] = ParamValueAuto 1258 1259 def ProcessOptionMultiprocessingParameters(ParamsOptionName, ParamsOptionValue): 1260 """Process parameters for multiprocessing and return a map containing processed 1261 parameter names and values. 1262 1263 Arguments: 1264 ParamsOptionName (str): Command line multiprocessing parameters option name. 1265 ParamsOptionValue (str): Comma delimited list of parameter name and value pairs. 1266 1267 Returns: 1268 dictionary: Processed parameter name and value pairs. 1269 1270 Notes: 1271 The parameter name and values specified in ParamsOptionValue are validated before 1272 returning them in a dictionary. 1273 1274 """ 1275 1276 ParamsInfo = {'ChunkSize': 'auto', 'InputDataMode' : 'Lazy', 'NumProcesses': 'auto'} 1277 1278 if re.match("^auto$", ParamsOptionValue, re.I): 1279 # No specific parameters to process except for parameters with possible auto value... 1280 _ProcessOptionMultiprocessingParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 1281 return ParamsInfo 1282 1283 ParamsOptionValue = re.sub(" ", "", ParamsOptionValue) 1284 if not ParamsOptionValue: 1285 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 1286 1287 ParamsOptionValueWords = ParamsOptionValue.split(",") 1288 if len(ParamsOptionValueWords) % 2: 1289 PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 1290 1291 # Setup a canonical paramater names... 1292 ValidParamNames = [] 1293 CanonicalParamNamesMap = {} 1294 for ParamName in sorted(ParamsInfo): 1295 ValidParamNames.append(ParamName) 1296 CanonicalParamNamesMap[ParamName.lower()] = ParamName 1297 1298 # Validate paramater name and value pairs... 1299 for Index in range(0, len(ParamsOptionValueWords), 2): 1300 Name = ParamsOptionValueWords[Index] 1301 Value = ParamsOptionValueWords[Index + 1] 1302 1303 CanonicalName = Name.lower() 1304 if CanonicalName not in CanonicalParamNamesMap: 1305 PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 1306 1307 ParamName = CanonicalParamNamesMap[CanonicalName] 1308 ParamValue = Value 1309 1310 if re.match("^InputDataMode$", ParamName, re.I): 1311 if re.match("^Lazy$", Value, re.I): 1312 ParamValue = "Lazy" 1313 elif re.match("^InMemory$", Value, re.I): 1314 ParamValue = "InMemory" 1315 else: 1316 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: Lazy or InMemory" % (Value, Name, ParamsOptionName)) 1317 elif re.match("^NumProcesses$", ParamName, re.I): 1318 if not re.match("^Auto$", Value, re.I): 1319 Value = int(Value) 1320 if Value <= 0: 1321 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1322 if Value > mp.cpu_count(): 1323 PrintWarning("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is greater than number of CPUs, %s, returned by mp.cpu_count()." % (Value, Name, ParamsOptionName, mp.cpu_count())) 1324 else: 1325 if not re.match("^Auto$", Value, re.I): 1326 Value = int(Value) 1327 if Value <= 0: 1328 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1329 1330 # Set value... 1331 ParamsInfo[ParamName] = ParamValue 1332 1333 # Handle paramaters with possible auto values... 1334 _ProcessOptionMultiprocessingParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 1335 1336 return ParamsInfo 1337 1338 def _ProcessOptionMultiprocessingParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue): 1339 """Process parameters with possible auto values.""" 1340 1341 # NumProcesses parameter... 1342 Value = ParamsInfo["NumProcesses"] 1343 ParamsInfo["NumProcesses"] = mp.cpu_count() if re.match("^auto$", Value, re.I) else int(Value) 1344 1345 # ChunkSize parameter... 1346 Value = ParamsInfo["ChunkSize"] 1347 if re.match("^auto$", Value, re.I): 1348 Value = None if re.match("^InMemory$", ParamsInfo["InputDataMode"], re.I) else 1 1349 else: 1350 Value = int(Value) 1351 ParamsInfo["ChunkSize"] = Value 1352 1353 def ProcessOptionInfileParameters(ParamsOptionName, ParamsOptionValue, InfileName = None, OutfileName = None, ParamsDefaultInfo = None): 1354 """Process parameters for reading input files and return a map containing 1355 processed parameter names and values. 1356 1357 Arguments: 1358 ParamsOptionName (str): Command line input parameters option name. 1359 ParamsOptionValue (str): Comma delimited list of parameter name and value pairs. 1360 InfileName (str): Name of input file. 1361 OutfileName (str): Name of output file. 1362 ParamsDefaultInfo (dict): Default values to override for selected parameters. 1363 1364 Returns: 1365 dictionary: Processed parameter name and value pairs. 1366 1367 Notes: 1368 The parameter name and values specified in ParamsOptionValue are validated before 1369 returning them in a dictionary. 1370 1371 """ 1372 1373 ParamsInfo = {'RemoveHydrogens': True, 'Sanitize': True, 'StrictParsing': True, 'SMILESColumn': 1, 'SMILESNameColumn': 2, 'SMILESDelimiter': ' ', 'SMILESTitleLine': 'auto'} 1374 1375 # Update default values... 1376 if ParamsDefaultInfo is not None: 1377 for ParamName in ParamsDefaultInfo: 1378 if ParamName not in ParamsInfo: 1379 ValidParamNames = sorted(ParamsInfo.keys()) 1380 PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessOptionInfileParameters is not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 1381 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 1382 1383 _ProcessInfileAndOutfileParameters('Infile', ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName, OutfileName) 1384 1385 return ParamsInfo 1386 1387 def ProcessOptionOutfileParameters(ParamsOptionName, ParamsOptionValue, InfileName = None, OutfileName = None, ParamsDefaultInfo = None): 1388 """Process parameters for writing output files and return a map containing 1389 processed parameter names and values. 1390 1391 Arguments: 1392 ParamsOptionName (str): Command line input parameters option name. 1393 ParamsOptionValue (str): Comma delimited list of parameter name and value pairs. 1394 InfileName (str): Name of input file. 1395 OutfileName (str): Name of output file. 1396 ParamsDefaultInfo (dict): Default values to override for selected parameters. 1397 1398 Returns: 1399 dictionary: Processed parameter name and value pairs. 1400 1401 Notes: 1402 The parameter name and values specified in ParamsOptionValue are validated before 1403 returning them in a dictionary. 1404 1405 The default value of some parameters may depend on type of input file. Consequently, 1406 the input file name is also needed. 1407 1408 """ 1409 1410 ParamsInfo = {'Compute2DCoords': 'auto', 'Kekulize': True, 'ForceV3000': False, 'SMILESKekulize': False, 'SMILESDelimiter': ' ', 'SMILESIsomeric': True, 'SMILESTitleLine': True, 'SMILESMolName': True, 'SMILESMolProps': False} 1411 1412 # Update default values... 1413 if ParamsDefaultInfo is not None: 1414 for ParamName in ParamsDefaultInfo: 1415 if ParamName not in ParamsInfo: 1416 ValidParamNames = sorted(ParamsInfo.keys()) 1417 PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessOptionOutfileParameters is not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 1418 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 1419 1420 _ProcessInfileAndOutfileParameters('Outfile', ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName, OutfileName) 1421 1422 return ParamsInfo 1423 1424 def _ProcessInfileAndOutfileParameters(Mode, ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName, OutfileName): 1425 """Process specified infile and outfile paramaters. 1426 1427 """ 1428 if re.match("^auto$", ParamsOptionValue, re.I): 1429 # No specific parameters to process except for parameters with possible auto value... 1430 _ProcessInfileAndOutfileAutoParameters(Mode, ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName, OutfileName) 1431 return 1432 1433 ParamsOptionValue = re.sub(" ", "", ParamsOptionValue) 1434 if not ParamsOptionValue: 1435 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 1436 1437 ParamsOptionValueWords = ParamsOptionValue.split(",") 1438 if len(ParamsOptionValueWords) % 2: 1439 PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 1440 1441 # Setup a canonical paramater names... 1442 ValidParamNames = [] 1443 CanonicalParamNamesMap = {} 1444 for ParamName in sorted(ParamsInfo): 1445 ValidParamNames.append(ParamName) 1446 CanonicalParamNamesMap[ParamName.lower()] = ParamName 1447 1448 # Validate paramater name and value pairs... 1449 for Index in range(0, len(ParamsOptionValueWords), 2): 1450 Name = ParamsOptionValueWords[Index] 1451 Value = ParamsOptionValueWords[Index + 1] 1452 1453 CanonicalName = Name.lower() 1454 if CanonicalName not in CanonicalParamNamesMap: 1455 PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 1456 1457 ParamName = CanonicalParamNamesMap[CanonicalName] 1458 ParamValue = Value 1459 1460 if re.match("^(Sanitize|StrictParsing|RemoveHydrogens|Kekulize|ForceV3000|SMILESKekulize|SMILESIsomeric)$", ParamName, re.I): 1461 if not re.match("^(Yes|No|True|False)$", Value, re.I): 1462 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: Yes No True False" % (Value, Name, ParamsOptionName)) 1463 ParamValue = True 1464 if re.match("^(No|False)$", Value, re.I): 1465 ParamValue = False 1466 elif re.match("^SMILESTitleLine$", ParamName, re.I): 1467 if re.match("^Infile$", Mode, re.I): 1468 if not re.match("^(Yes|No|True|False|Auto)$", Value, re.I): 1469 PrintError("The parameter value, %s, specified for paramater name, %s, using \"%s\" option is not a valid value. Supported values: Yes No True False Auto" % (Value, Name, ParamsOptionName)) 1470 elif re.match("^Outfile$", Mode, re.I): 1471 if not re.match("^(Yes|No|True|False)$", Value, re.I): 1472 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: Yes No True False" % (Value, Name, ParamsOptionName)) 1473 ParamValue = True 1474 if re.match("^(No|False)$", Value, re.I): 1475 ParamValue = False 1476 elif re.match("^(SMILESMolName|SMILESMolProps)$", ParamName, re.I): 1477 if re.match("^Outfile$", Mode, re.I): 1478 if not re.match("^(Yes|No|True|False)$", Value, re.I): 1479 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: Yes No True False" % (Value, Name, ParamsOptionName)) 1480 ParamValue = True 1481 if re.match("^(No|False)$", Value, re.I): 1482 ParamValue = False 1483 else: 1484 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value during mode %s." % (Value, Name, ParamsOptionName, Mode)) 1485 elif re.match("^SMILESDelimiter$", ParamName, re.I): 1486 if not re.match("^(space|tab|comma)$", Value, re.I): 1487 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: space tab comma" % (Value, Name, ParamsOptionName)) 1488 ParamValue = " " 1489 if re.match("^tab$", Value, re.I): 1490 ParamValue = "\t" 1491 elif re.match("^comma$", Value, re.I): 1492 ParamValue = "," 1493 elif re.match("^Compute2DCoords$", ParamName, re.I): 1494 # No need to set the value. It would be processed later to handle "auto" value... 1495 if not re.match("^(Yes|No|True|False|Auto)$", Value, re.I): 1496 PrintError("The parameter value, %s, specified for paramater name, %s, using \"%s\" option is not a valid value. Supported values: Yes No True False Auto" % (Value, Name, ParamsOptionName)) 1497 else: 1498 ParamValue = int(Value) 1499 if ParamValue <= 0: 1500 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1501 1502 # Set value... 1503 ParamsInfo[ParamName] = ParamValue 1504 1505 # Handle paramaters with possible auto values... 1506 _ProcessInfileAndOutfileAutoParameters(Mode, ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName, OutfileName) 1507 1508 def _ProcessInfileAndOutfileAutoParameters(Mode, ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName, OutfileName): 1509 """Process parameters with possible auto values. 1510 1511 """ 1512 if re.match("^Infile$", Mode, re.I): 1513 # SMILESTitleLine parameter... 1514 Value = ParamsInfo["SMILESTitleLine"] 1515 ParamValue = False 1516 if re.match("^auto$", Value, re.I): 1517 if InfileName is not None: 1518 if CheckFileExt(InfileName, "smi csv tsv txt"): 1519 ParamValue = DoesSMILESFileContainTitleLine(InfileName) 1520 elif re.match("^(Yes|True)$", Value, re.I): 1521 ParamValue = True 1522 ParamsInfo["SMILESTitleLine"] = ParamValue 1523 elif re.match("^Outfile$", Mode, re.I): 1524 # Compute2DCoords parameter... 1525 Value = ParamsInfo["Compute2DCoords"] 1526 ParamValue = False 1527 if re.match("^auto$", Value, re.I): 1528 if InfileName is not None: 1529 if CheckFileExt(InfileName, "smi csv tsv txt"): 1530 ParamValue = True 1531 if OutfileName is not None: 1532 if CheckFileExt(OutfileName, "smi csv tsv txt"): 1533 # No need to compute 2D coords for SMILES file... 1534 ParamValue = False 1535 elif re.match("^(Yes|True)$", Value, re.I): 1536 ParamValue = True 1537 ParamsInfo["Compute2DCoords"] = ParamValue 1538 1539 # SetSMILESMolProps parameter... 1540 SetSMILESMolProps = False 1541 if OutfileName is not None: 1542 SetSMILESMolProps = True if (ParamsInfo["SMILESMolProps"] and CheckFileExt(OutfileName, "smi")) else False 1543 ParamsInfo["SetSMILESMolProps"] = SetSMILESMolProps 1544 1545 def ProcessOptionSeabornPlotParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 1546 """Process parameters for generating Seaborn plots and return a map containing 1547 processed parameter names and values. 1548 1549 Arguments: 1550 ParamsOptionName (str): Command line seaborn parameters option name. 1551 ParamsOptionValue (str): Comma delimited list of parameter name and value pairs. 1552 ParamsDefaultValues (dict): Default values for selected parameters. 1553 1554 Returns: 1555 dictionary: Processed parameter name and value pairs. 1556 1557 Notes: 1558 The parameter name and values specified in ParamsOptionValue are validated before 1559 returning them in a dictionary. 1560 1561 """ 1562 # The default width and height in Matplotlib is 6.4 and 4.8 and maps to aspect ratio of 1.3... 1563 ParamsInfo = {'Type' : 'auto', 'OutExt': 'svg', 'Width': 'auto', 'Height': 'auto', 1564 'Title': 'auto', 'XLabel': 'auto', 'YLabel': 'auto', 'TitleWeight': 'bold', 'LabelWeight': 'bold', 1565 'Style': 'darkgrid', 'Palette': 'deep', 'Font': 'sans-serif', 'FontScale': 1, 1566 'Context': 'notebook'} 1567 1568 # Setup a canonical paramater names... 1569 ValidParamNames = [] 1570 CanonicalParamNamesMap = {} 1571 for ParamName in sorted(ParamsInfo): 1572 ValidParamNames.append(ParamName) 1573 CanonicalParamNamesMap[ParamName.lower()] = ParamName 1574 1575 # Update default values... 1576 if ParamsDefaultInfo is not None: 1577 for ParamName in ParamsDefaultInfo: 1578 if ParamName not in ParamsInfo: 1579 PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessOptionSeabornPlotParameters is not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 1580 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 1581 1582 if re.match("^auto$", ParamsOptionValue, re.I): 1583 # No specific parameters to process except for parameters with possible auto value... 1584 _ProcessOptionSeabornPlotParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo) 1585 return ParamsInfo 1586 1587 ParamsOptionValue = ParamsOptionValue.strip() 1588 if not ParamsOptionValue: 1589 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 1590 1591 ParamsOptionValueWords = ParamsOptionValue.split(",") 1592 if len(ParamsOptionValueWords) % 2: 1593 PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 1594 1595 # Validate paramater name and value pairs... 1596 for Index in range(0, len(ParamsOptionValueWords), 2): 1597 Name = ParamsOptionValueWords[Index].strip() 1598 Value = ParamsOptionValueWords[Index + 1].strip() 1599 1600 CanonicalName = Name.lower() 1601 if CanonicalName not in CanonicalParamNamesMap: 1602 PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 1603 1604 ParamName = CanonicalParamNamesMap[CanonicalName] 1605 ParamValue = Value 1606 1607 if re.match("^Style$", ParamName, re.I): 1608 if not re.match("^(darkgrid|whitegrid|dark|white|ticks)$", Value): 1609 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: darkgrid, whitegrid, dark, white or ticks" % (Value, Name, ParamsOptionName)) 1610 elif re.match("^Palette$", ParamName, re.I): 1611 if not re.match("^(deep|muted|pastel|dark|bright|colorblind)$", Value): 1612 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: deep, muted, pastel, dark, bright or colorblind" % (Value, Name, ParamsOptionName)) 1613 elif re.match("^Context$", ParamName, re.I): 1614 if not re.match("^(notebook|paper|talk|poster)$", Value): 1615 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: notebook, paper, talk or poster" % (Value, Name, ParamsOptionName)) 1616 elif re.match("^(Width|Height)$", ParamName, re.I): 1617 if not re.match("^auto$", ParamValue, re.I): 1618 Value = float(Value) 1619 if Value <= 0: 1620 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1621 ParamValue = Value 1622 elif re.match("^(FontScale)$", ParamName, re.I): 1623 Value = float(Value) 1624 if Value <= 0: 1625 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 1626 ParamValue = Value 1627 # Set value... 1628 ParamsInfo[ParamName] = ParamValue 1629 1630 # Handle paramaters with possible auto values... 1631 _ProcessOptionSeabornPlotParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo) 1632 1633 return ParamsInfo 1634 1635 def _ProcessOptionSeabornPlotParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 1636 """Process parameters with possible auto values.""" 1637 1638 if ParamsDefaultInfo is None: 1639 return 1640 1641 for ParamName in ParamsInfo: 1642 ParamValue = "%s" % ParamsInfo[ParamName] 1643 if re.match("^auto$", ParamValue, re.I): 1644 if ParamName in ParamsDefaultInfo: 1645 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 1646 1647 def ProcessOptionNameValuePairParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo): 1648 """Process name and value parameter pairs for an option and return a map 1649 containing processed parameter names and values. 1650 1651 Arguments: 1652 ParamsOptionName (str): Command line option name for name and value 1653 parameter pairs. 1654 ParamsOptionValue (str): Comma delimited list of parameter name and 1655 value parameter pairs. 1656 ParamsDefaultInfo (dict): A dictionary containing a list of parameter 1657 type and default value pairs keyed by parameter name. Supported 1658 parameter types: bool, int, float, file, and str. 1659 1660 Returns: 1661 dictionary: Processed parameter name and value pairs. 1662 1663 Notes: 1664 The parameter names and values specified in ParamsOptionValue are validated before 1665 returning them in a dictionary. 1666 1667 Examples: 1668 1669 ParamsDefaultInfo = {"Cleanup": ["bool", True], "RemoveFragments": 1670 ["bool", True], "Neutralize": ["bool", True], 1671 "CanonicalizeTautomer": ["bool", True]} 1672 ProcessOptionNameValuePairParameters("--methodologyParams", 1673 Options["--methodologyParams"], ParamsDefaultInfo) 1674 1675 """ 1676 1677 # Process parameters default informaton.... 1678 ParamsValueInfo = {} 1679 ParamsTypeInfo = {} 1680 for ParamName in ParamsDefaultInfo: 1681 (ParamType, ParamValue) = ParamsDefaultInfo[ParamName] 1682 ParamsTypeInfo[ParamName] = ParamType 1683 ParamsValueInfo[ParamName] = ParamValue 1684 1685 if re.match("^auto$", ParamsOptionValue, re.I): 1686 return ParamsValueInfo 1687 1688 # Setup a canonical paramater names... 1689 ValidParamNames = [] 1690 CanonicalParamNamesMap = {} 1691 for ParamName in sorted(ParamsValueInfo): 1692 ValidParamNames.append(ParamName) 1693 CanonicalParamNamesMap[ParamName.lower()] = ParamName 1694 1695 ParamsOptionValue = ParamsOptionValue.strip() 1696 if not ParamsOptionValue: 1697 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 1698 1699 ParamsOptionValueWords = ParamsOptionValue.split(",") 1700 if len(ParamsOptionValueWords) % 2: 1701 PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 1702 1703 # Validate paramater name and value pairs... 1704 for Index in range(0, len(ParamsOptionValueWords), 2): 1705 Name = ParamsOptionValueWords[Index].strip() 1706 Value = ParamsOptionValueWords[Index + 1].strip() 1707 1708 CanonicalName = Name.lower() 1709 if CanonicalName not in CanonicalParamNamesMap: 1710 PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 1711 1712 ParamName = CanonicalParamNamesMap[CanonicalName] 1713 ParamType = ParamsTypeInfo[ParamName] 1714 ParamValue = Value 1715 1716 if re.match("^(bool|boolean)$", ParamType, re.I): 1717 if re.match("^(yes|true)$", Value, re.I): 1718 Value = True 1719 elif re.match("^(no|false)$", Value, re.I): 1720 Value = False 1721 else: 1722 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: yes, no, true, or false" % (Value, Name, ParamsOptionName)) 1723 ParamValue = Value 1724 elif re.match("^(int|integer)$", ParamType, re.I): 1725 if not IsInteger(Value): 1726 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be an integer." % (Value, Name, ParamsOptionName)) 1727 ParamValue = int(Value) 1728 elif re.match("^float$", ParamType, re.I): 1729 if not IsFloat(Value): 1730 PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 1731 ParamValue = float(Value) 1732 elif re.match("^file$", ParamType, re.I): 1733 if not os.path.exists(Value): 1734 PrintError("The file, %s, specified for parameter name, %s, using \"%s\" option doen't exist." % (Value, Name, ParamsOptionName)) 1735 ParamValue = Value 1736 elif re.match("^(str|string)$", ParamType, re.I): 1737 ParamValue = Value 1738 else: 1739 # Default to string values... 1740 PrintWarning("The parameter type, %s, specified for parameter name, %s, using \"%s\" option is not supported by function ProcessOptionNameValuePairParameters. It's being treated as a string type..." % (ParamType, Name, ParamsOptionName)) 1741 ParamValue = Value 1742 1743 # Set value... 1744 ParamsValueInfo[ParamName] = ParamValue 1745 1746 return ParamsValueInfo 1747 1748 def ReplaceHTMLEntitiesInText(Text): 1749 """Check and replace the followng HTML entities to their respective code 1750 for display in a browser: < (less than), > (greater than), & (ampersand), 1751 " (double quote), and ' (single quote). 1752 1753 Arguments: 1754 Text (str): Text value. 1755 1756 Returns: 1757 str : Modifed text value. 1758 1759 """ 1760 1761 if re.search("""(<|>|&|"|')""", Text): 1762 return Text.replace("<", "<").replace(">", ">").replace("&", "&").replace('"', """).replace("'","'") 1763 else: 1764 return Text 1765 1766 def TruncateText(Text, Width, TrailingChars = "..."): 1767 """Truncate text using specified width along with appending any trailing 1768 characters. 1769 1770 Arguments: 1771 Text (string): Input text. 1772 Width (int): Max number of characters before truncating text. 1773 Delimiter (string): Trailing characters to append or None. 1774 1775 Returns: 1776 str : Truncated text 1777 1778 """ 1779 1780 if len(Text) < Width: 1781 return Text 1782 1783 TruncatedText = (Text[:Width] + TrailingChars) if not IsEmpty(TrailingChars) else Text[:Width] 1784 1785 return TruncatedText 1786 1787 def WrapText(Text, Delimiter, Width): 1788 """Wrap text using specified delimiter and width. 1789 1790 Arguments: 1791 Text (string): Input text 1792 Delimiter (string): Delimiter for wrapping text 1793 Width (int): Max number of characters before wrapping text 1794 1795 Returns: 1796 str : Wrapped text 1797 1798 """ 1799 WrappedText = Delimiter.join(textwrap.wrap(Text, width = Width)) 1800 1801 return WrappedText