Skip to content

Commit

Permalink
Merge pull request #614 from hed-standard/dev_with_standard
Browse files Browse the repository at this point in the history
Add with-standard schema header attribute
  • Loading branch information
VisLab authored Feb 15, 2023
2 parents 6af6b24 + cf6082b commit a93b4be
Show file tree
Hide file tree
Showing 35 changed files with 40,739 additions and 444 deletions.
10 changes: 5 additions & 5 deletions hed/errors/error_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@


@hed_tag_error(ValidationErrors.HED_UNITS_INVALID)
def val_error_invalid_unit(tag, unit_class_units):
units_string = ','.join(sorted(unit_class_units))
def val_error_invalid_unit(tag, units):
units_string = ','.join(sorted(units))
return f'Invalid unit - "{tag}" valid units are "{units_string}"', {
"unit_class_units": sorted(unit_class_units)
"units": sorted(units)
}


Expand Down Expand Up @@ -202,8 +202,8 @@ def val_warning_default_units_used(tag, default_unit):
@hed_error(SchemaErrors.HED_SCHEMA_DUPLICATE_NODE)
def schema_error_hed_duplicate_node(tag, duplicate_tag_list, section):
tag_join_delimiter = "\n\t"
return f"Duplicate term '{str(tag)}' used {len(duplicate_tag_list)} places in '{section}' section schema as: " + \
f"{tag_join_delimiter} {tag_join_delimiter.join(duplicate_tag_list)}", {}
return f"Duplicate term '{str(tag)}' used {len(duplicate_tag_list)} places in '{section}' section schema as:" + \
f"{tag_join_delimiter}{tag_join_delimiter.join(duplicate_tag_list)}", {}


@hed_error(SchemaErrors.HED_SCHEMA_ATTRIBUTE_INVALID)
Expand Down
2 changes: 1 addition & 1 deletion hed/errors/error_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def format_error(error_type, *args, actual_error=None, **kwargs):
error_type (str): The type of error for this. Registered with @hed_error or @hed_tag_error.
args (args): Any remaining non keyword args after those required by the error type.
actual_error (str or None): Code to actually add to report out.
kwargs (dict): The other keyword args to pass down to the error handling func.
kwargs (kwargs): The other keyword args to pass down to the error handling func.
Returns:
list: A list containing a single dictionary representing a single error.
Expand Down
2 changes: 1 addition & 1 deletion hed/models/hed_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def get_tag_unit_class_units(self):
units = []
unit_classes = self.unit_classes
for unit_class_entry in unit_classes.values():
units += unit_class_entry.unit_class_units.keys()
units += unit_class_entry.units.keys()

return units

Expand Down
170 changes: 115 additions & 55 deletions hed/schema/hed_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

from hed.schema.hed_schema_constants import HedKey, HedSectionKey
from hed.schema import hed_schema_constants as constants
from hed.schema.schema_io import schema_util
from hed.schema.schema_io.schema2xml import HedSchema2XML
from hed.schema.schema_io.schema2wiki import HedSchema2Wiki
Expand All @@ -9,7 +10,6 @@
from hed.errors import ErrorHandler
from hed.errors.error_types import ValidationErrors


class HedSchema:
""" A HED schema suitable for processing. """

