Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SCHEMA] Create macro to render fields within objects #1097

Merged
merged 7 commits into from
May 4, 2022
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: 7 additions & 6 deletions src/04-modality-specific-files/08-genetic-descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@ Datasets linked to a genetic database entry include the following REQUIRED or OP
`dataset_description.json` keys (a dot in the key name denotes a key in a sub-[object][],
see the example further below):

<!-- This block generates a metadata table.
<!-- This block generates a table describing subfields within a metadata field.
The definitions of these fields can be found in
src/schema/objects/metadata.yaml
and a guide for using macros can be found at
https://github.com/bids-standard/bids-specification/blob/master/macros_doc.md
-->
{{ MACROS___make_metadata_table(
{{ MACROS___make_subobject_table(
("objects", "metadata", "Genetics"),
{
"Genetics.Dataset": "REQUIRED",
"Genetics.Database": "OPTIONAL",
"Genetics.Descriptors": "OPTIONAL",
}
"Dataset": "REQUIRED",
"Database": "OPTIONAL",
"Descriptors": "OPTIONAL",
},
) }}

Example:
Expand Down
52 changes: 23 additions & 29 deletions src/schema/objects/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -965,44 +965,38 @@ GeneticLevel:
- type: array
items:
$ref: _GeneticLevelEnum
Genetics.Database:
name: Genetics.Database
description: |
[URI](SPEC_ROOT/02-common-principles.md#uniform-resource-indicator)
of database where the dataset is hosted.
type: string
format: uri
Genetics.Dataset:
name: Genetics.Dataset
description: |
[URI](SPEC_ROOT/02-common-principles.md#uniform-resource-indicator)
where data can be retrieved.
type: string
format: uri
Genetics.Descriptors:
name: Genetics.Descriptors
description: |
List of relevant descriptors (for example, journal articles) for dataset
using a valid
[URI](SPEC_ROOT/02-common-principles.md#uniform-resource-indicator)
when possible.
anyOf:
- type: string
- type: array
items:
type: string
Genetics:
name: Genetics
description: |
An object containing information about the genetics descriptor.
type: object
properties:
Database:
$ref: Genetics.Database
name: Database
description: |
[URI](SPEC_ROOT/02-common-principles.md#uniform-resource-indicator)
of database where the dataset is hosted.
type: string
format: uri
Dataset:
$ref: Genetics.Dataset
name: Dataset
description: |
[URI](SPEC_ROOT/02-common-principles.md#uniform-resource-indicator)
where data can be retrieved.
type: string
format: uri
Descriptors:
$ref: Genetics.Descriptors
name: Descriptors
description: |
List of relevant descriptors (for example, journal articles) for dataset
using a valid
[URI](SPEC_ROOT/02-common-principles.md#uniform-resource-indicator)
when possible.
anyOf:
- type: string
- type: array
items:
type: string
GradientSetType:
name: GradientSetType
description: |
Expand Down
34 changes: 34 additions & 0 deletions tools/mkdocs_macros_bids/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def _get_source_path(level=1):
just go back to explicitly using the ``page.file`` variable throughout the macros.
"""
import inspect

# currentframe = _get_source_path()
# caller = the macro calling this function, e.g. make_glossary()
caller = inspect.currentframe().f_back
Expand Down Expand Up @@ -199,6 +200,39 @@ def make_metadata_table(field_info, src_path=None):
return table


def make_subobject_table(object_tuple, field_info, src_path=None):
"""Generate a markdown table of a metadata object's field information.

Parameters
----------
object_tuple : tuple of string
A tuple pointing to the object to render.
field_names : dict
A list of the field names.
Field names correspond to filenames in the "metadata" directory of the
schema.
Until requirement levels can be codified in the schema,
this argument will be dictionary, with the field names as keys and
the requirement levels as values.
src_path : str | None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.

Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested fields.
"""
if src_path is None:
src_path = _get_source_path()

schemapath = utils.get_schema_path()
schema_obj = schema.load_schema(schemapath)
table = render.make_subobject_table(schema_obj, object_tuple, field_info, src_path=src_path)
return table


def make_columns_table(column_info, src_path=None):
"""Generate a markdown table of TSV column information.

Expand Down
1 change: 1 addition & 0 deletions tools/mkdocs_macros_bids/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ def define_env(env):
env.macro(macros.make_glossary, "MACROS___make_glossary")
env.macro(macros.make_suffix_table, "MACROS___make_suffix_table")
env.macro(macros.make_metadata_table, "MACROS___make_metadata_table")
env.macro(macros.make_subobject_table, "MACROS___make_subobject_table")
env.macro(macros.make_columns_table, "MACROS___make_columns_table")
env.macro(macros.make_filetree_example, "MACROS___make_filetree_example")
130 changes: 95 additions & 35 deletions tools/schemacode/schemacode/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,51 @@ def make_suffix_table(schema, suffixes, src_path=None, tablefmt="github"):
return table_str


def make_obj_table(subschema, field_info, src_path=None, tablefmt="github"):
# Use the "name" field in the table, to allow for filenames to not match
# "names".
df = pd.DataFrame(
index=[subschema[f]["name"] for f in subschema.keys()],
columns=["**Requirement Level**", "**Data type**", "**Description**"],
)
df.index.name = "**Key name**"
for field in subschema.keys():
field_name = subschema[field]["name"]
requirement_info = field_info[field]
description_addendum = ""
if isinstance(requirement_info, tuple):
requirement_info, description_addendum = requirement_info

requirement_info = requirement_info.replace(
"DEPRECATED",
"[DEPRECATED](/02-common-principles.html#definitions)",
)

type_string = utils.resolve_metadata_type(subschema[field])

description = subschema[field]["description"] + " " + description_addendum

# Try to add info about valid values
valid_values_str = utils.describe_valid_values(subschema[field])
if valid_values_str:
description += "\n\n\n\n" + valid_values_str

# A backslash before a newline means continue a string
description = description.replace("\\\n", "")
# Two newlines should be respected
description = description.replace("\n\n", "<br>")
# Otherwise a newline corresponds to a space
description = description.replace("\n", " ")
# Spec internal links need to be replaced
description = description.replace("SPEC_ROOT", get_relpath(src_path))

df.loc[field_name] = [requirement_info, type_string, description]

# Print it as markdown
table_str = tabulate(df, headers="keys", tablefmt=tablefmt)
return table_str


def make_metadata_table(schema, field_info, src_path=None, tablefmt="github"):
"""Produce metadata table (markdown) based on requested fields.

Expand Down Expand Up @@ -519,47 +564,62 @@ def make_metadata_table(schema, field_info, src_path=None, tablefmt="github"):
if dropped_fields:
print("Warning: Missing fields: {}".format(", ".join(dropped_fields)))

# Use the "name" field in the table, to allow for filenames to not match
# "names".
df = pd.DataFrame(
index=[metadata_schema[f]["name"] for f in retained_fields],
columns=["**Requirement Level**", "**Data type**", "**Description**"],
)
df.index.name = "**Key name**"
for field in retained_fields:
field_name = metadata_schema[field]["name"]
requirement_info = field_info[field]
description_addendum = ""
if isinstance(requirement_info, tuple):
requirement_info, description_addendum = requirement_info

requirement_info = requirement_info.replace(
"DEPRECATED",
"[DEPRECATED](/02-common-principles.html#definitions)",
)
metadata_schema = {k: v for k, v in metadata_schema.items() if k in retained_fields}

type_string = utils.resolve_metadata_type(metadata_schema[field])
table_str = make_obj_table(
metadata_schema,
field_info=field_info,
src_path=src_path,
tablefmt=tablefmt,
)

description = metadata_schema[field]["description"] + " " + description_addendum
return table_str

# Try to add info about valid values
valid_values_str = utils.describe_valid_values(metadata_schema[field])
if valid_values_str:
description += "\n\n\n\n" + valid_values_str

# A backslash before a newline means continue a string
description = description.replace("\\\n", "")
# Two newlines should be respected
description = description.replace("\n\n", "<br>")
# Otherwise a newline corresponds to a space
description = description.replace("\n", " ")
# Spec internal links need to be replaced
description = description.replace("SPEC_ROOT", get_relpath(src_path))
def make_subobject_table(schema, object_tuple, field_info, src_path=None, tablefmt="github"):
"""Create a table of properties within an object.

df.loc[field_name] = [requirement_info, type_string, description]
Parameters
----------
schema
object_tuple : tuple of strings
A tuple of keys within the schema linking down to the object
that will be rendered.
For example, ("objects", "metadata", "Genetics") will result in a table
rendering the properties specified in
schema["object"]["metadata"]["Genetics"].
field_info : dict of strings or tuples
A dictionary mapping metadata keys to requirement levels in the
rendered metadata table.
The dictionary values may be strings, in which case the string
is the requirement level information, or two-item tuples of strings,
in which case the first string is the requirement level information
and the second string is additional table-specific information
about the metadata field that will be appended to the field's base
definition from the schema.
src_path : str | None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
The target table format. The default is "github" (GitHub format).
"""
assert isinstance(object_tuple, tuple)
assert all([isinstance(i, str) for i in object_tuple])

temp_dict = schema[object_tuple[0]]
for i in range(1, len(object_tuple)):
level_str = object_tuple[i]
temp_dict = temp_dict[level_str]

temp_dict = temp_dict["properties"]
assert isinstance(temp_dict, dict)
table_str = make_obj_table(
temp_dict,
field_info=field_info,
src_path=src_path,
tablefmt=tablefmt,
)

# Print it as markdown
table_str = tabulate(df, headers="keys", tablefmt=tablefmt)
return table_str


Expand Down