diff --git a/docs/datadoc/documenting-a-resource.md b/docs/datadoc/documenting-a-resource.md index e845a6d1..0ae55d9d 100644 --- a/docs/datadoc/documenting-a-resource.md +++ b/docs/datadoc/documenting-a-resource.md @@ -180,14 +180,15 @@ Ex: `distribution.downloadURL`. The header keyword(s) may be followed by an optional square bracket with the following syntax (inspired by URL options): - name[label?var1=value1&var2=value2] + name[label?key1=value1&key2=value2] where -- `name`: is the column name that is mapped to a keyword (or set of dot-separated keywords) defined in the JSON-LD context. + +- `name`: is the column name that is mapped to a keyword (or set of dot-separated keywords) defined in the JSON-LD context. (_required_) The only formal requirement is that it cannot contain a begin brace ([), but it might be wise to be more strict. - `label`: is a label for the column. It is used to make the column unique or to group related columns. - The only formal requirement is that is cannot contain a question mark (?), but it is probably wise to be more strict. + The only formal requirement is that is cannot contain a question mark (?) or equal sign (=), but it is probably wise to be more strict. A digit should be allowed. - `key`: a key identifying an option for the column. Should be a valid C or Python identifier (regex: `[_a-zA-Z][_a-zA-Z0-9]*`). @@ -195,6 +196,7 @@ where Should not contain (unescaped) ampersand (&) or end braces (]). Currently recognised keys: + - **`unit`**: A unit symbol. All numbers in this column have this unit. - **`sep`**: Separator character. A common user request when you have multiple values for a column, is to be able to provide multiple values in a single cell, instead of duplicating the column. This option makes it possible to specify a separator character that can be used in this column. @@ -216,16 +218,29 @@ Grouping of columns (e.g. for DLite datamodels): Specifying unit: -| @id | @type | length[?unit=m] | +| @id | @type | length[unit=m] | |--------------|-------------|-----------------| | ex:my_length | emmo:Length | 3.2 | Specifying a separator: -| @id | @type | keyword[?sep=;] | +| @id | @type | keyword[sep=;] | |-----------|--------------|--------------------| | ex:mydata | emmo:Dataset | geology;stone;cave | +Combining label ('temsample') and unit ('unit=mm'): + +| @id | @type | diameter[temsample?unit=mm] | +|-----------|--------------|-----------------------------| +| ex:mydata | emmo:Dataset | 3.0 | + +Same as above, but with sample being a blank node (labeled 'temsample') with property `diameter` (with unit mm). +In this case, we put the label on the `hasSample` property and the unit on the `diameter` property: + +| @id | @type | hasSample[temsample].diameter[unit=mm] | +|-----------|--------------|----------------------------------------| +| ex:mydata | emmo:Dataset | 3.0 | + ### Complete example For example, the table diff --git a/tests/datadoc/test_tabledoc.py b/tests/datadoc/test_tabledoc.py index 5e8aee54..0d4a459b 100644 --- a/tests/datadoc/test_tabledoc.py +++ b/tests/datadoc/test_tabledoc.py @@ -388,12 +388,17 @@ def test_column(): assert col5.options == {"sep": ",", "unit": "m"} assert col5.datatype is None - col6 = Column("creationDate", context=get_context()) - assert col6.header == "creationDate" - assert col6.datatype == XSD.dateTime + col6 = Column("length[unit=m]") + assert col6.names == ["length"] + assert col6.label == "" + assert col6.options == {"unit": "m"} + col7 = Column("creationDate", context=get_context()) + assert col7.header == "creationDate" + assert col7.datatype == XSD.dateTime -def test_sep(): + +def test_column_sep(): """Test the column separation.""" from tripper.datadoc import TableDoc diff --git a/tripper/datadoc/tabledoc.py b/tripper/datadoc/tabledoc.py index 1b01b1c6..8702064a 100644 --- a/tripper/datadoc/tabledoc.py +++ b/tripper/datadoc/tabledoc.py @@ -601,7 +601,7 @@ class Column: # pylint: disable=too-few-public-methods def __init__(self, header, context=None, strip=True): - # pylint: disable=line-too-long + # pylint: disable=line-too-long,too-many-branches """Initialise a column opject. Arguments: @@ -623,10 +623,18 @@ def __init__(self, header, context=None, strip=True): label = fields[0][2].split("?", 1)[0] spec = fields[-1][2].split("?", 1) + opts = spec[1] if len(spec) == 2 else "" + + # Special rule. If `label` contains an equal sign (=), it is treated + # to be the first part of `opts`. + # This allows the user to omit the ?-sign if there is no label, e.g. + # writing "length[unit=m]" instead of "length[?unit=m]" + if "=" in label: + label, opts = "", f"{label}&{opts}" if opts else label options = {} - if len(spec) == 2: - for opt in spec[1].split("&"): + if opts: + for opt in opts.split("&"): k, v = opt.split("=", 1) options[k] = v