Expand All @@ -20,7 +20,7 @@ def __init__(self):
"""
self._has_duplicate_tags = False
self.header_attributes = {}
self._filename = None
self.filename = None
self.prologue = ""
self.epilogue = ""

Expand All @@ -33,25 +33,6 @@ def __init__(self):
# ===============================================
# Basic schema properties
# ===============================================
@property
def filename(self):
""" The filename if one is known.
Returns:
str: The filename of this schema.
"""
return self._filename

@filename.setter
def filename(self, value):
""" Set the filename, if one has not already been set.
Parameters:
value (str): The source filename for this file
"""
if self._filename is None:
self._filename = value

@property
def version(self):
""" The HED version of this schema.
Expand All @@ -62,27 +43,63 @@ def version(self):
"""
return self.header_attributes['version']

def get_formatted_version(self, as_string=True):
def get_formatted_version(self, as_string=False):
""" The HED version string including prefix and library name if any of this schema.
Returns:
str: The complete version of this schema including library name and prefix.
"""
library = self.header_attributes.get('library', '')
library = self.library
if library:
library = library + '_'
return self._schema_prefix + library + self.header_attributes.get('version', '')
return self._schema_prefix + library + self.version

@property
def library(self):
""" The name of this library schema if one exists.
Returns:
str or None: Library name if any.
str: Library name if any.
"""
return self.header_attributes.get(constants.LIBRARY_ATTRIBUTE, "")

@property
def with_standard(self):
""" The version of the base schema this is extended from, if it exists..
Returns:
str: HED version or ""
"""
return self.header_attributes.get('library')
return self.header_attributes.get(constants.WITH_STANDARD_ATTRIBUTE, "")

@property
def merged(self):
""" Returns if this schema was loaded from a merged file
Returns:
bool: True if file was loaded from a merged file
"""
return self.header_attributes.get(constants.MERGED_ATTRIBUTE, "")

def get_save_header_attributes(self, save_merged=False):
""" returns the attributes that should be saved.
"""
sort_to_start = "!!!!!!!!!!!!!!"
header_attributes = dict(sorted(self.header_attributes.items(), key=lambda x: sort_to_start if x[0] == constants.VERSION_ATTRIBUTE else x[0], reverse=False))
if save_merged:
# make sure it's the last attribute(just to make sure it's in an order)
header_attributes.pop(constants.MERGED_ATTRIBUTE, None)
header_attributes[constants.MERGED_ATTRIBUTE] = "True"
else:
header_attributes.pop(constants.MERGED_ATTRIBUTE, None)

return header_attributes

def schema_for_prefix(self, prefix):
""" Return HedSchema object for this prefix.
Expand Down Expand Up @@ -116,52 +133,63 @@ def valid_prefixes(self):
# ===============================================
# Creation and saving functions
# ===============================================
def get_as_mediawiki_string(self):
def get_as_mediawiki_string(self, save_merged=False):
""" Return the schema to a mediawiki string.
save_merged: bool
If true, this will save the schema as a merged schema if it is a "with-standard" schema.
If it is not a "with-standard" schema, this setting has no effect.
Returns:
str: The schema as a string in mediawiki format.
"""
schema2wiki = HedSchema2Wiki()
output_strings = schema2wiki.process_schema(self)
output_strings = schema2wiki.process_schema(self, save_merged)
return '\n'.join(output_strings)

def get_as_xml_string(self):
def get_as_xml_string(self, save_merged=False):
""" Return the schema to an XML string.
save_merged: bool
If true, this will save the schema as a merged schema if it is a "with-standard" schema.
If it is not a "with-standard" schema, this setting has no effect.
Returns:
str: Return the schema as an XML string.
"""
schema2xml = HedSchema2XML()
xml_tree = schema2xml.process_schema(self)
xml_tree = schema2xml.process_schema(self, save_merged)
return schema_util._xml_element_2_str(xml_tree)

def save_as_xml(self):
""" Save as XML to a temporary file.
Returns:
str: The name of the newly created schema file.
"""
schema2xml = HedSchema2XML()
xml_tree = schema2xml.process_schema(self)
local_xml_file = schema_util.write_xml_tree_2_xml_file(xml_tree, ".xml")
return local_xml_file

def save_as_mediawiki(self):
def save_as_mediawiki(self, save_merged=False):
""" Save as mediawiki to a temporary file.
save_merged: bool
If true, this will save the schema as a merged schema if it is a "with-standard" schema.
If it is not a "with-standard" schema, this setting has no effect.
Returns:
str: The newly created schema filename.
"""
schema2wiki = HedSchema2Wiki()
output_strings = schema2wiki.process_schema(self)
output_strings = schema2wiki.process_schema(self, save_merged)
local_wiki_file = schema_util.write_strings_to_file(output_strings, ".mediawiki")
return local_wiki_file

def save_as_xml(self, save_merged=False):
""" Save as XML to a temporary file.
Returns:
str: The name of the newly created schema file.
save_merged: bool
If true, this will save the schema as a merged schema if it is a "with-standard" schema.
If it is not a "with-standard" schema, this setting has no effect.
"""
schema2xml = HedSchema2XML()
xml_tree = schema2xml.process_schema(self, save_merged)
local_xml_file = schema_util.write_xml_tree_2_xml_file(xml_tree, ".xml")
return local_xml_file

def set_schema_prefix(self, schema_prefix):
""" Set library prefix associated for this schema.
Expand Down Expand Up @@ -260,6 +288,26 @@ def value_classes(self):
"""
return self._sections[HedSectionKey.ValueClasses]

@property
def attributes(self):
""" Return the attributes schema section.
Returns:
HedSchemaSection: The attributes section.
"""
return self._sections[HedSectionKey.Attributes]

@property
def properties(self):
""" Return the properties schema section.
Returns:
HedSchemaSection: The properties section.
"""
return self._sections[HedSectionKey.Properties]

