Skip to content

Commit 79a645b

Browse files
committed
fix: read the uuid from a file page title with suffixes
- merge the two get_uuid copies into osw.utils.wiki.get_uuid - OSW.get_uuid delegates to it instead of holding a second copy - ignore the OSW prefix and any number of file suffixes via regex - reject a string that is not an OSW-ID instead of parsing it partly
1 parent eb9e0e1 commit 79a645b

3 files changed

Lines changed: 83 additions & 9 deletions

File tree

‎src/osw/core.py‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@
5959
get_full_title,
6060
get_namespace,
6161
get_title,
62-
get_uuid,
6362
is_empty,
6463
namespace_from_full_title,
6564
remove_empty,
6665
title_from_full_title,
6766
)
67+
from osw.utils.wiki import (
68+
get_uuid as get_uuid_from_osw_id,
69+
)
6870
from osw.wiki_tools import SearchParam
6971
from osw.wtsite import WtPage, WtSite
7072

@@ -197,18 +199,21 @@ def get_osw_id(uuid: Union[str, UUID]) -> str:
197199

198200
@staticmethod
199201
def get_uuid(osw_id: str) -> UUID:
200-
"""Returns the uuid for a given OSW-ID
202+
"""Returns the uuid for a given OSW-ID. Kept for backwards compatibility,
203+
the implementation lives in osw.utils.wiki.get_uuid()
201204
202205
Parameters
203206
----------
204207
osw_id
205-
OSW-ID string, e.g. OSW2ea5b605c91f4e5a95593dff79fdd4a5
208+
OSW-ID string, e.g. OSW2ea5b605c91f4e5a95593dff79fdd4a5, with or
209+
without file suffixes, e.g.
210+
OSW2ea5b605c91f4e5a95593dff79fdd4a5.drawio.png
206211
207212
Returns
208213
-------
209214
uuid object, e.g. UUID("2ea5b605-c91f-4e5a-9559-3dff79fdd4a5")
210215
"""
211-
return UUID(osw_id.replace("OSW", ""))
216+
return get_uuid_from_osw_id(osw_id)
212217

213218
class SortEntitiesResult(OswBaseModel):
214219
by_name: Dict[str, List[OswBaseModel]]
@@ -1348,7 +1353,7 @@ def validate_entity(cls, entity, values):
13481353
if jsondata is None: # Guard clause
13491354
title = title_from_full_title(page.title)
13501355
try:
1351-
uuid_from_title = get_uuid(title)
1356+
uuid_from_title = get_uuid_from_osw_id(title)
13521357
except ValueError:
13531358
print(
13541359
f"Error: UUID could not be determined from title: '{title}', "

‎src/osw/utils/wiki.py‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1+
import re
12
from copy import deepcopy
23
from uuid import UUID
34

45
# Legacy imports:
56
from opensemantic.v1 import get_full_title, get_namespace, get_title # noqa: F401
67

8+
OSW_ID_PATTERN = re.compile(
9+
r"^(?:OSW)?" # the prefix, absent when a bare uuid is passed
10+
r"([0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12})"
11+
r"(?:\.[\w-]+)*$", # file suffixes, e.g. '.png' or '.drawio.png'
12+
re.IGNORECASE,
13+
)
14+
"""Matches an OSW-ID with an optional prefix and any number of file suffixes"""
15+
716

817
def get_osw_id(uuid: UUID) -> str:
918
"""Generates a OSW-ID based on the given uuid by prefixing "OSW" and removing
@@ -21,19 +30,33 @@ def get_osw_id(uuid: UUID) -> str:
2130
return "OSW" + str(uuid).replace("-", "")
2231

2332

