#!/usr/bin/env python3 '''Small helpers shared by analyze_flex_ddG.py and extract_structures.py. Standard library only, so that extract_structures.py keeps running without numpy or pandas. ''' import re import sqlite3 # Rosetta records the full option list it was invoked with in the "protocols" table of every # features database it writes, so the stride a run actually used can be read back out of the # output instead of being remembered and re-typed by hand. _stride_re = re.compile( r'backrub_trajectory_stride[= ]+(\d+)' ) def trajectory_stride_from_db3( db3_file ): '''The backrub_trajectory_stride the run in db3_file was launched with. Returns None if it cannot be determined, which happens for databases written by Rosetta versions that predate the protocols table, or if the stride was left at the XML default instead of being passed on the command line. Callers should fall back to a default and say so, because the stride does not affect any energy -- it only labels the checkpoints, so a wrong value silently mislabels every row (and misnames every extracted PDB) rather than causing a visible failure. ''' try: conn = sqlite3.connect( 'file:%s?mode=ro' % db3_file, uri = True ) except sqlite3.Error: return None try: try: rows = conn.execute( 'SELECT specified_options, command_line FROM protocols' ).fetchall() except sqlite3.Error: return None strides = set() for row in rows: for field in row: if field: strides.update( int(m) for m in _stride_re.findall( field ) ) finally: conn.close() # More than one distinct value means the database mixes runs; we cannot label it correctly. if len( strides ) == 1: return strides.pop() return None