chemicalml 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +21 -14
- data/.rubocop.yml +23 -0
- data/CHANGELOG.md +32 -0
- data/CLAUDE.md +49 -1
- data/README.adoc +154 -26
- data/TODO.align/61-parallel-array-attrs.md +54 -0
- data/TODO.align/62-per-element-attribute-gaps.md +49 -0
- data/TODO.align/63-universal-children-mixin.md +42 -0
- data/TODO.align/64-per-element-child-gaps.md +81 -0
- data/TODO.align/65-simple-unit-convention.md +40 -0
- data/TODO.align/66-schema24-legacy-children.md +32 -0
- data/TODO.align/67-final-verification.md +32 -0
- data/TODO.align/68-detection-coverage.md +33 -0
- data/TODO.align/69-json-yaml-roundtrip.md +27 -0
- data/TODO.align/70-iterative-walker.md +40 -0
- data/TODO.align/71-violation-value.md +25 -0
- data/TODO.align/72-close-child-gaps.md +66 -0
- data/TODO.align/73-schema24-parse-limitation.md +64 -0
- data/TODO.align/74-final-verification-round18.md +15 -0
- data/TODO.align/75-key-value-mappings.md +43 -0
- data/TODO.align/76-schema24-workaround.md +57 -0
- data/TODO.align/77-convention-coverage-matrix.md +31 -0
- data/TODO.align/78-fixture-audit.md +23 -0
- data/TODO.align/79-final-verification-round19.md +15 -0
- data/TODO.align/80-xsd-enum-constants.md +35 -0
- data/TODO.align/81-enum-validation-constraints.md +29 -0
- data/TODO.align/82-reference-resolver.md +33 -0
- data/TODO.align/83-toml-roundtrip.md +20 -0
- data/TODO.align/84-final-verification-round21.md +16 -0
- data/TODO.align/85-bond-distinct-atoms.md +25 -0
- data/TODO.align/86-references-should-resolve.md +20 -0
- data/TODO.align/87-cli-utility.md +26 -0
- data/TODO.align/88-diverse-fixtures.md +26 -0
- data/TODO.align/89-final-verification-round22.md +15 -0
- data/TODO.align/90-bond-stereo-completeness.md +31 -0
- data/TODO.align/91-atomparity-distinct.md +21 -0
- data/TODO.align/92-unit-dictionary-completeness.md +28 -0
- data/TODO.align/93-yard-docs.md +23 -0
- data/TODO.align/94-final-verification-round23.md +15 -0
- data/TODO.align/README.round17.md +28 -0
- data/TODO.align/README.round18.md +37 -0
- data/TODO.align/README.round19.md +40 -0
- data/TODO.align/README.round20.md +49 -0
- data/TODO.align/README.round21.md +35 -0
- data/TODO.align/README.round22.md +48 -0
- data/TODO.align/README.round23.md +46 -0
- data/TODO.align/README.round24.md +53 -0
- data/TODO.align/README.round25.md +66 -0
- data/TODO.align/README.round26.md +55 -0
- data/TODO.align/README.round27.md +51 -0
- data/TODO.align/README.round28.md +61 -0
- data/TODO.align/README.round29.md +64 -0
- data/TODO.align/README.round30.md +50 -0
- data/TODO.align/README.round31.md +54 -0
- data/TODO.align/README.round32.md +67 -0
- data/TODO.align/README.round33.md +62 -0
- data/TODO.align/README.round34.md +70 -0
- data/chemicalml.gemspec +1 -1
- data/docs/constraints.md +161 -0
- data/docs/generate_constraint_docs.rb +44 -0
- data/exe/chemicalml +6 -0
- data/lib/chemicalml/cli/base.rb +68 -0
- data/lib/chemicalml/cli/command.rb +43 -0
- data/lib/chemicalml/cli/constraints_command.rb +27 -0
- data/lib/chemicalml/cli/conventions_command.rb +14 -0
- data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
- data/lib/chemicalml/cli/elements_command.rb +17 -0
- data/lib/chemicalml/cli/enums_command.rb +17 -0
- data/lib/chemicalml/cli/info_command.rb +77 -0
- data/lib/chemicalml/cli/inspect_command.rb +39 -0
- data/lib/chemicalml/cli/validate_command.rb +54 -0
- data/lib/chemicalml/cli.rb +43 -0
- data/lib/chemicalml/cml/base/abundance.rb +18 -8
- data/lib/chemicalml/cml/base/action.rb +34 -16
- data/lib/chemicalml/cml/base/action_list.rb +35 -17
- data/lib/chemicalml/cml/base/alternative.rb +13 -4
- data/lib/chemicalml/cml/base/amount.rb +14 -6
- data/lib/chemicalml/cml/base/angle.rb +28 -13
- data/lib/chemicalml/cml/base/annotation.rb +14 -6
- data/lib/chemicalml/cml/base/any_cml.rb +12 -5
- data/lib/chemicalml/cml/base/appinfo.rb +14 -6
- data/lib/chemicalml/cml/base/arg.rb +47 -13
- data/lib/chemicalml/cml/base/array.rb +40 -19
- data/lib/chemicalml/cml/base/array_list.rb +14 -6
- data/lib/chemicalml/cml/base/atom.rb +84 -28
- data/lib/chemicalml/cml/base/atom_array.rb +60 -23
- data/lib/chemicalml/cml/base/atom_parity.rb +14 -6
- data/lib/chemicalml/cml/base/atom_set.rb +14 -6
- data/lib/chemicalml/cml/base/atom_type.rb +25 -9
- data/lib/chemicalml/cml/base/atom_type_list.rb +22 -8
- data/lib/chemicalml/cml/base/atomic_basis_function.rb +33 -13
- data/lib/chemicalml/cml/base/band.rb +21 -8
- data/lib/chemicalml/cml/base/band_list.rb +15 -7
- data/lib/chemicalml/cml/base/basis_set.rb +24 -9
- data/lib/chemicalml/cml/base/bond.rb +36 -10
- data/lib/chemicalml/cml/base/bond_array.rb +34 -11
- data/lib/chemicalml/cml/base/bond_set.rb +14 -6
- data/lib/chemicalml/cml/base/bond_stereo.rb +20 -9
- data/lib/chemicalml/cml/base/bond_type.rb +23 -8
- data/lib/chemicalml/cml/base/bond_type_list.rb +22 -8
- data/lib/chemicalml/cml/base/cell_parameter.rb +28 -10
- data/lib/chemicalml/cml/base/cml_module.rb +42 -11
- data/lib/chemicalml/cml/base/common_children.rb +35 -0
- data/lib/chemicalml/cml/base/complex_object.rb +12 -5
- data/lib/chemicalml/cml/base/condition_list.rb +26 -10
- data/lib/chemicalml/cml/base/crystal.rb +26 -10
- data/lib/chemicalml/cml/base/definition.rb +14 -5
- data/lib/chemicalml/cml/base/description.rb +16 -6
- data/lib/chemicalml/cml/base/dictionary.rb +28 -11
- data/lib/chemicalml/cml/base/dictionary_entry.rb +69 -10
- data/lib/chemicalml/cml/base/dimension.rb +26 -12
- data/lib/chemicalml/cml/base/document.rb +24 -8
- data/lib/chemicalml/cml/base/documentation.rb +12 -5
- data/lib/chemicalml/cml/base/eigen.rb +32 -8
- data/lib/chemicalml/cml/base/electron.rb +26 -12
- data/lib/chemicalml/cml/base/enumeration.rb +16 -7
- data/lib/chemicalml/cml/base/expression.rb +21 -6
- data/lib/chemicalml/cml/base/float.rb +22 -10
- data/lib/chemicalml/cml/base/float_array.rb +24 -11
- data/lib/chemicalml/cml/base/formula.rb +31 -10
- data/lib/chemicalml/cml/base/fragment.rb +30 -12
- data/lib/chemicalml/cml/base/fragment_list.rb +24 -9
- data/lib/chemicalml/cml/base/gradient.rb +19 -6
- data/lib/chemicalml/cml/base/identifier.rb +18 -8
- data/lib/chemicalml/cml/base/integer.rb +22 -10
- data/lib/chemicalml/cml/base/integer_array.rb +24 -11
- data/lib/chemicalml/cml/base/isotope.rb +28 -9
- data/lib/chemicalml/cml/base/isotope_list.rb +17 -8
- data/lib/chemicalml/cml/base/join.rb +25 -9
- data/lib/chemicalml/cml/base/kpoint.rb +16 -7
- data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
- data/lib/chemicalml/cml/base/label.rb +14 -6
- data/lib/chemicalml/cml/base/lattice.rb +35 -10
- data/lib/chemicalml/cml/base/lattice_vector.rb +16 -7
- data/lib/chemicalml/cml/base/length.rb +26 -12
- data/lib/chemicalml/cml/base/line3.rb +18 -8
- data/lib/chemicalml/cml/base/link.rb +41 -20
- data/lib/chemicalml/cml/base/list.rb +22 -10
- data/lib/chemicalml/cml/base/map.rb +32 -11
- data/lib/chemicalml/cml/base/matrix.rb +32 -15
- data/lib/chemicalml/cml/base/mechanism.rb +20 -7
- data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
- data/lib/chemicalml/cml/base/metadata.rb +18 -8
- data/lib/chemicalml/cml/base/metadata_list.rb +27 -8
- data/lib/chemicalml/cml/base/molecule.rb +92 -26
- data/lib/chemicalml/cml/base/molecule_list.rb +26 -8
- data/lib/chemicalml/cml/base/name.rb +10 -4
- data/lib/chemicalml/cml/base/object.rb +18 -8
- data/lib/chemicalml/cml/base/observation.rb +16 -7
- data/lib/chemicalml/cml/base/operator.rb +14 -6
- data/lib/chemicalml/cml/base/parameter.rb +33 -13
- data/lib/chemicalml/cml/base/parameter_list.rb +27 -8
- data/lib/chemicalml/cml/base/particle.rb +20 -9
- data/lib/chemicalml/cml/base/peak.rb +73 -26
- data/lib/chemicalml/cml/base/peak_group.rb +62 -26
- data/lib/chemicalml/cml/base/peak_list.rb +24 -9
- data/lib/chemicalml/cml/base/peak_structure.rb +40 -13
- data/lib/chemicalml/cml/base/plane3.rb +14 -6
- data/lib/chemicalml/cml/base/point3.rb +14 -6
- data/lib/chemicalml/cml/base/potential.rb +17 -6
- data/lib/chemicalml/cml/base/potential_form.rb +30 -7
- data/lib/chemicalml/cml/base/potential_list.rb +15 -7
- data/lib/chemicalml/cml/base/product.rb +45 -10
- data/lib/chemicalml/cml/base/product_list.rb +29 -9
- data/lib/chemicalml/cml/base/property.rb +29 -11
- data/lib/chemicalml/cml/base/property_list.rb +23 -8
- data/lib/chemicalml/cml/base/reactant.rb +45 -10
- data/lib/chemicalml/cml/base/reactant_list.rb +29 -9
- data/lib/chemicalml/cml/base/reaction.rb +74 -20
- data/lib/chemicalml/cml/base/reaction_list.rb +23 -8
- data/lib/chemicalml/cml/base/reaction_scheme.rb +43 -13
- data/lib/chemicalml/cml/base/reaction_step.rb +30 -12
- data/lib/chemicalml/cml/base/reaction_step_list.rb +30 -11
- data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
- data/lib/chemicalml/cml/base/region.rb +20 -9
- data/lib/chemicalml/cml/base/related_entry.rb +11 -3
- data/lib/chemicalml/cml/base/sample.rb +30 -7
- data/lib/chemicalml/cml/base/scalar.rb +32 -15
- data/lib/chemicalml/cml/base/spectator.rb +25 -6
- data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
- data/lib/chemicalml/cml/base/spectrum.rb +57 -18
- data/lib/chemicalml/cml/base/spectrum_data.rb +19 -9
- data/lib/chemicalml/cml/base/spectrum_list.rb +28 -9
- data/lib/chemicalml/cml/base/sphere3.rb +14 -6
- data/lib/chemicalml/cml/base/stmml.rb +12 -5
- data/lib/chemicalml/cml/base/string.rb +14 -6
- data/lib/chemicalml/cml/base/string_array.rb +22 -10
- data/lib/chemicalml/cml/base/substance.rb +36 -14
- data/lib/chemicalml/cml/base/substance_list.rb +29 -10
- data/lib/chemicalml/cml/base/symmetry.rb +31 -9
- data/lib/chemicalml/cml/base/system.rb +21 -10
- data/lib/chemicalml/cml/base/table.rb +38 -13
- data/lib/chemicalml/cml/base/table_cell.rb +12 -5
- data/lib/chemicalml/cml/base/table_content.rb +19 -9
- data/lib/chemicalml/cml/base/table_header.rb +15 -7
- data/lib/chemicalml/cml/base/table_header_cell.rb +22 -10
- data/lib/chemicalml/cml/base/table_row.rb +15 -7
- data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
- data/lib/chemicalml/cml/base/tcell.rb +16 -7
- data/lib/chemicalml/cml/base/torsion.rb +28 -13
- data/lib/chemicalml/cml/base/transform3.rb +12 -5
- data/lib/chemicalml/cml/base/transition_state.rb +23 -5
- data/lib/chemicalml/cml/base/trow.rb +22 -6
- data/lib/chemicalml/cml/base/unit.rb +39 -16
- data/lib/chemicalml/cml/base/unit_list.rb +34 -12
- data/lib/chemicalml/cml/base/unit_type.rb +30 -10
- data/lib/chemicalml/cml/base/unit_type_list.rb +29 -11
- data/lib/chemicalml/cml/base/vector3.rb +14 -6
- data/lib/chemicalml/cml/base/xaxis.rb +32 -10
- data/lib/chemicalml/cml/base/yaxis.rb +30 -10
- data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
- data/lib/chemicalml/cml/base.rb +139 -138
- data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
- data/lib/chemicalml/cml/elements.rb +145 -136
- data/lib/chemicalml/cml/enums.rb +55 -0
- data/lib/chemicalml/cml/namespace.rb +2 -3
- data/lib/chemicalml/cml/patterns.rb +33 -0
- data/lib/chemicalml/cml/reference_resolver.rb +137 -0
- data/lib/chemicalml/cml/role.rb +138 -138
- data/lib/chemicalml/cml/schema24/configuration.rb +11 -1
- data/lib/chemicalml/cml/schema24.rb +139 -139
- data/lib/chemicalml/cml/schema3.rb +123 -123
- data/lib/chemicalml/cml/visitable.rb +77 -3
- data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
- data/lib/chemicalml/cml/xhtml/p.rb +21 -0
- data/lib/chemicalml/cml/xhtml.rb +20 -0
- data/lib/chemicalml/cml.rb +164 -129
- data/lib/chemicalml/context_configuration.rb +4 -2
- data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
- data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
- data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
- data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
- data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
- data/lib/chemicalml/convention/cascade.rb +43 -0
- data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +2 -1
- data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
- data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
- data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +1 -0
- data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +1 -0
- data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +1 -0
- data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +1 -1
- data/lib/chemicalml/convention/compchem/constraints.rb +4 -0
- data/lib/chemicalml/convention/compchem.rb +4 -2
- data/lib/chemicalml/convention/constraint.rb +40 -9
- data/lib/chemicalml/convention/coordinator.rb +4 -3
- data/lib/chemicalml/convention/detection.rb +10 -4
- data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +1 -0
- data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +15 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +1 -0
- data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +1 -1
- data/lib/chemicalml/convention/dictionary/constraints.rb +8 -8
- data/lib/chemicalml/convention/dictionary.rb +2 -2
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +15 -6
- data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +3 -2
- data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +2 -1
- data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +2 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +1 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
- data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
- data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
- data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +1 -1
- data/lib/chemicalml/convention/molecular/constraints.rb +49 -23
- data/lib/chemicalml/convention/molecular.rb +15 -2
- data/lib/chemicalml/convention/registry.rb +79 -11
- data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
- data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
- data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
- data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
- data/lib/chemicalml/convention/simple_unit.rb +37 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
- data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
- data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
- data/lib/chemicalml/convention/spectroscopy.rb +41 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +1 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +1 -1
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +1 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +1 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +1 -1
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
- data/lib/chemicalml/convention/unit_dictionary/constraints.rb +10 -0
- data/lib/chemicalml/convention/unit_dictionary.rb +7 -2
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +1 -1
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +1 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +1 -1
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +1 -0
- data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +1 -1
- data/lib/chemicalml/convention/unit_type_dictionary.rb +2 -2
- data/lib/chemicalml/convention/validation_report.rb +30 -3
- data/lib/chemicalml/convention/violation.rb +9 -3
- data/lib/chemicalml/convention.rb +41 -14
- data/lib/chemicalml/dictionary.rb +13 -9
- data/lib/chemicalml/logger.rb +47 -0
- data/lib/chemicalml/schema/definition.rb +2 -2
- data/lib/chemicalml/schema.rb +7 -7
- data/lib/chemicalml/version.rb +1 -1
- data/lib/chemicalml/versioned_parser.rb +10 -10
- data/lib/chemicalml.rb +78 -15
- data/lib/tasks/reference_fetch.rake +30 -31
- data/lib/tasks/scrape_fixtures.rake +23 -24
- metadata +118 -5
data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb
CHANGED
|
@@ -7,9 +7,9 @@ module Chemicalml
|
|
|
7
7
|
# Every unitType MUST have an `id` and a `name` attribute per
|
|
8
8
|
# the unitType-dictionary convention.
|
|
9
9
|
class UnitTypeMustHaveIdAndName < Chemicalml::Convention::Constraint::NodeConstraint
|
|
10
|
+
self.description = 'Every unitType MUST have an `id` and a `name` attribute per the unitType-dictionary convention.'
|
|
10
11
|
applies_to Chemicalml::Cml::Role::UnitType
|
|
11
12
|
def check_node(node, path)
|
|
12
|
-
|
|
13
13
|
violations = []
|
|
14
14
|
if node.id.to_s.empty?
|
|
15
15
|
violations << violation(path: path.join('/'),
|
|
@@ -8,9 +8,9 @@ module Chemicalml
|
|
|
8
8
|
module UnitTypeDictionary
|
|
9
9
|
extend Base
|
|
10
10
|
|
|
11
|
-
autoload :Constraints,
|
|
11
|
+
autoload :Constraints, 'chemicalml/convention/unit_type_dictionary/constraints'
|
|
12
12
|
|
|
13
|
-
QNAME =
|
|
13
|
+
QNAME = 'convention:unitType-dictionary'
|
|
14
14
|
NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}unitType-dictionary".freeze
|
|
15
15
|
|
|
16
16
|
def self.qname
|
|
@@ -39,9 +39,7 @@ module Chemicalml
|
|
|
39
39
|
def +(other)
|
|
40
40
|
return other if other.nil?
|
|
41
41
|
|
|
42
|
-
unless other.is_a?(ValidationReport)
|
|
43
|
-
raise ArgumentError, "cannot combine ValidationReport with #{other.class}"
|
|
44
|
-
end
|
|
42
|
+
raise ArgumentError, "cannot combine ValidationReport with #{other.class}" unless other.is_a?(ValidationReport)
|
|
45
43
|
|
|
46
44
|
ValidationReport.new(violations + other.violations)
|
|
47
45
|
end
|
|
@@ -58,6 +56,35 @@ module Chemicalml
|
|
|
58
56
|
def to_s
|
|
59
57
|
"#{size} violation(s): #{errors.length} error(s), #{warnings.length} warning(s)"
|
|
60
58
|
end
|
|
59
|
+
|
|
60
|
+
# Human-readable multi-line summary. Suitable for CLI output
|
|
61
|
+
# and logging. Each violation is rendered with severity, path,
|
|
62
|
+
# and message (and value when present).
|
|
63
|
+
#
|
|
64
|
+
# @return [String]
|
|
65
|
+
def summary
|
|
66
|
+
return 'OK — no violations' if ok? && !has_warnings?
|
|
67
|
+
|
|
68
|
+
lines = []
|
|
69
|
+
lines << "Errors: #{errors.size}, Warnings: #{warnings.size}"
|
|
70
|
+
unless errors.empty?
|
|
71
|
+
lines << ''
|
|
72
|
+
lines << 'Errors:'
|
|
73
|
+
errors.each { |v| lines << " ERROR #{v.path}: #{v.message}#{value_suffix(v)}" }
|
|
74
|
+
end
|
|
75
|
+
unless warnings.empty?
|
|
76
|
+
lines << ''
|
|
77
|
+
lines << 'Warnings:'
|
|
78
|
+
warnings.each { |v| lines << " WARN #{v.path}: #{v.message}#{value_suffix(v)}" }
|
|
79
|
+
end
|
|
80
|
+
lines.join("\n")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def value_suffix(violation)
|
|
86
|
+
violation.value.nil? ? '' : " (value=#{violation.value.inspect})"
|
|
87
|
+
end
|
|
61
88
|
end
|
|
62
89
|
end
|
|
63
90
|
end
|
|
@@ -5,18 +5,23 @@ module Chemicalml
|
|
|
5
5
|
# Value object describing one constraint violation. Severity is
|
|
6
6
|
# either `:error` (document is invalid under the convention) or
|
|
7
7
|
# `:warning` (document is valid but problematic).
|
|
8
|
+
#
|
|
9
|
+
# `value` carries the offending value where natural (e.g. the
|
|
10
|
+
# duplicated atom id, the malformed unit symbol). Optional —
|
|
11
|
+
# constraints that flag a missing attribute leave `value` nil.
|
|
8
12
|
class Violation
|
|
9
13
|
SEVERITIES = %i[error warning].freeze
|
|
10
14
|
|
|
11
|
-
attr_reader :path, :message, :severity, :constraint
|
|
15
|
+
attr_reader :path, :message, :severity, :constraint, :value
|
|
12
16
|
|
|
13
|
-
def initialize(path:, message:, severity: :error, constraint: nil)
|
|
17
|
+
def initialize(path:, message:, severity: :error, constraint: nil, value: nil)
|
|
14
18
|
raise ArgumentError, "unknown severity: #{severity.inspect}" unless SEVERITIES.include?(severity.to_sym)
|
|
15
19
|
|
|
16
20
|
@path = path
|
|
17
21
|
@message = message
|
|
18
22
|
@severity = severity.to_sym
|
|
19
23
|
@constraint = constraint
|
|
24
|
+
@value = value
|
|
20
25
|
freeze
|
|
21
26
|
end
|
|
22
27
|
|
|
@@ -29,7 +34,8 @@ module Chemicalml
|
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
def to_s
|
|
32
|
-
|
|
37
|
+
suffix = value.nil? ? '' : " (value=#{value.inspect})"
|
|
38
|
+
"#{severity.upcase} #{path}: #{message}#{suffix}"
|
|
33
39
|
end
|
|
34
40
|
end
|
|
35
41
|
end
|
|
@@ -8,41 +8,68 @@ module Chemicalml
|
|
|
8
8
|
# convention owns its constraint classes; the framework walks
|
|
9
9
|
# whatever was registered.
|
|
10
10
|
#
|
|
11
|
-
# The
|
|
11
|
+
# The built-in conventions:
|
|
12
12
|
#
|
|
13
13
|
# molecular http://www.xml-cml.org/convention/molecular
|
|
14
14
|
# compchem http://www.xml-cml.org/convention/compchem
|
|
15
15
|
# dictionary http://www.xml-cml.org/convention/dictionary
|
|
16
16
|
# unit-dictionary http://www.xml-cml.org/convention/unit-dictionary
|
|
17
17
|
# unitType-dictionary http://www.xml-cml.org/convention/unitType-dictionary
|
|
18
|
+
# spectroscopy http://www.xml-cml.org/convention/spectroscopy
|
|
19
|
+
# cascade http://www.xml-cml.org/convention/cascade
|
|
20
|
+
# simpleUnit http://www.xml-cml.org/convention/simpleUnit
|
|
18
21
|
module Convention
|
|
19
|
-
autoload :Base,
|
|
20
|
-
autoload :
|
|
21
|
-
autoload :
|
|
22
|
-
autoload :
|
|
23
|
-
autoload :
|
|
24
|
-
autoload :
|
|
25
|
-
autoload :
|
|
26
|
-
autoload :
|
|
27
|
-
autoload :
|
|
28
|
-
autoload :
|
|
29
|
-
autoload :
|
|
30
|
-
autoload :
|
|
22
|
+
autoload :Base, 'chemicalml/convention/base'
|
|
23
|
+
autoload :Cascade, 'chemicalml/convention/cascade'
|
|
24
|
+
autoload :Compchem, 'chemicalml/convention/compchem'
|
|
25
|
+
autoload :Constraint, 'chemicalml/convention/constraint'
|
|
26
|
+
autoload :Coordinator, 'chemicalml/convention/coordinator'
|
|
27
|
+
autoload :Detection, 'chemicalml/convention/detection'
|
|
28
|
+
autoload :Dictionary, 'chemicalml/convention/dictionary'
|
|
29
|
+
autoload :Molecular, 'chemicalml/convention/molecular'
|
|
30
|
+
autoload :Registry, 'chemicalml/convention/registry'
|
|
31
|
+
autoload :SimpleUnit, 'chemicalml/convention/simple_unit'
|
|
32
|
+
autoload :Spectroscopy, 'chemicalml/convention/spectroscopy'
|
|
33
|
+
autoload :UnitDictionary, 'chemicalml/convention/unit_dictionary'
|
|
34
|
+
autoload :UnitTypeDictionary, 'chemicalml/convention/unit_type_dictionary'
|
|
35
|
+
autoload :ValidationReport, 'chemicalml/convention/validation_report'
|
|
36
|
+
autoload :Violation, 'chemicalml/convention/violation'
|
|
31
37
|
|
|
32
|
-
CONVENTION_NAMESPACE =
|
|
38
|
+
CONVENTION_NAMESPACE = 'http://www.xml-cml.org/convention/'
|
|
33
39
|
|
|
40
|
+
# Look up a convention by QName (e.g. "convention:molecular").
|
|
41
|
+
# @param qname [String] the convention QName.
|
|
42
|
+
# @return [Module, nil] the convention module, or nil if unknown.
|
|
34
43
|
def self.lookup(qname)
|
|
35
44
|
Registry.lookup(qname)
|
|
36
45
|
end
|
|
37
46
|
|
|
47
|
+
# Validate a document against a named convention. Returns the
|
|
48
|
+
# raw violation list.
|
|
49
|
+
#
|
|
50
|
+
# @param document [Lutaml::Model::Serializable] the CML document.
|
|
51
|
+
# @param qname [String] the convention QName (e.g. "convention:molecular").
|
|
52
|
+
# @return [Array<Chemicalml::Convention::Violation>] the violations.
|
|
53
|
+
# @raise [ArgumentError] if the qname is not registered.
|
|
38
54
|
def self.validate(document, qname:)
|
|
39
55
|
Registry.validate(document, qname: qname)
|
|
40
56
|
end
|
|
41
57
|
|
|
58
|
+
# Validate and return a structured report (errors + warnings).
|
|
59
|
+
#
|
|
60
|
+
# @param document [Lutaml::Model::Serializable] the CML document.
|
|
61
|
+
# @param qname [String] the convention QName.
|
|
62
|
+
# @return [Chemicalml::Convention::ValidationReport] the report.
|
|
42
63
|
def self.validate_report(document, qname:)
|
|
43
64
|
Registry.validate_report(document, qname: qname)
|
|
44
65
|
end
|
|
45
66
|
|
|
67
|
+
# Auto-detect the convention from the document root's `convention`
|
|
68
|
+
# attribute and validate.
|
|
69
|
+
#
|
|
70
|
+
# @param document [Lutaml::Model::Serializable] the CML document.
|
|
71
|
+
# @return [Chemicalml::Convention::ValidationReport] the report.
|
|
72
|
+
# @raise [ArgumentError] if no convention is declared.
|
|
46
73
|
def self.detect_and_validate(document)
|
|
47
74
|
Registry.detect_and_validate(document)
|
|
48
75
|
end
|
|
@@ -8,17 +8,21 @@ module Chemicalml
|
|
|
8
8
|
# Built-in dictionaries ship as YAML under `data/dictionaries/` and
|
|
9
9
|
# are loaded into `Chemicalml::Dictionary::Registry` lazily.
|
|
10
10
|
module Dictionary
|
|
11
|
-
autoload :Entry,
|
|
12
|
-
autoload :Enum,
|
|
13
|
-
autoload :Link,
|
|
14
|
-
autoload :Loader,
|
|
15
|
-
autoload :Model,
|
|
16
|
-
autoload :Registry,
|
|
11
|
+
autoload :Entry, 'chemicalml/dictionary/entry'
|
|
12
|
+
autoload :Enum, 'chemicalml/dictionary/enum'
|
|
13
|
+
autoload :Link, 'chemicalml/dictionary/link'
|
|
14
|
+
autoload :Loader, 'chemicalml/dictionary/loader'
|
|
15
|
+
autoload :Model, 'chemicalml/dictionary/model'
|
|
16
|
+
autoload :Registry, 'chemicalml/dictionary/registry'
|
|
17
17
|
|
|
18
|
-
BUILTIN_DIR = File.expand_path(
|
|
18
|
+
BUILTIN_DIR = File.expand_path('../../data/dictionaries', __dir__).freeze
|
|
19
19
|
|
|
20
|
-
# Convenience: load a built-in dictionary by short name
|
|
21
|
-
#
|
|
20
|
+
# Convenience: load a built-in dictionary by short name.
|
|
21
|
+
#
|
|
22
|
+
# @param name [Symbol, String] the dictionary short name (YAML
|
|
23
|
+
# filename stem, e.g. `:compchem`, `:cml`).
|
|
24
|
+
# @return [Chemicalml::Dictionary::Model] the loaded dictionary.
|
|
25
|
+
# @raise [KeyError] if `name` is not a built-in dictionary.
|
|
22
26
|
def self.load(name)
|
|
23
27
|
Registry.load_builtin(name)
|
|
24
28
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
module Chemicalml
|
|
6
|
+
# Logger used across the CLI and API. Wraps Ruby's ::Logger and can
|
|
7
|
+
# bridge to Thor's `say` / `say_error` when running under a Thor
|
|
8
|
+
# dispatcher — that gives coloured output on the terminal while
|
|
9
|
+
# plain `Logger.new($stderr)` works for library callers.
|
|
10
|
+
#
|
|
11
|
+
# Severity routing under Thor:
|
|
12
|
+
# debug → suppressed (set level manually if needed)
|
|
13
|
+
# info → say (green-ish, $stdout)
|
|
14
|
+
# warn → say_error with :yellow ($stderr)
|
|
15
|
+
# error → say_error with :red ($stderr)
|
|
16
|
+
# fatal → say_error with :red ($stderr)
|
|
17
|
+
class Logger < ::Logger
|
|
18
|
+
attr_reader :thor_shell
|
|
19
|
+
|
|
20
|
+
# Bridge to a Thor shell instance (self inside a Thor command).
|
|
21
|
+
# When set, info/warn/error route through Thor for coloured output.
|
|
22
|
+
def thor_shell=(shell)
|
|
23
|
+
@thor_shell = shell
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add(severity, message = nil, progname = nil)
|
|
27
|
+
return super unless thor_shell
|
|
28
|
+
return true if severity < level
|
|
29
|
+
|
|
30
|
+
msg = message || progname
|
|
31
|
+
case severity
|
|
32
|
+
when ERROR, FATAL
|
|
33
|
+
thor_shell.say_error(msg.to_s, :red)
|
|
34
|
+
when WARN
|
|
35
|
+
thor_shell.say_error(msg.to_s, :yellow)
|
|
36
|
+
else
|
|
37
|
+
thor_shell.say(msg.to_s)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Convenience: create a default logger that writes to $stderr at
|
|
42
|
+
# INFO level. Used when no logger is explicitly provided.
|
|
43
|
+
def self.default
|
|
44
|
+
new($stderr).tap { |l| l.level = INFO }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -19,7 +19,7 @@ module Chemicalml
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def wire_namespace_constant
|
|
22
|
-
ruby_namespace.split(
|
|
22
|
+
ruby_namespace.split('::').reduce(Object) { |ns, name| ns.const_get(name) }
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def eql?(other)
|
|
@@ -34,7 +34,7 @@ module Chemicalml
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
36
|
def project_root
|
|
37
|
-
File.expand_path(
|
|
37
|
+
File.expand_path('../../..', __dir__)
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
end
|
data/lib/chemicalml/schema.rb
CHANGED
|
@@ -9,10 +9,10 @@ module Chemicalml
|
|
|
9
9
|
# `reference-docs/schemas/`. They are never modified or regenerated
|
|
10
10
|
# from code.
|
|
11
11
|
module Schema
|
|
12
|
-
autoload :Definition,
|
|
13
|
-
autoload :Registry,
|
|
12
|
+
autoload :Definition, 'chemicalml/schema/definition'
|
|
13
|
+
autoload :Registry, 'chemicalml/schema/registry'
|
|
14
14
|
|
|
15
|
-
CML_XML_NAMESPACE =
|
|
15
|
+
CML_XML_NAMESPACE = 'http://www.xml-cml.org/schema'
|
|
16
16
|
|
|
17
17
|
# Built-in versions registered at load time. Adding a new CML schema
|
|
18
18
|
# version = appending an entry here and creating the matching
|
|
@@ -20,15 +20,15 @@ module Chemicalml
|
|
|
20
20
|
BUILTIN = {
|
|
21
21
|
schema24: Definition.new(
|
|
22
22
|
version: :schema24,
|
|
23
|
-
xsd_path:
|
|
23
|
+
xsd_path: 'reference-docs/schemas/schema24/schema.xsd',
|
|
24
24
|
xml_namespace: CML_XML_NAMESPACE,
|
|
25
|
-
ruby_namespace:
|
|
25
|
+
ruby_namespace: 'Chemicalml::Cml::Schema24'
|
|
26
26
|
),
|
|
27
27
|
schema3: Definition.new(
|
|
28
28
|
version: :schema3,
|
|
29
|
-
xsd_path:
|
|
29
|
+
xsd_path: 'reference-docs/schemas/schema3/schema.xsd',
|
|
30
30
|
xml_namespace: CML_XML_NAMESPACE,
|
|
31
|
-
ruby_namespace:
|
|
31
|
+
ruby_namespace: 'Chemicalml::Cml::Schema3'
|
|
32
32
|
)
|
|
33
33
|
}.freeze
|
|
34
34
|
end
|
data/lib/chemicalml/version.rb
CHANGED
|
@@ -32,14 +32,14 @@ module Chemicalml
|
|
|
32
32
|
# Adding a new root type = adding one entry here. OCP — no
|
|
33
33
|
# existing code changes.
|
|
34
34
|
KNOWN_ROOTS = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
'cml' => :Document,
|
|
36
|
+
'molecule' => :Molecule,
|
|
37
|
+
'reaction' => :Reaction,
|
|
38
|
+
'reactionList' => :ReactionList,
|
|
39
|
+
'module' => :Module,
|
|
40
|
+
'dictionary' => :Dictionary,
|
|
41
|
+
'unitList' => :UnitList,
|
|
42
|
+
'unitTypeList' => :UnitTypeList
|
|
43
43
|
}.freeze
|
|
44
44
|
|
|
45
45
|
def parse(xml, namespace_exist: true)
|
|
@@ -57,7 +57,7 @@ module Chemicalml
|
|
|
57
57
|
unless const_defined?(class_name, false)
|
|
58
58
|
raise ArgumentError,
|
|
59
59
|
"#{name} does not define #{class_name} " \
|
|
60
|
-
|
|
60
|
+
'(not all root elements exist in every schema version)'
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
const_get(class_name).from_xml(
|
|
@@ -83,7 +83,7 @@ module Chemicalml
|
|
|
83
83
|
match = xml.match(/<([a-zA-Z][\w.-]*)(?::[a-zA-Z][\w.-]*)?/)
|
|
84
84
|
return match[1] if match
|
|
85
85
|
|
|
86
|
-
raise ArgumentError,
|
|
86
|
+
raise ArgumentError, 'could not detect root element in XML'
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def xml_input(xml, namespace_exist, root)
|
data/lib/chemicalml.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'lutaml/model'
|
|
4
4
|
|
|
5
5
|
# Chemicalml provides a Ruby object model for the Chemical Markup Language
|
|
6
6
|
# (CML).
|
|
@@ -13,36 +13,99 @@ require "lutaml/model"
|
|
|
13
13
|
# declared attributes and an XML mapping block. Serialization is
|
|
14
14
|
# framework-backed — no hand-rolled XML in this library.
|
|
15
15
|
module Chemicalml
|
|
16
|
-
autoload :Cml,
|
|
17
|
-
autoload :
|
|
18
|
-
autoload :
|
|
19
|
-
autoload :
|
|
20
|
-
autoload :
|
|
21
|
-
autoload :
|
|
22
|
-
autoload :
|
|
23
|
-
autoload :
|
|
24
|
-
autoload :
|
|
16
|
+
autoload :Cml, 'chemicalml/cml'
|
|
17
|
+
autoload :Cli, 'chemicalml/cli'
|
|
18
|
+
autoload :ContextConfiguration, 'chemicalml/context_configuration'
|
|
19
|
+
autoload :Convention, 'chemicalml/convention'
|
|
20
|
+
autoload :Dictionary, 'chemicalml/dictionary'
|
|
21
|
+
autoload :Error, 'chemicalml/errors'
|
|
22
|
+
autoload :Logger, 'chemicalml/logger'
|
|
23
|
+
autoload :ParseError, 'chemicalml/errors'
|
|
24
|
+
autoload :Schema, 'chemicalml/schema'
|
|
25
|
+
autoload :VERSION, 'chemicalml/version'
|
|
26
|
+
autoload :VersionedParser, 'chemicalml/versioned_parser'
|
|
27
|
+
autoload :Dictionary, 'chemicalml/dictionary'
|
|
28
|
+
autoload :Error, 'chemicalml/errors'
|
|
29
|
+
autoload :ParseError, 'chemicalml/errors'
|
|
30
|
+
autoload :Schema, 'chemicalml/schema'
|
|
31
|
+
autoload :VERSION, 'chemicalml/version'
|
|
32
|
+
autoload :VersionedParser, 'chemicalml/versioned_parser'
|
|
25
33
|
|
|
26
34
|
module_function
|
|
27
35
|
|
|
36
|
+
# Parse a CML XML string into a wire document.
|
|
37
|
+
#
|
|
38
|
+
# @param xml [String] the XML content.
|
|
39
|
+
# @param schema [Symbol] one of `:schema3` (default) or `:schema24`.
|
|
40
|
+
# @param namespace_exist [Boolean] whether the XML already declares
|
|
41
|
+
# the CML namespace. When false, the parser injects it.
|
|
42
|
+
# @return [Chemicalml::Cml::Schema3::Document,
|
|
43
|
+
# Chemicalml::Cml::Schema24::Document,
|
|
44
|
+
# Chemicalml::Cml::Schema3::Molecule, ...] the parsed root,
|
|
45
|
+
# dispatch by detected root element.
|
|
46
|
+
# @raise [ArgumentError] if `xml` is nil/empty or `schema` is unknown.
|
|
47
|
+
# @raise [Chemicalml::ParseError] on malformed XML.
|
|
48
|
+
#
|
|
49
|
+
# @example Parse a Schema3 document
|
|
50
|
+
# doc = Chemicalml.parse("<cml xmlns='...'>...</cml>")
|
|
51
|
+
# @example Parse a Schema2.4 document
|
|
52
|
+
# doc = Chemicalml.parse(xml, schema: :schema24)
|
|
28
53
|
def parse(xml, schema: :schema3, namespace_exist: true)
|
|
29
|
-
raise ArgumentError,
|
|
30
|
-
raise ArgumentError,
|
|
54
|
+
raise ArgumentError, 'xml must not be nil' if xml.nil?
|
|
55
|
+
raise ArgumentError, 'xml must not be empty' if xml.to_s.strip.empty?
|
|
31
56
|
|
|
32
57
|
parser_for(schema).parse(xml, namespace_exist: namespace_exist)
|
|
33
58
|
end
|
|
34
59
|
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
# Parse a CML file from disk. Convenience wrapper around `parse`
|
|
61
|
+
# that handles the `File.read` boilerplate.
|
|
62
|
+
#
|
|
63
|
+
# @param path [String] path to the CML file.
|
|
64
|
+
# @param schema [Symbol] `:schema3` (default) or `:schema24`.
|
|
65
|
+
# @param namespace_exist [Boolean] forwarded to `parse`.
|
|
66
|
+
# @return (see .parse)
|
|
67
|
+
# @raise [ArgumentError] if the file doesn't exist or is empty.
|
|
68
|
+
def parse_file(path, schema: :schema3, namespace_exist: true)
|
|
69
|
+
raise ArgumentError, "file not found: #{path}" unless File.exist?(path)
|
|
70
|
+
|
|
71
|
+
parse(File.read(path), schema: schema, namespace_exist: namespace_exist)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Serialize a wire document back to XML.
|
|
75
|
+
#
|
|
76
|
+
# @param document [Lutaml::Model::Serializable] a CML wire instance.
|
|
77
|
+
# @param opts [Hash] forwarded to `Lutaml::Model::Serializable#to_xml`.
|
|
78
|
+
# @return [String] the serialized XML.
|
|
79
|
+
def serialize(document, **)
|
|
80
|
+
document.to_xml(**)
|
|
37
81
|
end
|
|
38
82
|
|
|
83
|
+
# Resolve the versioned-parser module for a schema id.
|
|
84
|
+
#
|
|
85
|
+
# @param schema [Symbol] `:schema3` or `:schema24`.
|
|
86
|
+
# @return [Module] the matching `Schema3` or `Schema24` module.
|
|
87
|
+
# @raise [ArgumentError] if `schema` is unknown.
|
|
39
88
|
def parser_for(schema)
|
|
40
89
|
case schema.to_sym
|
|
41
90
|
when :schema3 then Chemicalml::Cml::Schema3
|
|
42
91
|
when :schema24 then Chemicalml::Cml::Schema24
|
|
43
92
|
else
|
|
44
93
|
raise ArgumentError, "unsupported schema: #{schema.inspect} " \
|
|
45
|
-
|
|
94
|
+
'(supported: :schema3, :schema24)'
|
|
46
95
|
end
|
|
47
96
|
end
|
|
97
|
+
|
|
98
|
+
# Auto-detect the convention from the document's root and validate.
|
|
99
|
+
# Top-level convenience for `Convention.detect_and_validate`.
|
|
100
|
+
#
|
|
101
|
+
# @param document [Lutaml::Model::Serializable] the CML document.
|
|
102
|
+
# @param logger [Chemicalml::Logger, nil] optional logger for progress.
|
|
103
|
+
# @return [Chemicalml::Convention::ValidationReport] the report.
|
|
104
|
+
# @raise [ArgumentError] if no convention is declared.
|
|
105
|
+
def validate(document, logger: nil)
|
|
106
|
+
logger&.info 'Auto-detecting convention…'
|
|
107
|
+
report = Convention.detect_and_validate(document)
|
|
108
|
+
logger&.info "Validation complete: #{report.size} violation(s)"
|
|
109
|
+
report
|
|
110
|
+
end
|
|
48
111
|
end
|
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'fileutils'
|
|
6
6
|
|
|
7
7
|
# Idempotent fetcher for xml-cml.org reference material.
|
|
8
8
|
# Re-running this task only downloads files that are missing locally.
|
|
9
9
|
module ReferenceFetch
|
|
10
|
-
BASE =
|
|
11
|
-
LICENSE =
|
|
10
|
+
BASE = 'http://www.xml-cml.org'
|
|
11
|
+
LICENSE = 'Source: <url>. Distributed by xml-cml.org under CC-BY-3.0.'
|
|
12
12
|
|
|
13
13
|
# Map of remote URL => local path under reference-docs/.
|
|
14
14
|
SOURCES = {
|
|
15
15
|
# Conventions
|
|
16
|
-
"#{BASE}/convention/"
|
|
17
|
-
"#{BASE}/convention/molecular"
|
|
18
|
-
"#{BASE}/convention/compchem"
|
|
19
|
-
"#{BASE}/convention/dictionary"
|
|
20
|
-
"#{BASE}/convention/unit-dictionary"
|
|
21
|
-
"#{BASE}/convention/unitType-dictionary"
|
|
16
|
+
"#{BASE}/convention/" => 'reference-docs/conventions/index.md',
|
|
17
|
+
"#{BASE}/convention/molecular" => 'reference-docs/conventions/molecular.md',
|
|
18
|
+
"#{BASE}/convention/compchem" => 'reference-docs/conventions/compchem.md',
|
|
19
|
+
"#{BASE}/convention/dictionary" => 'reference-docs/conventions/dictionary.md',
|
|
20
|
+
"#{BASE}/convention/unit-dictionary" => 'reference-docs/conventions/unit-dictionary.md',
|
|
21
|
+
"#{BASE}/convention/unitType-dictionary" => 'reference-docs/conventions/unitType-dictionary.md',
|
|
22
22
|
# Dictionaries
|
|
23
|
-
"#{BASE}/dictionary/"
|
|
24
|
-
"#{BASE}/dictionary/compchem/"
|
|
25
|
-
"#{BASE}/dictionary/cml/"
|
|
26
|
-
"#{BASE}/dictionary/cml/name/"
|
|
27
|
-
"#{BASE}/dictionary/cml/formula/"
|
|
28
|
-
"#{BASE}/dictionary/cif/"
|
|
23
|
+
"#{BASE}/dictionary/" => 'reference-docs/dictionaries/index.md',
|
|
24
|
+
"#{BASE}/dictionary/compchem/" => 'reference-docs/dictionaries/compchem.md',
|
|
25
|
+
"#{BASE}/dictionary/cml/" => 'reference-docs/dictionaries/cml.md',
|
|
26
|
+
"#{BASE}/dictionary/cml/name/" => 'reference-docs/dictionaries/cml-name.md',
|
|
27
|
+
"#{BASE}/dictionary/cml/formula/" => 'reference-docs/dictionaries/cml-formula.md',
|
|
28
|
+
"#{BASE}/dictionary/cif/" => 'reference-docs/dictionaries/cif.md',
|
|
29
29
|
# Unit dictionaries
|
|
30
|
-
"#{BASE}/unit/si/"
|
|
31
|
-
"#{BASE}/unit/nonSi/"
|
|
32
|
-
"#{BASE}/unit/unitType/"
|
|
30
|
+
"#{BASE}/unit/si/" => 'reference-docs/dictionaries/unit-si.md',
|
|
31
|
+
"#{BASE}/unit/nonSi/" => 'reference-docs/dictionaries/unit-nonSi.md',
|
|
32
|
+
"#{BASE}/unit/unitType/" => 'reference-docs/dictionaries/unit-type.md',
|
|
33
33
|
# Schema / spec
|
|
34
|
-
|
|
34
|
+
'https://www.xml-cml.org/schema/cmllite.html' => 'reference-docs/cmllite.html',
|
|
35
35
|
# Examples
|
|
36
|
-
"#{BASE}/examples/schema3/molecular/"
|
|
37
|
-
"#{BASE}/examples/schema3/compchem/"
|
|
38
|
-
"#{BASE}/examples/schema24/"
|
|
36
|
+
"#{BASE}/examples/schema3/molecular/" => 'reference-docs/examples/schema3-molecular-index.md',
|
|
37
|
+
"#{BASE}/examples/schema3/compchem/" => 'reference-docs/examples/schema3-compchem-index.md',
|
|
38
|
+
"#{BASE}/examples/schema24/" => 'reference-docs/examples/schema24-index.md'
|
|
39
39
|
}.freeze
|
|
40
40
|
|
|
41
41
|
def self.fetch_all
|
|
@@ -60,7 +60,7 @@ module ReferenceFetch
|
|
|
60
60
|
return
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
if path.end_with?(
|
|
63
|
+
if path.end_with?('.html')
|
|
64
64
|
File.write(full, body)
|
|
65
65
|
else
|
|
66
66
|
File.write(full, header(url) + body)
|
|
@@ -73,19 +73,18 @@ module ReferenceFetch
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def self.http_get(url, limit = 5)
|
|
76
|
-
raise
|
|
76
|
+
raise 'too many redirects' if limit.zero?
|
|
77
77
|
|
|
78
78
|
uri = URI(url)
|
|
79
79
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
80
80
|
http.use_ssl = true if uri.is_a?(URI::HTTPS)
|
|
81
81
|
http.read_timeout = 30
|
|
82
|
-
req = Net::HTTP::Get.new(uri,
|
|
82
|
+
req = Net::HTTP::Get.new(uri, 'User-Agent' => 'chemicalml-gem-reference-fetch')
|
|
83
83
|
res = http.request(req)
|
|
84
84
|
|
|
85
85
|
case res
|
|
86
86
|
when Net::HTTPSuccess then res.body
|
|
87
|
-
when Net::HTTPRedirection then http_get(res[
|
|
88
|
-
else nil
|
|
87
|
+
when Net::HTTPRedirection then http_get(res['location'], limit - 1)
|
|
89
88
|
end
|
|
90
89
|
rescue StandardError => e
|
|
91
90
|
warn " error fetching #{url}: #{e.message}"
|
|
@@ -93,12 +92,12 @@ module ReferenceFetch
|
|
|
93
92
|
end
|
|
94
93
|
|
|
95
94
|
def self.project_root
|
|
96
|
-
File.expand_path(
|
|
95
|
+
File.expand_path('..', __dir__)
|
|
97
96
|
end
|
|
98
97
|
end
|
|
99
98
|
|
|
100
99
|
namespace :reference do
|
|
101
|
-
desc
|
|
100
|
+
desc 'Download all xml-cml.org reference material into reference-docs/. Idempotent.'
|
|
102
101
|
task :fetch do
|
|
103
102
|
ReferenceFetch.fetch_all
|
|
104
103
|
end
|