@property
def is_hed3_schema(self):
""" Return true if this is at least version HED3.
Expand Down Expand Up @@ -291,7 +339,7 @@ def __eq__(self, other):
"""
if other is None:
return False
if self.header_attributes != other.header_attributes:
if self.get_save_header_attributes() != other.get_save_header_attributes():
return False
if self._has_duplicate_tags != other._has_duplicate_tags:
return False
Expand All @@ -316,7 +364,7 @@ def __eq__(self, other):
# print(f"{key} not in dict2")
# continue
# if dict1[key] != dict2[key]:
# s = f"{key} unmatched: '{str(dict1[key].long_name)}' vs '{str(dict2[key].long_name)}'"
# s = f"{key} unmatched: '{str(dict1[key].name)}' vs '{str(dict2[key].name)}'"
# print(s)
return False
if self._schema_prefix != other._schema_prefix:
Expand All @@ -338,15 +386,15 @@ def get_unit_class_units(self, unit_class_type):
"""
unit_class_entry = self.get_tag_entry(unit_class_type, HedSectionKey.UnitClasses)
if unit_class_entry:
return unit_class_entry.unit_class_units
return unit_class_entry.units
return []

def get_tags_with_attribute(self, key, section_key=HedSectionKey.AllTags):
""" Return tag entries with the given attribute.
Parameters:
key (str): A tag attribute. Eg HedKey.ExtensionAllowed
section_key (str): The HedSectionKey for the section to retrieve from.
section_key (HedSectionKey): The HedSectionKey for the section to retrieve from.
Returns:
list: A list of all tags with this attribute.
Expand Down Expand Up @@ -511,15 +559,16 @@ def finalize_dictionaries(self):

def _update_all_entries(self):
""" Call finalize_entry on every schema entry(tag, unit, etc). """
for section in self._sections.values():
for key_class, section in self._sections.items():
self._initialize_attributes(key_class)
for entry in section.values():
entry.finalize_entry(self)

def _initialize_attributes(self, key_class):
""" Set the valid attributes for a section.
Parameters:
key_class (str): The section key for the section to update.
key_class (HedSectionKey): The section key for the section to update.
"""
self._sections[key_class].valid_attributes = self._get_attributes_for_section(key_class)
Expand Down Expand Up @@ -573,6 +622,7 @@ def get_all_schema_tags(self, return_last_term=False):
final_list.append(tag_entry.name.split('/')[-1])
else:
final_list.append(tag_entry.name)

return final_list

def get_unknown_attributes(self):
Expand Down Expand Up @@ -674,11 +724,17 @@ def get_modifiers_for_unit(self, unit):
valid_modifiers = self.unit_modifiers.get_entries_with_attribute(modifier_attribute_name)
return valid_modifiers

def _add_element_property_attributes(self, attribute_dict):
attributes = {attribute: entry for attribute, entry in self._sections[HedSectionKey.Attributes].items()
if entry.has_attribute(HedKey.ElementProperty)}

attribute_dict.update(attributes)

def _get_attributes_for_section(self, key_class):
""" Return the valid attributes for this section.
Parameters:
key_class (str): The HedKey for this section.
key_class (HedSectionKey): The HedKey for this section.
Returns:
dict or HedSchemaSection: A dict of all the attributes and this section.
Expand All @@ -687,7 +743,13 @@ def _get_attributes_for_section(self, key_class):
if key_class == HedSectionKey.AllTags:
return self.get_tag_attribute_names()
elif key_class == HedSectionKey.Attributes:
return self._sections[HedSectionKey.Properties]
prop_added_dict = {key:value for key, value in self._sections[HedSectionKey.Properties].items()}
self._add_element_property_attributes(prop_added_dict)
return prop_added_dict
elif key_class == HedSectionKey.Properties:
prop_added_dict = {}
self._add_element_property_attributes(prop_added_dict)
return prop_added_dict
else:
attrib_classes = {
HedSectionKey.UnitClasses: HedKey.UnitClassProperty,
Expand All @@ -700,14 +762,12 @@ def _get_attributes_for_section(self, key_class):
return []

attributes = {attribute: entry for attribute, entry in self._sections[HedSectionKey.Attributes].items()
if entry.has_attribute(attrib_class)}
if entry.has_attribute(attrib_class) or entry.has_attribute(HedKey.ElementProperty)}
return attributes

# ===============================================
# Semi private function used to create a schema in memory(usually from a source file)
# ===============================================
def _add_tag_to_dict(self, long_tag_name, key_class):
section = self._sections[key_class]
if not section:
self._initialize_attributes(key_class)
return section._add_to_dict(long_tag_name)
Loading

0 comments on commit a93b4be

Please sign in to comment.