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
13 changes: 10 additions & 3 deletions src/easydiffraction/io/cif/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@

from __future__ import annotations

import re

import gemmi

# Minimum raw-string length for CIF surrounding-quote detection
_MIN_QUOTED_LEN = 2

# Model datablock names use a backend-safe subset of CIF block-name
# characters. Hyphens and underscores are retained because they are used
# throughout EasyDiffraction's public naming conventions.
_UNSUPPORTED_DATABLOCK_NAME_CHARS = re.compile(r'[^a-z0-9_-]')


def document_from_path(path: str) -> gemmi.cif.Document:
"""Read a CIF document from a file path."""
Expand All @@ -26,9 +33,9 @@ def pick_sole_block(doc: gemmi.cif.Document) -> gemmi.cif.Block:


def name_from_block(block: gemmi.cif.Block) -> str:
"""Extract a model name from the CIF block name."""
# TODO: Need validator or normalization?
return block.name
"""Extract and normalize a model name from the CIF block name."""
lowercase_name = block.name.lower()
return _UNSUPPORTED_DATABLOCK_NAME_CHARS.sub('', lowercase_name)


def read_cif_str(block: gemmi.cif.Block, tag: str) -> str | None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ def test_experiment_factory_from_scratch():
assert ex.experiment_type.sample_form.value == SampleFormEnum.POWDER.value


def test_from_cif_str_normalizes_datablock_name():
from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory

experiment = ExperimentFactory.from_cif_str('data_83267-ICSD\n')

assert experiment.name == '83267-icsd'
assert experiment.as_cif.startswith('data_83267-icsd\n')


def test_from_cif_str_restores_non_default_peak_profile_type():
"""
Loading a CIF with a non-default peak profile type must reconstruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ def test_from_scratch():
assert m.name == 'abc'


def test_from_cif_str_normalizes_datablock_name():
structure = StructureFactory.from_cif_str('data_83267-ICSD\n')

assert structure.name == '83267-icsd'
assert structure.as_cif.startswith('data_83267-icsd\n')


def test_from_cif_str_accepts_underscore_style_structure_tags():
cif = """\
data_legacy
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/easydiffraction/io/cif/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ def test_name_from_block(self):
name = name_from_block(block)
assert name == 'silicon'

def test_name_from_block_normalizes_unsupported_name(self):
from easydiffraction.io.cif.parse import document_from_string
from easydiffraction.io.cif.parse import name_from_block
from easydiffraction.io.cif.parse import pick_sole_block

cif = 'data_My+83267-ICSD.example\n_cell.length_a 5.43\n'
doc = document_from_string(cif)
block = pick_sole_block(doc)
name = name_from_block(block)
assert name == 'my83267-icsdexample'


class TestDocumentFromPath:
def test_valid_file(self, tmp_path):
Expand Down