Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 76 additions & 42 deletions src/pdbdepo/pdb_deposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,70 @@ def read_software_templates():
return d


def substitute_tokens(
template: str,
xtal_name: str,
cmpd_code: str,
ext_codes: list,
pose_id_str: str,
) -> str:
"""Replace all known template tokens in a string and return the result.

$ExternalCodeN (N=2..9) are filled from ext_codes[N-2]; any that remain after substitution
are erased. The numbering starts at 2 because column 1 of the external-codes CSV is the
crystal name and is not exposed as a token.
"""
template = template.replace('$CompoundCode', cmpd_code)
template = template.replace('$CrystalName', xtal_name)
template = template.replace('$PoseID', pose_id_str)
if ext_codes:
for i, code in enumerate(ext_codes):
template = template.replace('$ExternalCode' + str(i + 2), code)
for i in range(1, 9):
template = template.replace('$ExternalCode' + str(i), '')
return template


def merge_mmcifgen_into_structure(
structure_block: cif.Block,
mmcifgen_block,
xtal_name: str,
cmpd_code: str,
cmpd_codes_dict: dict,
pose_ids_dict: dict,
) -> None:
"""Apply the per-investigation template block from mmcif-gen onto a per-crystal structure block.

Items that contain template tokens are expanded with crystal-specific values; all other items
are copied across unchanged.
"""
pose_id_str = ', '.join(pose_ids_dict.get(xtal_name, []))
ext_codes = cmpd_codes_dict.get(xtal_name)

for item in mmcifgen_block:
to_add = True
if item.loop is not None:
if '_struct.title' in item.loop.tags:
to_add = False
if len(item.loop.values) > 1:
expanded = substitute_tokens(item.loop.values[1], xtal_name, cmpd_code, ext_codes, pose_id_str)
new_loop = structure_block.init_loop('', item.loop.tags)
new_loop.add_row([item.loop.values[0], expanded])
elif any('$PoseID' in v for v in item.loop.values):
to_add = False
ncols = len(item.loop.tags)
nrows = len(item.loop.values) // ncols
new_loop = structure_block.init_loop('', item.loop.tags)
for r in range(nrows):
loop_row = [item.loop.values[r * ncols + c].replace('$PoseID', pose_id_str) for c in range(ncols)]
new_loop.add_row(loop_row)
elif item.pair is not None and '$PoseID' in item.pair[1]:
structure_block.set_pair(item.pair[0], item.pair[1].replace('$PoseID', pose_id_str))
to_add = False
if to_add:
structure_block.add_item(item)


def process_input(
base_dir: Path,
input_path: Path,
Expand Down Expand Up @@ -278,47 +342,10 @@ def process_input(
mmcifgen_refine_values,
)

# build the comma-separated pose ID string for this crystal
pose_ids = pose_ids_dict.get(xtal_name, [])
pose_id_str = ', '.join(pose_ids)

# add in the common metadata (generated by mmcif-gen)
for item in mmcifgen_block:
to_add = True
if item.loop is not None:
if '_struct.title' in item.loop.tags:
# special case of the structure title loop that needs expanding
to_add = False
if len(item.loop.values) > 1:
template = item.loop.values[1]
template = template.replace('$CompoundCode', cmpd_code)
template = template.replace('$CrystalName', xtal_name)
template = template.replace('$PoseID', pose_id_str)
cmpd_codes = cmpd_codes_dict.get(xtal_name)
if cmpd_codes:
for i, cmpd_code in enumerate(cmpd_codes):
template = template.replace('$ExternalCode' + str(i + 2), cmpd_code)
# erase any non-substituted ExternalCodes
for i in range(1, 9):
template = template.replace('$ExternalCode' + str(i), '')
new_loop = structure_cif_block0.init_loop('', item.loop.tags)
new_loop.add_row([item.loop.values[0], template])
elif any('$PoseID' in v for v in item.loop.values):
to_add = False
ncols = len(item.loop.tags)
nrows = len(item.loop.values) // ncols
new_loop = structure_cif_block0.init_loop('', item.loop.tags)
for r in range(nrows):
loop_row = [
item.loop.values[r * ncols + c].replace('$PoseID', pose_id_str) for c in range(ncols)
]
new_loop.add_row(loop_row)
elif item.pair is not None and '$PoseID' in item.pair[1]:
structure_cif_block0.set_pair(item.pair[0], item.pair[1].replace('$PoseID', pose_id_str))
to_add = False
if to_add:
# otherwise add the item as it is
added_item = structure_cif_block0.add_item(item)
merge_mmcifgen_into_structure(
structure_cif_block0, mmcifgen_block, xtal_name, cmpd_code, cmpd_codes_dict, pose_ids_dict
)

data_processing_log_file = None
data_processing_prog = row.get(Constants.SOAKDB_COL_DATA_PROCESSING_PROGRAM)
Expand Down Expand Up @@ -1101,7 +1128,14 @@ def read_fragalysis_csv(filename):
return d


def run(collator_path, metadata_csv, compound_codes_csv=None, fragalysis_csv=None, debug=False):
def run(
collator_path,
metadata_csv,
compound_codes_csv=None,
fragalysis_csv=None,
mmcifgen_runner=run_mmcifgen,
debug=False,
):
info('run on ' + str(datetime.datetime.now()))
info('using RDKit version ' + rdBase.rdkitVersion)
# info('using InCHI version ' + Chem.GetInchiVersion())
Expand All @@ -1127,7 +1161,7 @@ def run(collator_path, metadata_csv, compound_codes_csv=None, fragalysis_csv=Non

# run mmcif-gen
input_name = 'input' + str(i + 1)
run_mmcifgen(input_name, str(soakdb_file_p), metadata_csv, output_dir_p)
mmcifgen_runner(input_name, str(soakdb_file_p), metadata_csv, output_dir_p)

meta_doc = cif.read(str(output_dir_p / (input_name + '_model.cif')))
meta_mmcifgen = meta_doc[0]
Expand Down
Loading
Loading