Repository: Materials-Consortia/optimade-python-tools

Issue:
`chemical_formula_anonymous` validator accepts incorrect proportion order if started with 1
As reported by @JPBergsma in https://github.com/Materials-Consortia/optimade-python-tools/pull/1001#issue-1059491669, the `chemical_formula_anonymous` validator has an edge case that allows the wrong order of elements for certain compositions (those with only two different proportions, e.g. 1 and 4), if the first element has proportion 1, e.g. `"AB4"` does not raise an error as it should.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia) Report
> Merging [#1001](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia) (f1054e6) into [master](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/commit/03b60f9f313db87daf5b76192a426588072a79e2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia) (03b60f9) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001/graphs/tree.svg?width=650&height=150&src=pr&token=UJAtmqkZZO&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia)](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia)

```diff
@@           Coverage Diff           @@
##           master    #1001   +/-   ##
=======================================
  Coverage   92.91%   92.91%           
=======================================
  Files          67       67           
  Lines        3781     3782    +1     
=======================================
+ Hits         3513     3514    +1     
  Misses        268      268           
```

| Flag | Coverage Δ | |
|---|---|---|
| project | `92.91% <100.00%> (+<0.01%)` | :arrow_up: |
| validator | `92.91% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia) | Coverage Δ | |
|---|---|---|
| [optimade/models/structures.py](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia#diff-b3B0aW1hZGUvbW9kZWxzL3N0cnVjdHVyZXMucHk=) | `95.75% <100.00%> (+0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia). Last update [03b60f9...f1054e6](https://codecov.io/gh/Materials-Consortia/optimade-python-tools/pull/1001?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Materials-Consortia).

ml-evs: Could you raise an issue briefly explaining this and then close it with this PR? Just for the sake of the changelog 😅 
ml-evs: > Could you raise an issue briefly explaining this and then close it with this PR? Just for the sake of the changelog 😅

Done, will merge.

Patch:
diff --git a/optimade/models/structures.py b/optimade/models/structures.py
index 62a39f46..1ef9ae57 100644
--- a/optimade/models/structures.py
+++ b/optimade/models/structures.py
@@ -872,7 +872,8 @@ The properties of the species are found in the property `species`.
             return v
 
         elements = tuple(re.findall(r"[A-Z][a-z]*", v))
-        numbers = [int(n.strip()) for n in re.split(r"[A-Z][a-z]*", v) if n.strip()]
+        numbers = re.split(r"[A-Z][a-z]*", v)[1:]
+        numbers = [int(i) if i else 1 for i in numbers]
 
         expected_labels = ANONYMOUS_ELEMENTS[: len(elements)]
         expected_numbers = sorted(numbers, reverse=True)


Test patch:
diff --git a/tests/models/test_structures.py b/tests/models/test_structures.py
index 09e9a626..e1401e55 100644
--- a/tests/models/test_structures.py
+++ b/tests/models/test_structures.py
@@ -103,8 +103,8 @@ deformities = (
         "'chemical_formula_anonymous' A2B90 has wrong order: elements with highest proportion should appear first: [2, 90] vs expected [90, 2]",
     ),
     (
-        {"chemical_formula_anonymous": "A2B10"},
-        "'chemical_formula_anonymous' A2B10 has wrong order: elements with highest proportion should appear first: [2, 10] vs expected [10, 2]",
+        {"chemical_formula_anonymous": "AB10"},
+        "'chemical_formula_anonymous' AB10 has wrong order: elements with highest proportion should appear first: [1, 10] vs expected [10, 1]",
     ),
     (
         {"chemical_formula_hill": "SiGe"},