24-
def get_uuid(osw_id) -> UUID:
25-
"""Returns the uuid for a given OSW-ID. Duplicate of OSW.get_uuid() from src/sw/core/osw.py
33+
def get_uuid(osw_id: str) -> UUID:
34+
"""Returns the uuid for a given OSW-ID. The single implementation, wrapped by
35+
OSW.get_uuid() from src/osw/core.py
36+
37+
A file page keeps its file extension in the title, so the OSW-ID of a file is
38+
followed by one or more suffixes that are not part of the uuid. These are
39+
ignored, as is the OSW prefix.
2640
2741
Parameters
2842
----------
2943
osw_id
30-
OSW-ID string, e.g., OSW2ea5b605c91f4e5a95593dff79fdd4a5
44+
OSW-ID string, e.g., OSW2ea5b605c91f4e5a95593dff79fdd4a5, with or without
45+
file suffixes, e.g., OSW2ea5b605c91f4e5a95593dff79fdd4a5.drawio.png
3146
3247
Returns
3348
-------
3449
uuid object, e.g., UUID("2ea5b605-c91f-4e5a-9559-3dff79fdd4a5")
50+
51+
Raises
52+
------
53+
ValueError
54+
If no OSW-ID can be read from the given string.
3555
"""
36-
return UUID(osw_id.replace("OSW", ""))
56+
match = OSW_ID_PATTERN.match(osw_id)
57+
if match is None:
58+
raise ValueError(f"No OSW-ID could be read from '{osw_id}'")
59+
return UUID(match.group(1))
3760

3861

3962
def namespace_from_full_title(full_title: str) -> str:

‎tests/utils/utils_test.py‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import uuid
22

3+
import pytest
4+
35
import osw.model.entity as model
6+
from osw.core import OSW
47
from osw.utils.regex import count_match_groups
58
from osw.utils.strings import camel_case, pascal_case
69
from osw.utils.wiki import (
@@ -28,6 +31,49 @@ def test_get_uuid():
2831
assert get_uuid(osw_id) == uuid_
2932

3033

34+
@pytest.mark.parametrize(
35+
"suffix", ["", ".png", ".drawio.png", ".tar.gz", ".jpg-2", ".a.b.c.d"]
36+
)
37+
def test_get_uuid_ignores_file_suffixes(suffix):
38+
"""File pages carry their extension in the title, ahead of the OSW-ID."""
39+
uuid_ = uuid.uuid4()
40+
osw_id = f"OSW{str(uuid_).replace('-', '')}{suffix}"
41+
42+
assert get_uuid(osw_id) == uuid_
43+
44+
45+
@pytest.mark.parametrize("prefix", ["", "OSW", "osw"])
46+
def test_get_uuid_accepts_a_bare_uuid(prefix):
47+
uuid_ = uuid.uuid4()
48+
49+
assert get_uuid(f"{prefix}{str(uuid_).replace('-', '')}") == uuid_
50+
assert get_uuid(f"{prefix}{uuid_}") == uuid_ # dashed
51+
52+
53+
@pytest.mark.parametrize(
54+
"osw_id",
55+
[
56+
"",
57+
"OSW",
58+
"Category:OSW2ea5b605c91f4e5a95593dff79fdd4a5", # full title, not an ID
59+
"OSW2ea5b605c91f4e5a95593dff79fdd4a", # one character short
60+
"OSW2ea5b605c91f4e5a95593dff79fdd4a5x",
61+
"OSW2ea5b605c91f4e5a95593dff79fdd4a5.", # empty suffix
62+
"OSWzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
63+
],
64+
)
65+
def test_get_uuid_rejects_what_is_not_an_osw_id(osw_id):
66+
with pytest.raises(ValueError):
67+
get_uuid(osw_id)
68+
69+
70+
def test_osw_get_uuid_delegates():
71+
"""OSW.get_uuid is a wrapper, so it must handle suffixes just the same."""
72+
osw_id = "OSW2ea5b605c91f4e5a95593dff79fdd4a5.drawio.png"
73+
74+
assert OSW.get_uuid(osw_id) == get_uuid(osw_id)
75+
76+
3177
def test_get_entity_namespace():
3278
class DummyClass(model.Entity):
3379
pass

0 commit comments

Comments
 (0)