Repository: hed-standard/hed-python (language: python)

Issue:
Web work
Fixed docker paths


Discussion / hints:

Fix patch:
diff --git a/webtools/hedweb/constants/common.py b/webtools/hedweb/constants/common.py
index 9e9a67da4..70d0236ae 100644
--- a/webtools/hedweb/constants/common.py
+++ b/webtools/hedweb/constants/common.py
@@ -1,77 +1,77 @@
-CHECK_FOR_WARNINGS = 'check-for-warnings'
-COLUMN_NAMES = 'column-names'
-COLUMN_NAMES_TABLE = 'column-names-table'
-HAS_COLUMN_NAMES = 'has-column-names'
-COLUMN_POSTFIX = '-column'
-COLUMN_PREFIX_DICTIONARY = 'column-prefix-dictionary'
-COLUMNS_FILE = 'columns-file'
-COLUMNS_PATH = 'columns-path'
-COLUMNS_DISPLAY_NAME = 'columns-display-name'
-TAG_COLUMNS = 'tag-columns'
-TAG_COLUMN_INDICES = 'tag-column-indices'
-
-DOWNLOAD_FILE = 'download-file'
-JSON_FILE = 'json-file'
-JSON_PATH = 'json-path'
-ERROR_COUNT = 'error-count'
-EVENTS_DISPLAY_NAME = 'events-display-name'
-EVENTS_FILE = 'events-file'
-EVENTS_FLASH = 'events-flash'
-EVENTS_PATH = 'events-path'
-EVENTS_VALIDATION_SUBMIT = 'events-validation-submit'
-EVENTS_SUBMIT_FLASH = 'events-validation-submit-flash'
-
-HED_DISPLAY_NAME = 'hed-display-name'
-HED_MAJOR_VERSIONS = 'hed-major-versions'
-HED_OPTION = 'hed-option'
-HED_OPTION_TO_LONG = 'hed-option-to-long'
-HED_OPTION_TO_SHORT = 'hed-option-to-short'
-HED_OPTION_TO_TAGGED_LIST = 'hed-option-to-tagged-list'
-HED_OPTION_VALIDATE = 'hed-option-validate'
-HED_OTHER_VERSION = 'hed-other-version'
-HED_OTHER_VERSION_OPTION = 'Other'
-HED_SCHEMA_FILE = 'hed-schema-file'
-HED_SCHEMA_PATH = 'hed-schema-path'
-HED_SCHEMA_URL = 'hed-schema-url'
-HED_SELECT_FLASH = 'hed-select-flash'
-HEDSTRING = 'hedstring'
-HEDSTRING_LIST = 'hedstring_list'
-HED_VERSION = 'hed-version'
-HED_XML_FILE = 'hed-xml-file'
-ISSUE_COUNT = 'issue-count'
-REQUIRED_TAG_COLUMN_INDICES = 'required-tag-column-indices'
-SPREADSHEET_DISPLAY_NAME = 'spreadsheet-display-name'
-SPREADSHEET_FILE = 'spreadsheet-file'
-SPREADSHEET_PATH = 'spreadsheet-path'
-
-WARNING_COUNT = 'warning-count'
-
-WORKSHEET_NAME = 'worksheet-name'
-WORKSHEET_NAMES = 'worksheet-names'
-WORKSHEET_SELECT = 'worksheet-select'
-WORKSHEET_SELECTED = 'worksheet-selected'
-
-# Schema-specific constants
-SCHEMA_FORM = 'schema-form'
-SCHEMA_CONVERSION_SUBMIT = 'schema-conversion-submit'
-SCHEMA_DUPLICATE_TAG_SUBMIT = 'schema-duplicate-tag-submit'
-SCHEMA_DISPLAY_NAME = 'schema-display-name'
-SCHEMA_FILE = 'schema-file'
-SCHEMA_FILE_FLASH = 'schema-file-flash'
-SCHEMA_FILE_OPTION = 'schema-file-option'
-SCHEMA_OPTION = 'schema-option'
-SCHEMA_OPTION_CHECK = 'schema-option-check'
-SCHEMA_OPTION_CONVERT = 'schema-option-convert'
-SCHEMA_PATH = 'schema-path'
-SCHEMA_SUBMIT = 'schema-submit'
-SCHEMA_SUBMIT_FLASH = 'schema-submit-flash'
-SCHEMA_UPLOAD_OPTIONS = 'schema-upload-options'
-SCHEMA_URL = 'schema-url'
-SCHEMA_URL_DISPLAY_NAME = 'schema-url-display-name'
-SCHEMA_URL_FLASH = 'schema-url-flash'
-SCHEMA_URL_DISPLAY_NAME = 'schema-url-display-name'
-SCHEMA_URL_OPTION = 'schema-url-option'
-
-# Type constants
-BOOLEAN = 'boolean'
-STRING = 'string'
+CHECK_FOR_WARNINGS = 'check-for-warnings'
+COLUMN_NAMES = 'column-names'
+COLUMN_NAMES_TABLE = 'column-names-table'
+HAS_COLUMN_NAMES = 'has-column-names'
+COLUMN_POSTFIX = '-column'
+COLUMN_PREFIX_DICTIONARY = 'column-prefix-dictionary'
+COLUMNS_FILE = 'columns-file'
+COLUMNS_PATH = 'columns-path'
+COLUMNS_DISPLAY_NAME = 'columns-display-name'
+TAG_COLUMNS = 'tag-columns'
+TAG_COLUMN_INDICES = 'tag-column-indices'
+
+DOWNLOAD_FILE = 'download-file'
+JSON_FILE = 'json-file'
+JSON_PATH = 'json-path'
+ERROR_COUNT = 'error-count'
+EVENTS_DISPLAY_NAME = 'events-display-name'
+EVENTS_FILE = 'events-file'
+EVENTS_FLASH = 'events-flash'
+EVENTS_PATH = 'events-path'
+EVENTS_SUBMIT = 'events-submit'
+EVENTS_SUBMIT_FLASH = 'events-submit-flash'
+
+HED_DISPLAY_NAME = 'hed-display-name'
+HED_MAJOR_VERSIONS = 'hed-major-versions'
+HED_OPTION = 'hed-option'
+HED_OPTION_ASSEMBLE = 'hed-option-assemble'
+HED_OPTION_TO_LONG = 'hed-option-to-long'
+HED_OPTION_TO_SHORT = 'hed-option-to-short'
+HED_OPTION_VALIDATE = 'hed-option-validate'
+HED_OTHER_VERSION = 'hed-other-version'
+HED_OTHER_VERSION_OPTION = 'Other'
+HED_SCHEMA_FILE = 'hed-schema-file'
+HED_SCHEMA_PATH = 'hed-schema-path'
+HED_SCHEMA_URL = 'hed-schema-url'
+HED_SELECT_FLASH = 'hed-select-flash'
+HEDSTRING = 'hedstring'
+HEDSTRING_LIST = 'hedstring_list'
+HED_VERSION = 'hed-version'
+HED_XML_FILE = 'hed-xml-file'
+ISSUE_COUNT = 'issue-count'
+REQUIRED_TAG_COLUMN_INDICES = 'required-tag-column-indices'
+SPREADSHEET_DISPLAY_NAME = 'spreadsheet-display-name'
+SPREADSHEET_FILE = 'spreadsheet-file'
+SPREADSHEET_PATH = 'spreadsheet-path'
+
+WARNING_COUNT = 'warning-count'
+
+WORKSHEET_NAME = 'worksheet-name'
+WORKSHEET_NAMES = 'worksheet-names'
+WORKSHEET_SELECT = 'worksheet-select'
+WORKSHEET_SELECTED = 'worksheet-selected'
+
+# Schema-specific constants
+SCHEMA_FORM = 'schema-form'
+SCHEMA_CONVERSION_SUBMIT = 'schema-conversion-submit'
+SCHEMA_DUPLICATE_TAG_SUBMIT = 'schema-duplicate-tag-submit'
+SCHEMA_DISPLAY_NAME = 'schema-display-name'
+SCHEMA_FILE = 'schema-file'
+SCHEMA_FILE_FLASH = 'schema-file-flash'
+SCHEMA_FILE_OPTION = 'schema-file-option'
+SCHEMA_OPTION = 'schema-option'
+SCHEMA_OPTION_CHECK = 'schema-option-check'
+SCHEMA_OPTION_CONVERT = 'schema-option-convert'
+SCHEMA_PATH = 'schema-path'
+SCHEMA_SUBMIT = 'schema-submit'
+SCHEMA_SUBMIT_FLASH = 'schema-submit-flash'
+SCHEMA_UPLOAD_OPTIONS = 'schema-upload-options'
+SCHEMA_URL = 'schema-url'
+SCHEMA_URL_DISPLAY_NAME = 'schema-url-display-name'
+SCHEMA_URL_FLASH = 'schema-url-flash'
+SCHEMA_URL_DISPLAY_NAME = 'schema-url-display-name'
+SCHEMA_URL_OPTION = 'schema-url-option'
+
+# Type constants
+BOOLEAN = 'boolean'
+STRING = 'string'
diff --git a/webtools/hedweb/constants/route_constants.py b/webtools/hedweb/constants/route_constants.py
index 5fb1770bd..691b7850c 100644
--- a/webtools/hedweb/constants/route_constants.py
+++ b/webtools/hedweb/constants/route_constants.py
@@ -1,26 +1,26 @@
-ROUTE_BLUEPRINT = 'route_blueprint'
-
-ADDITIONAL_EXAMPLES_ROUTE = '/additional-examples'
-
-COLUMN_INFO_ROUTE = '/get-columns-info'
-
-COMMON_ERRORS_ROUTE = '/common-errors'
-DICTIONARY_ROUTE = '/dictionary'
-DICTIONARY_SUBMIT_ROUTE = '/dictionary-submit'
-
-EVENTS_ROUTE = '/events'
-EVENTS_SUBMIT_ROUTE = '/events-validation-submit'
-HED_MAJOR_VERSION_ROUTE = '/get-hed-major-versions'
-HED_SERVICES_SUBMIT_ROUTE = '/hed-services-submit'
-HED_SERVICES_ROUTE = '/hed-services'
-HEDSTRING_ROUTE = '/hedstring'
-HEDSTRING_SUBMIT_ROUTE = '/hedstring-submit'
-HED_TOOLS_HELP_ROUTE = '/hed-tools-help'
-HED_TOOLS_HOME_ROUTE = '/'
-HED_VERSION_ROUTE = '/get-hed-version'
-SCHEMA_ROUTE = '/schema'
-SCHEMA_SUBMIT_ROUTE = '/schema-submit'
-
-SPREADSHEET_ROUTE = '/spreadsheet'
-SPREADSHEET_SUBMIT_ROUTE = '/spreadsheet-submit'
-WORKSHEET_COLUMN_INFO_ROUTE = '/get-worksheets-info'
+ROUTE_BLUEPRINT = 'route_blueprint'
+
+ADDITIONAL_EXAMPLES_ROUTE = '/additional-examples'
+
+COLUMN_INFO_ROUTE = '/get-columns-info'
+
+COMMON_ERRORS_ROUTE = '/common-errors'
+DICTIONARY_ROUTE = '/dictionary'
+DICTIONARY_SUBMIT_ROUTE = '/dictionary-submit'
+
+EVENTS_ROUTE = '/events'
+EVENTS_SUBMIT_ROUTE = '/events-submit'
+HED_MAJOR_VERSION_ROUTE = '/get-hed-major-versions'
+HED_SERVICES_SUBMIT_ROUTE = '/hed-services-submit'
+HED_SERVICES_ROUTE = '/hed-services'
+HEDSTRING_ROUTE = '/hedstring'
+HEDSTRING_SUBMIT_ROUTE = '/hedstring-submit'
+HED_TOOLS_HELP_ROUTE = '/hed-tools-help'
+HED_TOOLS_HOME_ROUTE = '/'
+HED_VERSION_ROUTE = '/get-hed-version'
+SCHEMA_ROUTE = '/schema'
+SCHEMA_SUBMIT_ROUTE = '/schema-submit'
+
+SPREADSHEET_ROUTE = '/spreadsheet'
+SPREADSHEET_SUBMIT_ROUTE = '/spreadsheet-submit'
+WORKSHEET_COLUMN_INFO_ROUTE = '/get-worksheets-info'
diff --git a/webtools/hedweb/dictionary.py b/webtools/hedweb/dictionary.py
index 11698a782..41391c1a2 100644
--- a/webtools/hedweb/dictionary.py
+++ b/webtools/hedweb/dictionary.py
@@ -1,160 +1,164 @@
-import os
-from flask import current_app
-
-from hed.schema.hed_schema_file import load_schema
-from hedweb.constants import common, file_constants
-from hed.util.column_def_group import ColumnDefGroup
-from hed.util.hed_string import HedString
-from hed.util.error_reporter import ErrorHandler, get_printable_issue_string
-from hed.util.error_types import ErrorSeverity
-from hed.util.exceptions import HedFileError
-from hedweb.web_utils import form_has_option, generate_filename, generate_download_file_response, \
-    generate_text_response, get_hed_path_from_pull_down, \
-    get_uploaded_file_path_from_form, save_text_to_upload_folder
-
-app_config = current_app.config
-
-
-def generate_input_from_dictionary_form(request):
-    """Gets the validation function input arguments from a request object associated with the validation form.
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data from the validation form.
-
-    Returns
-    -------
-    dictionary
-        A dictionary containing input arguments for calling the underlying validation function.
-    """
-    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
-    uploaded_file_name, original_file_name = \
-        get_uploaded_file_path_from_form(request, common.JSON_FILE, file_constants.DICTIONARY_FILE_EXTENSIONS)
-
-    arguments = {
-        common.HED_XML_FILE: hed_file_path,
-        common.HED_DISPLAY_NAME: hed_display_name,
-        common.JSON_PATH: uploaded_file_name,
-        common.JSON_FILE: original_file_name,
-    }
-    if form_has_option(request, common.HED_OPTION, common.HED_OPTION_VALIDATE):
-        arguments[common.HED_OPTION_VALIDATE] = True
-    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_SHORT):
-        arguments[common.HED_OPTION_TO_SHORT] = True
-    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_LONG):
-        arguments[common.HED_OPTION_TO_LONG] = True
-
-    return arguments
-
-
-def dictionary_process(arguments):
-    """Perform the requested action for the dictionary.
-
-    Parameters
-    ----------
-    arguments: dict
-        A dictionary with the input arguments from the dictionary form
-
-    Returns
-    -------
-      Response
-        Downloadable response object.
-    """
-
-    if not arguments[common.JSON_PATH]:
-        raise HedFileError('EmptyDictionaryFile', "Please upload a dictionary to process", "")
-    if arguments.get(common.HED_OPTION_VALIDATE, None):
-        return dictionary_validate(arguments)
-    elif arguments.get(common.HED_OPTION_TO_SHORT, None):
-        return dictionary_convert(arguments, short_to_long=False)
-    elif arguments.get(common.HED_OPTION_TO_LONG, None):
-        return dictionary_convert(arguments)
-    else:
-        raise HedFileError('UnknownProcessingMethod', "Select a dictionary processing method", "")
-
-
-def dictionary_convert(arguments, short_to_long=True, hed_schema=None):
-    """Converts a dictionary from short to long unless short_to_long is set to False, then long_to_short
-
-    Parameters
-    ----------
-    arguments: dict
-        Dictionary containing standard input form arguments
-    short_to_long: bool
-        If True convert the dictionary to long form, otherwise convert to short form
-    hed_schema:str or HedSchema
-        Version number or path or HedSchema object to be used
-
-    Returns
-    -------
-    Response
-        A downloadable dictionary file or a file containing warnings
-    """
-
-    json_dictionary = ColumnDefGroup(arguments.get(common.JSON_PATH, ''))
-
-    if not hed_schema:
-        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
-    issues = []
-    for column_def in json_dictionary:
-        for hed_string, position in column_def.hed_string_iter(include_position=True):
-            hed_string_obj = HedString(hed_string)
-            if short_to_long:
-                errors = hed_string_obj.convert_to_long(hed_schema)
-            else:
-                errors = hed_string_obj.convert_to_short(hed_schema)
-                column_def.set_hed_string(hed_string_obj, position)
-                print(f"'{hed_string_obj.get_original_hed_string()}' \nconverts to\n '{str(hed_string_obj)}'")
-            issues = issues + errors
-            column_def.set_hed_string(hed_string_obj, position)
-    if short_to_long:
-        suffix = '_to_long'
-    else:
-        suffix = '_to_short'
-    issues = ErrorHandler.filter_issues_by_severity(issues, ErrorSeverity.ERROR)
-    display_name = arguments.get(common.JSON_FILE, '')
-    if issues:
-        display_name = arguments.get(common.JSON_FILE, '')
-        issue_str = get_printable_issue_string(issues, f"JSON conversion for {display_name} was unsuccessful")
-        file_name = generate_filename(display_name, suffix=f"{suffix}_conversion_errors", extension='.txt')
-        issue_file = save_text_to_upload_folder(issue_str, file_name)
-        return generate_download_file_response(issue_file, display_name=file_name, category='warning',
-                                               msg='JSON dictionary had conversion errors')
-    else:
-        file_name = generate_filename(display_name, suffix=suffix, extension='.json')
-        file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], file_name)
-        json_dictionary.save_as_json(file_path)
-        return generate_download_file_response(file_path, display_name=file_name, category='success',
-                                               msg='JSON dictionary was successfully converted')
-
-
-def dictionary_validate(input_arguments, hed_schema=None):
-    """ Validates the dictionary and returns a response or a printable string depending on return_response value
-
-    Parameters
-    ----------
-    input_arguments: dict
-        Dictionary containing standard input form arguments
-    hed_schema: str or HedSchema
-        Version number or path or HedSchema object to be used
-
-    Returns
-    -------
-    Response
-        Response object containing the results of the dictionary validation.
-    """
-
-    json_dictionary = ColumnDefGroup(input_arguments.get(common.JSON_PATH, ''))
-    if not hed_schema:
-        hed_schema = load_schema(input_arguments.get(common.HED_XML_FILE, ''))
-    issues = json_dictionary.validate_entries(hed_schema)
-    if issues:
-        display_name = input_arguments.get(common.JSON_FILE, '')
-        issue_str = get_printable_issue_string(issues, f"HED validation errors for {display_name}")
-        file_name = generate_filename(display_name, suffix='validation_errors', extension='.txt')
-        issue_file = save_text_to_upload_folder(issue_str, file_name)
-        return generate_download_file_response(issue_file, display_name=file_name, category='warning',
-                                               msg='JSON dictionary had validation errors')
-    else:
-        return generate_text_response("", msg='JSON dictionary had no validation errors')
+import os
+from flask import current_app
+from werkzeug import Response
+
+from hed.schema.hed_schema_file import load_schema
+from hedweb.constants import common, file_constants
+from hed.util.column_def_group import ColumnDefGroup
+from hed.util.hed_string import HedString
+from hed.util.error_reporter import ErrorHandler, get_printable_issue_string
+from hed.util.error_types import ErrorSeverity
+from hed.util.exceptions import HedFileError
+from hedweb.web_utils import form_has_option, generate_filename, generate_download_file_response, \
+    generate_text_response, get_hed_path_from_pull_down, \
+    get_uploaded_file_path_from_form, save_text_to_upload_folder
+
+app_config = current_app.config
+
+
+def generate_input_from_dictionary_form(request):
+    """Gets the validation function input arguments from a request object associated with the validation form.
+
+    Parameters
+    ----------
+    request: Request object
+        A Request object containing user data from the validation form.
+
+    Returns
+    -------
+    dictionary
+        A dictionary containing input arguments for calling the underlying validation function.
+    """
+    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
+    uploaded_file_name, original_file_name = \
+        get_uploaded_file_path_from_form(request, common.JSON_FILE, file_constants.DICTIONARY_FILE_EXTENSIONS)
+
+    arguments = {
+        common.HED_XML_FILE: hed_file_path,
+        common.HED_DISPLAY_NAME: hed_display_name,
+        common.JSON_PATH: uploaded_file_name,
+        common.JSON_FILE: original_file_name,
+    }
+    if form_has_option(request, common.HED_OPTION, common.HED_OPTION_VALIDATE):
+        arguments[common.HED_OPTION_VALIDATE] = True
+    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_SHORT):
+        arguments[common.HED_OPTION_TO_SHORT] = True
+    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_LONG):
+        arguments[common.HED_OPTION_TO_LONG] = True
+
+    return arguments
+
+
+def dictionary_process(arguments):
+    """Perform the requested action for the dictionary.
+
+    Parameters
+    ----------
+    arguments: dict
+        A dictionary with the input arguments from the dictionary form
+
+    Returns
+    -------
+      Response
+        Downloadable response object.
+    """
+
+    if not arguments[common.JSON_PATH]:
+        raise HedFileError('EmptyDictionaryFile', "Please upload a dictionary to process", "")
+    if arguments.get(common.HED_OPTION_VALIDATE, None):
+        return dictionary_validate(arguments)
+    elif arguments.get(common.HED_OPTION_TO_SHORT, None):
+        return dictionary_convert(arguments, short_to_long=False)
+    elif arguments.get(common.HED_OPTION_TO_LONG, None):
+        return dictionary_convert(arguments)
+    else:
+        raise HedFileError('UnknownProcessingMethod', "Select a dictionary processing method", "")
+
+
+def dictionary_convert(arguments, short_to_long=True, hed_schema=None):
+    """Converts a dictionary from short to long unless short_to_long is set to False, then long_to_short
+
+    Parameters
+    ----------
+    arguments: dict
+        Dictionary containing standard input form arguments
+    short_to_long: bool
+        If True convert the dictionary to long form, otherwise convert to short form
+    hed_schema:str or HedSchema
+        Version number or path or HedSchema object to be used
+
+    Returns
+    -------
+    Response
+        A downloadable dictionary file or a file containing warnings
+    """
+
+    json_dictionary = ColumnDefGroup(arguments.get(common.JSON_PATH, ''))
+
+    if not hed_schema:
+        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
+    results = dictionary_validate(arguments, hed_schema)
+    if isinstance(results, Response):
+        return results
+    issues = []
+    for column_def in json_dictionary:
+        for hed_string, position in column_def.hed_string_iter(include_position=True):
+            hed_string_obj = HedString(hed_string)
+            if short_to_long:
+                errors = hed_string_obj.convert_to_long(hed_schema)
+            else:
+                errors = hed_string_obj.convert_to_short(hed_schema)
+                column_def.set_hed_string(hed_string_obj, position)
+                print(f"'{hed_string_obj.get_original_hed_string()}' \nconverts to\n '{str(hed_string_obj)}'")
+            issues = issues + errors
+            column_def.set_hed_string(hed_string_obj, position)
+    if short_to_long:
+        suffix = '_to_long'
+    else:
+        suffix = '_to_short'
+    issues = ErrorHandler.filter_issues_by_severity(issues, ErrorSeverity.ERROR)
+    display_name = arguments.get(common.JSON_FILE, '')
+    if issues:
+        display_name = arguments.get(common.JSON_FILE, '')
+        issue_str = get_printable_issue_string(issues, f"JSON conversion for {display_name} was unsuccessful")
+        file_name = generate_filename(display_name, suffix=f"{suffix}_conversion_errors", extension='.txt')
+        issue_file = save_text_to_upload_folder(issue_str, file_name)
+        return generate_download_file_response(issue_file, display_name=file_name, category='warning',
+                                               msg='JSON dictionary had conversion errors')
+    else:
+        file_name = generate_filename(display_name, suffix=suffix, extension='.json')
+        file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], file_name)
+        json_dictionary.save_as_json(file_path)
+        return generate_download_file_response(file_path, display_name=file_name, category='success',
+                                               msg='JSON dictionary was successfully converted')
+
+
+def dictionary_validate(input_arguments, hed_schema=None):
+    """ Validates the dictionary and returns a response or a printable string depending on return_response value
+
+    Parameters
+    ----------
+    input_arguments: dict
+        Dictionary containing standard input form arguments
+    hed_schema: str or HedSchema
+        Version number or path or HedSchema object to be used
+
+    Returns
+    -------
+    Response
+        Response object containing the results of the dictionary validation.
+    """
+
+    json_dictionary = ColumnDefGroup(input_arguments.get(common.JSON_PATH, ''))
+    if not hed_schema:
+        hed_schema = load_schema(input_arguments.get(common.HED_XML_FILE, ''))
+    issues = json_dictionary.validate_entries(hed_schema)
+    if issues:
+        display_name = input_arguments.get(common.JSON_FILE, '')
+        issue_str = get_printable_issue_string(issues, f"HED validation errors for {display_name}")
+        file_name = generate_filename(display_name, suffix='validation_errors', extension='.txt')
+        issue_file = save_text_to_upload_folder(issue_str, file_name)
+        return generate_download_file_response(issue_file, display_name=file_name, category='warning',
+                                               msg='JSON dictionary had validation errors')
+    else:
+        return generate_text_response("", msg='JSON dictionary had no validation errors')
diff --git a/webtools/hedweb/events-file_expanded.tsv b/webtools/hedweb/events-file_expanded.tsv
new file mode 100644
index 000000000..9bea74ab6
--- /dev/null
+++ b/webtools/hedweb/events-file_expanded.tsv
@@ -0,0 +1,22 @@
+onset	HED
+15.038933333333	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/1,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+18.038933333333002	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/1,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+18.1556	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/1,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+18.2236	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/1,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+19.8922	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/2,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+22.8922	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/2,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+22.9922	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/2,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+23.0602	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/2,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+24.530466666666694	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/3,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+27.530466666666694	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/3,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+27.5638	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/3,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+27.6318	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/3,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+32.6353333333333	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/4,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+35.6353333333333	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/4,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+35.652	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/4,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+35.72	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/4,Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+37.192733333333294	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/5,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+40.192733333333294	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/5,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+40.2594	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/5,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+40.3274	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/5,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
+45.9162	Description/BCI acts randomly, (Definition/Random-selection, (Experimental-condition, Random, Predict)), Def/Random-selection,Description/The number of the trial in the experiment, Experimental-trial, Item-count/6,Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand)),Description/Computational prediction arrived on time, (Computational-agent, Predict)
diff --git a/webtools/hedweb/events.py b/webtools/hedweb/events.py
index b5822968e..adc783aa4 100644
--- a/webtools/hedweb/events.py
+++ b/webtools/hedweb/events.py
@@ -1,173 +1,170 @@
-from flask import current_app
-
-from hed.schema.hed_schema_file import load_schema
-from hed.util.column_def_group import ColumnDefGroup
-from hed.util.error_reporter import get_printable_issue_string
-from hed.util.event_file_input import EventFileInput
-from hed.util.exceptions import HedFileError
-from hed.validator.hed_validator import HedValidator
-from hedweb.constants import common, file_constants
-
-from hedweb.web_utils import form_has_option, generate_download_file_response,\
-    generate_filename, generate_text_response, \
-    get_hed_path_from_pull_down, get_uploaded_file_path_from_form, save_text_to_upload_folder
-app_config = current_app.config
-
-
-def generate_input_from_events_form(request):
-    """Gets the validation function input arguments from a request object associated with the validation form.
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data from the validation form.
-
-    Returns
-    -------
-    dictionary
-        A dictionary containing input arguments for calling the underlying validation function.
-    """
-    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
-    uploaded_events_path, original_events_name = \
-        get_uploaded_file_path_from_form(request, common.EVENTS_FILE, file_constants.TEXT_FILE_EXTENSIONS)
-    uploaded_json_name, original_json_name = \
-        get_uploaded_file_path_from_form(request, common.JSON_FILE, file_constants.DICTIONARY_FILE_EXTENSIONS)
-
-    arguments = {
-        common.HED_XML_FILE: hed_file_path,
-        common.HED_DISPLAY_NAME: hed_display_name,
-        common.EVENTS_PATH: uploaded_events_path,
-        common.EVENTS_FILE: original_events_name,
-        common.JSON_PATH: uploaded_json_name,
-        common.JSON_FILE: original_json_name,
-    }
-    if form_has_option(request, common.HED_OPTION, common.HED_OPTION_VALIDATE):
-        arguments[common.HED_OPTION_VALIDATE] = True
-    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_TAGGED_LIST):
-        arguments[common.HED_OPTION_TO_SHORT] = True
-    return arguments
-
-
-def events_process(arguments):
-    """Perform the requested action for the dictionary.
-
-    Parameters
-    ----------
-    arguments: dict
-        A dictionary with the input arguments from the dictionary form
-
-    Returns
-    -------
-      Response
-        Downloadable response object.
-    """
-
-    if not arguments[common.EVENTS_PATH]:
-        raise HedFileError('EmptyEventsFile', "Please upload an events file to process", "")
-    if arguments.get(common.HED_OPTION_VALIDATE, None):
-        return events_validate(arguments)
-    elif arguments.get(common.HED_OPTION_TO_TAGGED_LIST, None):
-        return events_convert(arguments)
-    else:
-        raise HedFileError('UnknownProcessingMethod', "Select an events file processing method", "")
-
-
-def events_convert(arguments, hed_schema=None):
-    """Converts an events file into a tagged event list
-
-    Parameters
-    ----------
-    arguments: dict
-        Dictionary containing standard input form arguments
-    hed_schema:str or HedSchema
-        Version number or path or HedSchema object to be used
-
-    Returns
-    -------
-    Response
-        A downloadable spreadsheet file or a file containing warnings
-    """
-
-    return generate_text_response('Not available', category='warning', msg='Spreadsheet conversion not implemented yet')
-    #
-    # if not hed_schema:
-    #     hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
-    # error_handler = ErrorHandler()
-    # tag_formatter = TagFormat(hed_schema=hed_schema, error_handler=error_handler)
-    # issues = []
-    # for column_def in json_dictionary:
-    #     for hed_string, position in column_def.hed_string_iter(include_position=True):
-    #         if short_to_long:
-    #             new_hed_string, errors = tag_formatter.convert_hed_string_to_long(hed_string)
-    #         else:
-    #             new_hed_string, errors = tag_formatter.convert_hed_string_to_short(hed_string)
-    #         issues = issues + errors
-    #         column_def.set_hed_string(new_hed_string, position)
-    # if short_to_long:
-    #     suffix = '_to_long'
-    # else:
-    #     suffix = '_to_short'
-    # issues = ErrorHandler.filter_issues_by_severity(issues, ErrorSeverity.ERROR)
-    # display_name = arguments.get(common.JSON_FILE, '')
-    # if issues:
-    #     display_name = arguments.get(common.JSON_FILE, '')
-    #     issue_str = get_printable_issue_string(issues, f"JSON conversion for {display_name} was unsuccessful")
-    #     file_name = generate_filename(display_name, suffix=f"{suffix}_conversion_errors", extension='.txt')
-    #     issue_file = save_text_to_upload_folder(issue_str, file_name)
-    #     return generate_download_file_response(issue_file, display_name=file_name, category='warning',
-    #                                            msg='JSON dictionary had conversion errors')
-    # else:
-    #     file_name = generate_filename(display_name, suffix=suffix, extension='.json')
-    #     file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], file_name)
-    #     json_dictionary.save_as_json(file_path)
-    #     return generate_download_file_response(file_path, display_name=file_name, category='success',
-    #                                            msg='JSON dictionary was successfully converted')
-
-
-def events_validate(arguments):
-    """Reports the spreadsheet validation status.
-
-    Parameters
-    ----------
-    arguments: dict
-        A dictionary of the values extracted from the form
-
-    Returns
-    -------
-    Response
-         The validation results as a Response object
-    """
-
-    hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
-    json_sidecar = None
-    if arguments.get(common.JSON_PATH, ''):  # If dictionary is provided and it has errors return those errors
-        json_sidecar = ColumnDefGroup(arguments.get(common.JSON_PATH, ''))
-        def_dict, issues = json_sidecar.extract_defs(hed_schema)
-        if issues:
-            issue_str = get_printable_issue_string(issues, f"{common.JSON_FILE} HED dictionary errors")
-            file_name = generate_filename(common.JSON_FILE, suffix='_dictionary_errors', extension='.txt')
-            issue_file = save_text_to_upload_folder(issue_str, file_name)
-            return generate_download_file_response(issue_file, display_name=file_name, category='warning',
-                                                   msg='JSON sidecar had dictionary errors')
-        issues = json_sidecar.validate_entries(hed_schema)
-        if issues:
-            issue_str = get_printable_issue_string(issues, f"{common.JSON_FILE} HED validation errors")
-            file_name = generate_filename(common.JSON_FILE, suffix='_validation_errors', extension='.txt')
-            issue_file = save_text_to_upload_folder(issue_str, file_name)
-            return generate_download_file_response(issue_file, display_name=file_name, category='warning',
-                                                   msg='JSON dictionary had validation errors')
-
-    input_file = EventFileInput(arguments.get(common.EVENTS_PATH),
-                                json_def_files=json_sidecar, hed_schema=hed_schema, def_dicts=def_dict)
-    validator = HedValidator(hed_schema=hed_schema)
-    issues = validator.validate_input(input_file)
-    if issues:
-        display_name = arguments.get(common.EVENTS_FILE, None)
-        issue_str = get_printable_issue_string(issues, f"{display_name} HED validation errors")
-
-        file_name = generate_filename(display_name, suffix='_validation_errors', extension='.txt')
-        issue_file = save_text_to_upload_folder(issue_str, file_name)
-        return generate_download_file_response(issue_file, display_name=file_name, category='warning',
-                                               msg='Events file had validation errors')
-    else:
-        return generate_text_response("", msg='Events file had no validation errors')
+from flask import current_app
+from werkzeug import Response
+import pandas as pd
+
+from hed.schema.hed_schema_file import load_schema
+from hed.util.column_def_group import ColumnDefGroup
+from hed.util.error_reporter import get_printable_issue_string
+from hed.util.event_file_input import EventFileInput
+from hed.util.exceptions import HedFileError
+from hed.validator.hed_validator import HedValidator
+from hedweb.constants import common, file_constants
+
+from hedweb.web_utils import form_has_option, generate_download_file_response,\
+    generate_filename, generate_text_response, \
+    get_hed_path_from_pull_down, get_uploaded_file_path_from_form, save_text_to_upload_folder
+app_config = current_app.config
+
+
+def generate_input_from_events_form(request):
+    """Gets the validation function input arguments from a request object associated with the validation form.
+
+    Parameters
+    ----------
+    request: Request object
+        A Request object containing user data from the validation form.
+
+    Returns
+    -------
+    dictionary
+        A dictionary containing input arguments for calling the underlying validation function.
+    """
+    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
+    uploaded_events_path, original_events_name = \
+        get_uploaded_file_path_from_form(request, common.EVENTS_FILE, file_constants.TEXT_FILE_EXTENSIONS)
+    uploaded_json_name, original_json_name = \
+        get_uploaded_file_path_from_form(request, common.JSON_FILE, file_constants.DICTIONARY_FILE_EXTENSIONS)
+
+    arguments = {
+        common.HED_XML_FILE: hed_file_path,
+        common.HED_DISPLAY_NAME: hed_display_name,
+        common.EVENTS_PATH: uploaded_events_path,
+        common.EVENTS_FILE: original_events_name,
+        common.JSON_PATH: uploaded_json_name,
+        common.JSON_FILE: original_json_name,
+    }
+    if form_has_option(request, common.HED_OPTION, common.HED_OPTION_VALIDATE):
+        arguments[common.HED_OPTION_VALIDATE] = True
+    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_ASSEMBLE):
+        arguments[common.HED_OPTION_ASSEMBLE] = True
+    return arguments
+
+
+def events_process(arguments):
+    """Perform the requested action for the dictionary.
+
+    Parameters
+    ----------
+    arguments: dict
+        A dictionary with the input arguments from the dictionary form
+
+    Returns
+    -------
+      Response
+        Downloadable response object.
+    """
+
+    if not arguments[common.EVENTS_PATH]:
+        raise HedFileError('EmptyEventsFile', "Please upload an events file to process", "")
+    if arguments.get(common.HED_OPTION_VALIDATE, None):
+        return events_validate(arguments)
+    elif arguments.get(common.HED_OPTION_ASSEMBLE, None):
+        return events_assemble(arguments)
+    else:
+        raise HedFileError('UnknownProcessingMethod', "Select an events file processing method", "")
+
+
+def events_assemble(arguments, hed_schema=None):
+    """Converts an events file from short to long unless short_to_long is set to False, then long_to_short
+
+    Parameters
+    ----------
+    arguments: dict
+        Dictionary containing standard input form arguments
+    hed_schema:str or HedSchema
+        Version number or path or HedSchema object to be used
+
+    Returns
+    -------
+    Response
+        A downloadable spreadsheet file or a file containing warnings
+    """
+
+    if not hed_schema:
+        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
+
+    results = events_validate(arguments, hed_schema)
+    if results.data:
+        return results
+
+    if arguments.get(common.JSON_PATH, ''):  # If dictionary is provided and it has errors return those errors
+        json_sidecar = ColumnDefGroup(arguments.get(common.JSON_PATH, ''))
+        def_dict = json_sidecar.extract_defs(hed_schema)
+    else:
+        json_sidecar = None
+        def_dict = None
+    event_file = EventFileInput(arguments.get(common.EVENTS_PATH),
+                                json_def_files=json_sidecar, hed_schema=hed_schema, def_dicts=def_dict)
+    hed_tags = []
+    onsets = []
+    for row_number, row_dict in event_file.parse_dataframe(return_row_dict=True):
+        hed_tags.append(row_dict.get("HED", "").hed_string)
+        onsets.append(row_dict.get("onset", "n/a"))
+    data = {'onset': onsets, 'HED': hed_tags}
+    df = pd.DataFrame(data)
+    file_name = generate_filename(common.EVENTS_FILE, suffix='_expanded', extension='.tsv')
+    df.to_csv(file_name, '\t', index=False, header=True)
+    return generate_download_file_response(file_name, display_name=file_name, category='success',
+                                           msg='Events file successfully expanded')
+
+
+def events_validate(arguments, hed_schema=None):
+    """Reports the spreadsheet validation status.
+
+    Parameters
+    ----------
+    arguments: dict
+        A dictionary of the values extracted from the form
+    hed_schema: str or HedSchema
+        Version number or path or HedSchema object to be used
+
+    Returns
+    -------
+    Response
+         The validation results as a Response object
+    """
+    if not hed_schema:
+        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
+    json_sidecar = None
+    def_dict = None
+    if arguments.get(common.JSON_PATH, ''):  # If dictionary is provided and it has errors return those errors
+        json_sidecar = ColumnDefGroup(arguments.get(common.JSON_PATH, ''))
+        def_dict, issues = json_sidecar.extract_defs(hed_schema)
+        if issues:
+            issue_str = get_printable_issue_string(issues, f"{common.JSON_FILE} HED dictionary errors")
+            file_name = generate_filename(common.JSON_FILE, suffix='_dictionary_errors', extension='.txt')
+            issue_file = save_text_to_upload_folder(issue_str, file_name)
+            return generate_download_file_response(issue_file, display_name=file_name, category='warning',
+                                                   msg='JSON sidecar had dictionary errors')
+        issues = json_sidecar.validate_entries(hed_schema)
+        if issues:
+            issue_str = get_printable_issue_string(issues, f"{common.JSON_FILE} HED validation errors")
+            file_name = generate_filename(common.JSON_FILE, suffix='_validation_errors', extension='.txt')
+            issue_file = save_text_to_upload_folder(issue_str, file_name)
+            return generate_download_file_response(issue_file, display_name=file_name, category='warning',
+                                                   msg='JSON dictionary had validation errors')
+
+    input_file = EventFileInput(arguments.get(common.EVENTS_PATH),
+                                json_def_files=json_sidecar, hed_schema=hed_schema, def_dicts=def_dict)
+    validator = HedValidator(hed_schema=hed_schema)
+    issues = validator.validate_input(input_file)
+    if issues:
+        display_name = arguments.get(common.EVENTS_FILE, None)
+        issue_str = get_printable_issue_string(issues, f"{display_name} HED validation errors")
+
+        file_name = generate_filename(display_name, suffix='_validation_errors', extension='.txt')
+        issue_file = save_text_to_upload_folder(issue_str, file_name)
+        return generate_download_file_response(issue_file, display_name=file_name, category='warning',
+                                               msg='Events file had validation errors')
+    else:
+        return generate_text_response("", msg='Events file had no validation errors')
diff --git a/webtools/hedweb/hedstring.py b/webtools/hedweb/hedstring.py
index 8cc758a28..9b8ff5723 100644
--- a/webtools/hedweb/hedstring.py
+++ b/webtools/hedweb/hedstring.py
@@ -1,125 +1,131 @@
-from flask import current_app
-
-from hed.schema.hed_schema_file import load_schema
-from hed.util.error_reporter import get_printable_issue_string
-from hed.util.hed_string import HedString
-from hed.util.exceptions import HedFileError
-from hed.validator.hed_validator import HedValidator
-from hedweb.constants import common
-from hedweb.web_utils import form_has_option, get_hed_path_from_pull_down
-app_config = current_app.config
-
-
-def generate_input_from_hedstring_form(request):
-    """Gets the validation function input arguments from a request object associated with the validation form.
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data from the validation form.
-
-    Returns
-    -------
-    dictionary
-        A dictionary containing input arguments for calling the underlying validation function.
-    """
-    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
-    arguments = {common.HED_XML_FILE: hed_file_path,
-                 common.HED_DISPLAY_NAME: hed_display_name,
-                 common.HEDSTRING: request.form['hedstring-input'],
-                 common.HED_OPTION_VALIDATE: False,
-                 common.HED_OPTION_TO_SHORT: False,
-                 common.HED_OPTION_TO_LONG: False}
-    if form_has_option(request, common.HED_OPTION, common.HED_OPTION_VALIDATE):
-        arguments[common.HED_OPTION_VALIDATE] = True
-    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_SHORT):
-        arguments[common.HED_OPTION_TO_SHORT] = True
-    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_LONG):
-        arguments[common.HED_OPTION_TO_LONG] = True
-    return arguments
-
-
-def hedstring_process(arguments):
-    """Perform the requested action on the HED string.
-
-    Parameters
-    ----------
-    arguments: dict
-        A dictionary with the input arguments from the hedstring form
-
-    Returns
-    -------
-    Response
-        Downloadable response object.
-    """
-
-    if not arguments.get(common.HEDSTRING, None):
-        raise HedFileError('EmptyHEDString', "Please enter a nonempty HED string to process", "")
-    if arguments.get(common.HED_OPTION_VALIDATE, None):
-        return hedstring_validate(arguments)
-    elif arguments.get(common.HED_OPTION_TO_SHORT, None):
-        return hedstring_convert(arguments, short_to_long=False)
-    elif arguments.get(common.HED_OPTION_TO_LONG, None):
-        return hedstring_convert(arguments)
-    else:
-        raise HedFileError('UnknownProcessingMethod', "Select a hedstring processing method", "")
-
-
-def hedstring_convert(arguments, short_to_long=True, hed_schema=None):
-    """Converts a hedstring from short to long unless short_to_long is set to False, then long_to_short
-
-    Parameters
-    ----------
-    arguments: dict
-        Dictionary containing standard input form arguments
-    short_to_long: bool
-        If True convert the hedstring to long form, otherwise convert to short form
-    hed_schema: HedSchema object
-
-    Returns
-    -------
-    dict
-        A dictionary with hedstring-results as the key
-    """
-
-    if not hed_schema:
-        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
-    hed_string_obj = HedString(arguments.get(common.HEDSTRING))
-    if short_to_long:
-        issues = hed_string_obj.convert_to_long(hed_schema)
-    else:
-        issues = hed_string_obj.convert_to_short(hed_schema)
-
-    if issues:
-        return_str = get_printable_issue_string(issues, "Unable to convert HED string:")
-        category = 'warning'
-    else:
-        return_str = str(hed_string_obj)
-        category = 'success'
-    return {'hedstring-result': return_str, 'category': category}
-
-
-def hedstring_validate(arguments):
-    """Validates a hedstring and returns a dictionary containing the issues or a no errors message
-
-    Parameters
-    ----------
-    arguments: dict
-        Dictionary containing standard input form arguments
-
-    Returns
-    -------
-    dict
-        A dictionary with hedstring-results as the key
-    """
-
-    hed_file_path = arguments.get(common.HED_XML_FILE)
-    hed_validator = HedValidator(hed_xml_file=hed_file_path)
-    issues = hed_validator.validate_input(arguments.get(common.HEDSTRING))
-    if issues:
-        return_str = get_printable_issue_string(issues, "HED validation errors:")
-        category = 'warning'
-    else:
-        return_str = "No validation errors found..."
-        category = 'success'
-    return {'hedstring-result': return_str, 'category': category}
+from flask import current_app
+from werkzeug import Response
+from hed.schema.hed_schema_file import load_schema
+from hed.util.error_reporter import get_printable_issue_string
+from hed.util.hed_string import HedString
+from hed.util.exceptions import HedFileError
+from hed.validator.hed_validator import HedValidator
+from hedweb.constants import common
+from hedweb.web_utils import form_has_option, get_hed_path_from_pull_down
+app_config = current_app.config
+
+
+def generate_input_from_hedstring_form(request):
+    """Gets the validation function input arguments from a request object associated with the validation form.
+
+    Parameters
+    ----------
+    request: Request object
+        A Request object containing user data from the validation form.
+
+    Returns
+    -------
+    dictionary
+        A dictionary containing input arguments for calling the underlying validation function.
+    """
+    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
+    arguments = {common.HED_XML_FILE: hed_file_path,
+                 common.HED_DISPLAY_NAME: hed_display_name,
+                 common.HEDSTRING: request.form['hedstring-input'],
+                 common.HED_OPTION_VALIDATE: False,
+                 common.HED_OPTION_TO_SHORT: False,
+                 common.HED_OPTION_TO_LONG: False}
+    if form_has_option(request, common.HED_OPTION, common.HED_OPTION_VALIDATE):
+        arguments[common.HED_OPTION_VALIDATE] = True
+    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_SHORT):
+        arguments[common.HED_OPTION_TO_SHORT] = True
+    elif form_has_option(request, common.HED_OPTION, common.HED_OPTION_TO_LONG):
+        arguments[common.HED_OPTION_TO_LONG] = True
+    return arguments
+
+
+def hedstring_process(arguments):
+    """Perform the requested action on the HED string.
+
+    Parameters
+    ----------
+    arguments: dict
+        A dictionary with the input arguments from the hedstring form
+
+    Returns
+    -------
+    Response
+        Downloadable response object.
+    """
+
+    if not arguments.get(common.HEDSTRING, None):
+        raise HedFileError('EmptyHEDString', "Please enter a nonempty HED string to process", "")
+    if arguments.get(common.HED_OPTION_VALIDATE, None):
+        return hedstring_validate(arguments)
+    elif arguments.get(common.HED_OPTION_TO_SHORT, None):
+        return hedstring_convert(arguments, short_to_long=False)
+    elif arguments.get(common.HED_OPTION_TO_LONG, None):
+        return hedstring_convert(arguments)
+    else:
+        raise HedFileError('UnknownProcessingMethod', "Select a hedstring processing method", "")
+
+
+def hedstring_convert(arguments, short_to_long=True, hed_schema=None):
+    """Converts a hedstring from short to long unless short_to_long is set to False, then long_to_short
+
+    Parameters
+    ----------
+    arguments: dict
+        Dictionary containing standard input form arguments
+    short_to_long: bool
+        If True convert the hedstring to long form, otherwise convert to short form
+    hed_schema: HedSchema object
+
+    Returns
+    -------
+    dict
+        A dictionary with hedstring-results as the key
+    """
+
+    if not hed_schema:
+        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
+    hed_string_obj = HedString(arguments.get(common.HEDSTRING))
+    results = hedstring_validate(arguments, hed_schema)
+    if results['category'] == 'warning':
+        return results
+    if short_to_long:
+        issues = hed_string_obj.convert_to_long(hed_schema)
+    else:
+        issues = hed_string_obj.convert_to_short(hed_schema)
+
+    if issues:
+        return_str = get_printable_issue_string(issues, "Unable to convert HED string:")
+        category = 'warning'
+    else:
+        return_str = str(hed_string_obj)
+        category = 'success'
+    return {'hedstring-result': return_str, 'category': category}
+
+
+def hedstring_validate(arguments, hed_schema=None):
+    """Validates a hedstring and returns a dictionary containing the issues or a no errors message
+
+    Parameters
+    ----------
+    arguments: dict
+        Dictionary containing standard input form arguments
+    hed_schema: str or HedSchema
+        Version number or path or HedSchema object to be used
+
+    Returns
+    -------
+    dict
+        A dictionary with hedstring-results as the key
+    """
+
+    if not hed_schema:
+        hed_schema = load_schema(arguments.get(common.HED_XML_FILE, ''))
+    hed_validator = HedValidator(hed_schema=hed_schema)
+    issues = hed_validator.validate_input(arguments.get(common.HEDSTRING))
+    if issues:
+        return_str = get_printable_issue_string(issues, "HED validation errors:")
+        category = 'warning'
+    else:
+        return_str = "No validation errors found..."
+        category = 'success'
+    return {'hedstring-result': return_str, 'category': category}
diff --git a/webtools/hedweb/routes.py b/webtools/hedweb/routes.py
index f58dbcab9..dc7c55007 100644
--- a/webtools/hedweb/routes.py
+++ b/webtools/hedweb/routes.py
@@ -1,324 +1,324 @@
-from flask import render_template, request, Blueprint, current_app
-import json
-
-from hed.util import hed_cache
-from hed.schema import hed_schema_file
-from hedweb.constants import common, page_constants
-from hedweb.constants import route_constants
-from hedweb.web_utils import delete_file_no_exceptions, \
-   handle_http_error, handle_error, save_file_to_upload_folder
-from hedweb import dictionary, events, schema, spreadsheet, services
-from hedweb.hedstring import generate_input_from_hedstring_form, hedstring_process
-from hedweb.spreadsheet_utils import generate_input_columns_info, get_columns_info
-
-app_config = current_app.config
-route_blueprint = Blueprint(route_constants.ROUTE_BLUEPRINT, __name__)
-
-
-@route_blueprint.route(route_constants.COLUMN_INFO_ROUTE, methods=['POST'])
-def get_columns_info_results():
-    """Gets the names of the spreadsheet columns and column indices that contain HED tags.
-
-    Returns
-    -------
-    string
-        A serialized JSON string containing information related to the spreadsheet columns.
-
-    """
-    input_arguments = {}
-    try:
-        input_arguments = generate_input_columns_info(request)
-        columns_info = get_columns_info(input_arguments)
-        return json.dumps(columns_info)
-    except Exception as ex:
-        return handle_error(ex)
-    finally:
-        delete_file_no_exceptions(input_arguments.get(common.COLUMNS_PATH, ''))
-
-
-@route_blueprint.route(route_constants.DICTIONARY_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
-def get_dictionary_results():
-    """Process the JSON dictionary in the form after submission and return an attachment containing the output.
-
-    Returns
-    -------
-        download file
-        A text file with the validation errors.
-    """
-    input_arguments = {}
-    try:
-        input_arguments = dictionary.generate_input_from_dictionary_form(request)
-        return dictionary.dictionary_process(input_arguments)
-    except Exception as ex:
-        return handle_http_error(ex)
-    finally:
-        delete_file_no_exceptions(input_arguments.get(common.JSON_PATH, ''))
-
-
-@route_blueprint.route(route_constants.EVENTS_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
-def get_events_results():
-    """Process the events file and JSON sidecar in the form and return an attachment with results.
-
-    Returns
-    -------
-        downloadable file
-        Contains the results of processing
-    """
-    input_arguments = {}
-    try:
-        input_arguments = events.generate_input_from_events_form(request)
-        return events.events_process(input_arguments)
-    except Exception as ex:
-        return handle_http_error(ex)
-    finally:
-        delete_file_no_exceptions(input_arguments.get(common.SPREADSHEET_PATH, ''))
-        delete_file_no_exceptions(input_arguments.get(common.JSON_PATH, ''))
-
-
-@route_blueprint.route(route_constants.HED_SERVICES_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
-def get_hed_services_results():
-    """Perform the requested hed hedweb service and return the results in JSON.
-
-    Returns
-    -------
-        string
-        A serialized JSON string containing processed information.
-    """
-    try:
-        form_data = request.data
-        form_string = form_data.decode()
-        arguments = json.loads(form_string)
-        status = services.services_process(arguments)
-        return json.dumps(status)
-    except Exception as ex:
-        return handle_error(ex)
-
-
-@route_blueprint.route(route_constants.HED_VERSION_ROUTE, methods=['POST'])
-def get_hed_version():
-    """Finds the information about the HED version of a file and returns as JSON.
-
-    Parameters
-    ----------
-
-    Returns
-    -------
-    string
-        A serialized JSON string containing information related to the spreadsheet columns.
-
-    """
-
-    try:
-        hed_info = {}
-        if common.HED_XML_FILE in request.files:
-            hed_file_path = save_file_to_upload_folder(request.files[common.HED_XML_FILE])
-            hed_info[common.HED_VERSION] = hed_schema_file.get_hed_xml_version(hed_file_path)
-        return json.dumps(hed_info)
-    except Exception as ex:
-        return handle_error(ex)
-    finally:
-        delete_file_no_exceptions(request.files[common.HED_XML_FILE])
-
-
-@route_blueprint.route(route_constants.HEDSTRING_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
-def get_hedstring_results():
-    """Process hed strings entered in a text box.
-
-    Returns
-    -------
-        A serialized JSON string
-
-    """
-
-    try:
-        input_arguments = generate_input_from_hedstring_form(request)
-        return json.dumps(hedstring_process(input_arguments))
-    except Exception as ex:
-        return handle_error(ex)
-
-
-@route_blueprint.route(route_constants.HED_MAJOR_VERSION_ROUTE, methods=['GET'])
-def get_major_hed_versions():
-    """Gets a list of major hed versions from the hed_cache and returns as a serialized JSON string
-
-    Returns
-    -------
-    string
-        A serialized JSON string containing a list of the HED versions.
-
-    """
-
-    try:
-        hed_cache.cache_all_hed_xml_versions()
-        hed_info = {common.HED_MAJOR_VERSIONS: hed_cache.get_all_hed_versions()}
-        return json.dumps(hed_info)
-    except Exception as ex:
-        return handle_error(ex)
-
-
-@route_blueprint.route(route_constants.SCHEMA_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
-def get_schema_results():
-    """Get the results of schema processing.
-
-    Returns
-    -------
-        downloadable file
-
-    """
-    input_arguments = {}
-    try:
-        input_arguments = schema.generate_input_from_schema_form(request)
-        return schema.schema_process(input_arguments)
-    except Exception as ex:
-        return handle_http_error(ex)
-    finally:
-        delete_file_no_exceptions(input_arguments.get(common.SCHEMA_PATH, ''))
-
-
-@route_blueprint.route(route_constants.SPREADSHEET_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
-def get_spreadsheet_results():
-    """Process the spreadsheet in the form and return an attachment with the results.
-
-    Returns
-    -------
-        string
-        Validation errors in readable format.
-    """
-    input_arguments = {}
-    try:
-        input_arguments = spreadsheet.generate_input_from_spreadsheet_form(request)
-        return spreadsheet.spreadsheet_process(input_arguments)
-    except Exception as ex:
-        return handle_http_error(ex)
-    finally:
-        delete_file_no_exceptions(input_arguments.get(common.SPREADSHEET_PATH, ''))
-
-
-@route_blueprint.route(route_constants.ADDITIONAL_EXAMPLES_ROUTE, strict_slashes=False, methods=['GET'])
-def render_additional_examples_page():
-    """The site additional examples page.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the additional examples page.
-
-    """
-    return render_template(page_constants.ADDITIONAL_EXAMPLES_PAGE)
-
-
-@route_blueprint.route(route_constants.COMMON_ERRORS_ROUTE, strict_slashes=False, methods=['GET'])
-def render_common_errors_page():
-    """The common errors page.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for a page explaining common errors.
-
-    """
-    return render_template(page_constants.COMMON_ERRORS_PAGE)
-
-
-@route_blueprint.route(route_constants.DICTIONARY_ROUTE, strict_slashes=False, methods=['GET'])
-def render_dictionary_form():
-    """Page with the dictionary processing form.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the dictionary form.
-
-    """
-    return render_template(page_constants.DICTIONARY_PAGE)
-
-
-@route_blueprint.route(route_constants.EVENTS_ROUTE, strict_slashes=False, methods=['GET'])
-def render_events_form():
-    """The form for BIDS event file (with JSON sidecar) processing.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the events form.
-
-    """
-    return render_template(page_constants.EVENTS_PAGE)
-
-
-@route_blueprint.route(route_constants.HED_SERVICES_ROUTE, strict_slashes=False, methods=['GET'])
-def render_hed_services_form():
-    """Landing page for HED hedweb services designed to be called from programs such as MATLAB.
-
-    Returns
-    -------
-    Rendered template
-        A dummy rendered template so that the service can get a csrf token.
-
-    """
-    return render_template(page_constants.HED_SERVICES_PAGE)
-
-
-@route_blueprint.route(route_constants.HEDSTRING_ROUTE, strict_slashes=False, methods=['GET'])
-def render_hedstring_form():
-    """Renders a form for different hedstring operations.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the hedstring form.
-
-    """
-    return render_template(page_constants.HEDSTRING_PAGE)
-
-
-@route_blueprint.route(route_constants.HED_TOOLS_HELP_ROUTE, strict_slashes=False, methods=['GET'])
-def render_help_page():
-    """The site help page.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the help page.
-
-    """
-    return render_template(page_constants.HED_TOOLS_HELP_PAGE)
-
-
-@route_blueprint.route(route_constants.HED_TOOLS_HOME_ROUTE, strict_slashes=False, methods=['GET'])
-def render_home_page():
-    """The home page.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the home page.
-
-    """
-    return render_template(page_constants.HED_TOOLS_HOME_PAGE)
-
-
-@route_blueprint.route(route_constants.SCHEMA_ROUTE, strict_slashes=False, methods=['GET'])
-def render_schema_form():
-    """Handles the site root and conversion tab functionality.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the schema processing form.
-
-    """
-    return render_template(page_constants.SCHEMA_PAGE)
-
-
-@route_blueprint.route(route_constants.SPREADSHEET_ROUTE, strict_slashes=False, methods=['GET'])
-def render_spreadsheet_form():
-    """Displays the spreadsheet Validation form.
-
-    Returns
-    -------
-    Rendered template
-        A rendered template for the spreadsheet hed tags form.
-
-    """
-    return render_template(page_constants.SPREADSHEET_PAGE)
+from flask import render_template, request, Blueprint, current_app
+import json
+
+from hed.util import hed_cache
+from hed.schema import hed_schema_file
+from hedweb.constants import common, page_constants
+from hedweb.constants import route_constants
+from hedweb.web_utils import delete_file_no_exceptions, \
+   handle_http_error, handle_error, save_file_to_upload_folder
+from hedweb import dictionary, events, schema, spreadsheet, services
+from hedweb.hedstring import generate_input_from_hedstring_form, hedstring_process
+from hedweb.spreadsheet_utils import generate_input_columns_info, get_columns_info
+
+app_config = current_app.config
+route_blueprint = Blueprint(route_constants.ROUTE_BLUEPRINT, __name__)
+
+
+@route_blueprint.route(route_constants.COLUMN_INFO_ROUTE, methods=['POST'])
+def get_columns_info_results():
+    """Gets the names of the spreadsheet columns and column indices that contain HED tags.
+
+    Returns
+    -------
+    string
+        A serialized JSON string containing information related to the spreadsheet columns.
+
+    """
+    input_arguments = {}
+    try:
+        input_arguments = generate_input_columns_info(request)
+        columns_info = get_columns_info(input_arguments)
+        return json.dumps(columns_info)
+    except Exception as ex:
+        return handle_error(ex)
+    finally:
+        delete_file_no_exceptions(input_arguments.get(common.COLUMNS_PATH, ''))
+
+
+@route_blueprint.route(route_constants.DICTIONARY_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
+def get_dictionary_results():
+    """Process the JSON dictionary in the form after submission and return an attachment containing the output.
+
+    Returns
+    -------
+        download file
+        A text file with the validation errors.
+    """
+    input_arguments = {}
+    try:
+        input_arguments = dictionary.generate_input_from_dictionary_form(request)
+        return dictionary.dictionary_process(input_arguments)
+    except Exception as ex:
+        return handle_http_error(ex)
+    finally:
+        delete_file_no_exceptions(input_arguments.get(common.JSON_PATH, ''))
+
+
+@route_blueprint.route(route_constants.EVENTS_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
+def get_events_results():
+    """Process the events file and JSON sidecar in the form and return an attachment with results.
+
+    Returns
+    -------
+        downloadable file
+        Contains the results of processing
+    """
+    input_arguments = {}
+    try:
+        input_arguments = events.generate_input_from_events_form(request)
+        return events.events_process(input_arguments)
+    except Exception as ex:
+        return handle_http_error(ex)
+    finally:
+        delete_file_no_exceptions(input_arguments.get(common.EVENTS_PATH, ''))
+        delete_file_no_exceptions(input_arguments.get(common.JSON_PATH, ''))
+
+
+@route_blueprint.route(route_constants.HED_SERVICES_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
+def get_hed_services_results():
+    """Perform the requested web service and return the results in JSON.
+
+    Returns
+    -------
+        string
+        A serialized JSON string containing processed information.
+    """
+    try:
+        form_data = request.data
+        form_string = form_data.decode()
+        arguments = json.loads(form_string)
+        status = services.services_process(arguments)
+        return json.dumps(status)
+    except Exception as ex:
+        return handle_error(ex)
+
+
+@route_blueprint.route(route_constants.HED_VERSION_ROUTE, methods=['POST'])
+def get_hed_version():
+    """Finds the information about the HED version of a file and returns as JSON.
+
+    Parameters
+    ----------
+
+    Returns
+    -------
+    string
+        A serialized JSON string containing information related to the spreadsheet columns.
+
+    """
+
+    try:
+        hed_info = {}
+        if common.HED_XML_FILE in request.files:
+            hed_file_path = save_file_to_upload_folder(request.files[common.HED_XML_FILE])
+            hed_info[common.HED_VERSION] = hed_schema_file.get_hed_xml_version(hed_file_path)
+        return json.dumps(hed_info)
+    except Exception as ex:
+        return handle_error(ex)
+    finally:
+        delete_file_no_exceptions(request.files[common.HED_XML_FILE])
+
+
+@route_blueprint.route(route_constants.HEDSTRING_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
+def get_hedstring_results():
+    """Process hed strings entered in a text box.
+
+    Returns
+    -------
+        A serialized JSON string
+
+    """
+
+    try:
+        input_arguments = generate_input_from_hedstring_form(request)
+        return json.dumps(hedstring_process(input_arguments))
+    except Exception as ex:
+        return handle_error(ex)
+
+
+@route_blueprint.route(route_constants.HED_MAJOR_VERSION_ROUTE, methods=['GET'])
+def get_major_hed_versions():
+    """Gets a list of major hed versions from the hed_cache and returns as a serialized JSON string
+
+    Returns
+    -------
+    string
+        A serialized JSON string containing a list of the HED versions.
+
+    """
+
+    try:
+        hed_cache.cache_all_hed_xml_versions()
+        hed_info = {common.HED_MAJOR_VERSIONS: hed_cache.get_all_hed_versions()}
+        return json.dumps(hed_info)
+    except Exception as ex:
+        return handle_error(ex)
+
+
+@route_blueprint.route(route_constants.SCHEMA_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
+def get_schema_results():
+    """Get the results of schema processing.
+
+    Returns
+    -------
+        downloadable file
+
+    """
+    input_arguments = {}
+    try:
+        input_arguments = schema.generate_input_from_schema_form(request)
+        return schema.schema_process(input_arguments)
+    except Exception as ex:
+        return handle_http_error(ex)
+    finally:
+        delete_file_no_exceptions(input_arguments.get(common.SCHEMA_PATH, ''))
+
+
+@route_blueprint.route(route_constants.SPREADSHEET_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
+def get_spreadsheet_results():
+    """Process the spreadsheet in the form and return an attachment with the results.
+
+    Returns
+    -------
+        string
+        Validation errors in readable format.
+    """
+    input_arguments = {}
+    try:
+        input_arguments = spreadsheet.generate_input_from_spreadsheet_form(request)
+        return spreadsheet.spreadsheet_process(input_arguments)
+    except Exception as ex:
+        return handle_http_error(ex)
+    finally:
+        delete_file_no_exceptions(input_arguments.get(common.SPREADSHEET_PATH, ''))
+
+
+@route_blueprint.route(route_constants.ADDITIONAL_EXAMPLES_ROUTE, strict_slashes=False, methods=['GET'])
+def render_additional_examples_page():
+    """The site additional examples page.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the additional examples page.
+
+    """
+    return render_template(page_constants.ADDITIONAL_EXAMPLES_PAGE)
+
+
+@route_blueprint.route(route_constants.COMMON_ERRORS_ROUTE, strict_slashes=False, methods=['GET'])
+def render_common_errors_page():
+    """The common errors page.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for a page explaining common errors.
+
+    """
+    return render_template(page_constants.COMMON_ERRORS_PAGE)
+
+
+@route_blueprint.route(route_constants.DICTIONARY_ROUTE, strict_slashes=False, methods=['GET'])
+def render_dictionary_form():
+    """Page with the dictionary processing form.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the dictionary form.
+
+    """
+    return render_template(page_constants.DICTIONARY_PAGE)
+
+
+@route_blueprint.route(route_constants.EVENTS_ROUTE, strict_slashes=False, methods=['GET'])
+def render_events_form():
+    """The form for BIDS event file (with JSON sidecar) processing.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the events form.
+
+    """
+    return render_template(page_constants.EVENTS_PAGE)
+
+
+@route_blueprint.route(route_constants.HED_SERVICES_ROUTE, strict_slashes=False, methods=['GET'])
+def render_hed_services_form():
+    """Landing page for HED hedweb services designed to be called from programs such as MATLAB.
+
+    Returns
+    -------
+    Rendered template
+        A dummy rendered template so that the service can get a csrf token.
+
+    """
+    return render_template(page_constants.HED_SERVICES_PAGE)
+
+
+@route_blueprint.route(route_constants.HEDSTRING_ROUTE, strict_slashes=False, methods=['GET'])
+def render_hedstring_form():
+    """Renders a form for different hedstring operations.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the hedstring form.
+
+    """
+    return render_template(page_constants.HEDSTRING_PAGE)
+
+
+@route_blueprint.route(route_constants.HED_TOOLS_HELP_ROUTE, strict_slashes=False, methods=['GET'])
+def render_help_page():
+    """The site help page.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the help page.
+
+    """
+    return render_template(page_constants.HED_TOOLS_HELP_PAGE)
+
+
+@route_blueprint.route(route_constants.HED_TOOLS_HOME_ROUTE, strict_slashes=False, methods=['GET'])
+def render_home_page():
+    """The home page.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the home page.
+
+    """
+    return render_template(page_constants.HED_TOOLS_HOME_PAGE)
+
+
+@route_blueprint.route(route_constants.SCHEMA_ROUTE, strict_slashes=False, methods=['GET'])
+def render_schema_form():
+    """Handles the site root and conversion tab functionality.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the schema processing form.
+
+    """
+    return render_template(page_constants.SCHEMA_PAGE)
+
+
+@route_blueprint.route(route_constants.SPREADSHEET_ROUTE, strict_slashes=False, methods=['GET'])
+def render_spreadsheet_form():
+    """Displays the spreadsheet Validation form.
+
+    Returns
+    -------
+    Rendered template
+        A rendered template for the spreadsheet hed tags form.
+
+    """
+    return render_template(page_constants.SPREADSHEET_PAGE)
diff --git a/webtools/hedweb/templates/events.html b/webtools/hedweb/templates/events.html
index 29b865c68..be7257472 100644
--- a/webtools/hedweb/templates/events.html
+++ b/webtools/hedweb/templates/events.html
@@ -1,59 +1,59 @@
-{% extends "layout.html" %}
-{% from "hed-pulldown.html" import create_hed_pulldown %}
-{% from "column-info.html" import create_column_info %}
-{% from "json-input.html" import create_json_input %}
-{% block content %}
-    <h2>Validate BIDS-style event file</h2>
-    <p>
-        <i>Upload a BIDS-style tsv events file and an optional JSON events file.<br>
-            Select a HED schema, selected a processing option, and press Process.<br>
-            You will see either a downloadable results file or a status message.
-        </i>
-    </p>
-    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/forms.css') }}">
-    <form id="events-form" method="POST" enctype="multipart/form-data">
-        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
-
-        <h3>Upload BIDS-style events file:</h3>
-        <div class="form-group">
-            <label class="secondary-label" for="events-file">Events file:</label>
-            <label class="btn-group file-browse-group">
-                <button for="events-file">Browse ...</button>
-                <label id="events-display-name"></label>
-                <input type="file" name="events-file" id="events-file"/>
-            </label>
-            <p class="flash" id="events-flash"></p>
-        </div>
-
-        {{ create_column_info('false') }}
-        {{ create_json_input() }}
-        {{ create_hed_pulldown() }}
-
-        <h3>Pick an action: </h3>
-        <div class="form-group">
-            <input class="radio-btn" type="radio" name="hed-option" checked
-                   id="hed-option-validate" value="hed-option-validate" aria-checked="true"
-                   aria-label="Select this button to validate HED">
-            <label class="secondary-label">Validate</label>
-        </div>
-        <div class="form-group">
-            <input class="radio-btn" type="radio" name="hed-option"
-                   id="hed-option-to-tagged-list" value="hed-option-to-tagged-list"
-                   aria-label="Select this button to convert hed to a list of tagged events">
-            <label class="secondary-label">Convert to tagged list</label>
-        </div>
-
-        <div class="form-group">
-            <button id="events-validation-submit" type="button">Validate</button>
-            <p class="flash" id="events-validation-submit-flash"></p>
-        </div>
-    </form>
-    <script type="module">
-        {%  include 'js/column-info.js' %}
-        {%  include 'js/form-helpers.js' %}
-        {%  include 'js/hed-pulldown.js' %}
-        {%  include 'js/json-input.js' %}
-        {%  include 'js/events-form.js' %}
-    </script>
-
+{% extends "layout.html" %}
+{% from "hed-pulldown.html" import create_hed_pulldown %}
+{% from "column-info.html" import create_column_info %}
+{% from "json-input.html" import create_json_input %}
+{% block content %}
+    <h2>Validate BIDS-style event file</h2>
+    <p>
+        <i>Upload a BIDS-style tsv events file and an optional JSON events file.<br>
+            Select a HED schema, selected a processing option, and press Process.<br>
+            You will see either a downloadable results file or a status message.
+        </i>
+    </p>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/forms.css') }}">
+    <form id="events-form" method="POST" enctype="multipart/form-data">
+        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
+
+        <h3>Upload BIDS-style events file:</h3>
+        <div class="form-group">
+            <label class="secondary-label" for="events-file">Events file:</label>
+            <label class="btn-group file-browse-group">
+                <button for="events-file">Browse ...</button>
+                <label id="events-display-name"></label>
+                <input type="file" name="events-file" id="events-file"/>
+            </label>
+            <p class="flash" id="events-flash"></p>
+        </div>
+
+        {{ create_column_info('false') }}
+        {{ create_json_input() }}
+        {{ create_hed_pulldown() }}
+
+        <h3>Pick an action: </h3>
+        <div class="form-group">
+            <input class="radio-btn" type="radio" name="hed-option" checked
+                   id="hed-option-validate" value="hed-option-validate" aria-checked="true"
+                   aria-label="Select this button to validate HED">
+            <label class="secondary-label">Validate</label>
+        </div>
+        <div class="form-group">
+            <input class="radio-btn" type="radio" name="hed-option"
+                   id="hed-option-assemble" value="hed-option-assemble"
+                   aria-label="Select this button to assemble all event tags">
+            <label class="secondary-label">Assemble HED annotations</label>
+        </div>
+
+        <div class="form-group">
+            <button id="events-submit" type="button">Process</button>
+            <p class="flash" id="events-submit-flash"></p>
+        </div>
+    </form>
+    <script type="module">
+        {%  include 'js/column-info.js' %}
+        {%  include 'js/form-helpers.js' %}
+        {%  include 'js/hed-pulldown.js' %}
+        {%  include 'js/json-input.js' %}
+        {%  include 'js/events-form.js' %}
+    </script>
+
 {% endblock %}
\ No newline at end of file
diff --git a/webtools/hedweb/templates/js/events-form.js b/webtools/hedweb/templates/js/events-form.js
index 20424dc76..cc20033be 100644
--- a/webtools/hedweb/templates/js/events-form.js
+++ b/webtools/hedweb/templates/js/events-form.js
@@ -1,103 +1,103 @@
-const TEXT_FILE_EXTENSIONS = ['tsv', 'txt'];
-
-$(function () {
-    prepareForm();
-});
-
-
-/**
- * Events file handler function. Checks if the file uploaded has a valid spreadsheet extension.
- */
-$('#events-file').on('change', function () {
-    let events = $('#events-file');
-    let eventsPath = events.val();
-    let eventsFile = events[0].files[0];
-    clearFlashMessages();
-    removeColumnTable();
-    if (cancelWasPressedInChromeFileUpload(eventsPath)) {
-        clearForm();
-        return;
-    }
-    updateFileLabel(eventsPath, '#events-display-name');
-    if (fileHasValidExtension(eventsPath, TEXT_FILE_EXTENSIONS)) {
-        setColumnsInfo(eventsFile, undefined, false, 'events-flash');
-    } else {
-        clearForm();
-        flashMessageOnScreen('Please upload a tsv file (.tsv, .txt)', 'error', 'events-flash');
-    }
-});
-
-/**
- * Submits the form if there is an events file and an available hed schema
- */
-$('#events-validation-submit').on('click', function () {
-    if (fileIsSpecified('#events-file', 'events-flash', 'Events file is not specified.')
-        && hedSpecifiedWhenOtherIsSelected()) {
-        submitForm();
-    }
-});
-
-
-/**
- * Clears the fields in the form.
- */
-function clearForm() {
-    $('#events-form')[0].reset();
-    $('#events-display-name').text('');
-    clearFlashMessages();
-    hideColumnNames();
-    hideOtherHEDVersionFileUpload();
-}
-
-/**
- * Clear the flash messages that aren't related to the form submission.
- */
-function clearFlashMessages() {
-    clearColumnInfoFlashMessages();
-    clearHedSelectFlashMessages();
-    clearJsonInputFlashMessages();
-    flashMessageOnScreen('', 'success', 'events-flash');
-    flashMessageOnScreen('', 'success', 'events-validation-submit-flash');
-}
-
-
-/**
- * Prepare the validation form after the page is ready. The form will be reset to handle page refresh and
- * components will be hidden and populated.
- */
-function prepareForm() {
-    clearForm();
-    getHedVersions()
-    hideColumnNames();
-    hideOtherHEDVersionFileUpload();
-}
-
-/**
- * Submit the form and return the validation results. If there are issues then they are returned in an attachment
- * file.
- */
-function submitForm() {
-    let eventsForm = document.getElementById("events-form");
-    let formData = new FormData(eventsForm);
-    let prefix = 'issues';
-    let eventsFile = $('#events-file')[0].files[0].name;
-    let display_name = convertToResultsName(eventsFile, prefix)
-    clearFlashMessages();
-    flashMessageOnScreen('Worksheet is being validated ...', 'success',
-        'events-validation-submit-flash')
-    $.ajax({
-            type: 'POST',
-            url: "{{url_for('route_blueprint.get_events_results')}}",
-            data: formData,
-            contentType: false,
-            processData: false,
-            dataType: 'text',
-            success: function (download, status, xhr) {
-                getResponseSuccess(download, xhr, display_name, 'events-validation-submit-flash')
-            },
-            error: function (download, status, xhr) {
-                getResponseFailure(download, xhr, display_name, 'events-validation-submit-flash')
-            }
-        }
-    )
+const TEXT_FILE_EXTENSIONS = ['tsv', 'txt'];
+
+$(function () {
+    prepareForm();
+});
+
+
+/**
+ * Events file handler function. Checks if the file uploaded has a valid spreadsheet extension.
+ */
+$('#events-file').on('change', function () {
+    let events = $('#events-file');
+    let eventsPath = events.val();
+    let eventsFile = events[0].files[0];
+    clearFlashMessages();
+    removeColumnTable();
+    if (cancelWasPressedInChromeFileUpload(eventsPath)) {
+        clearForm();
+        return;
+    }
+    updateFileLabel(eventsPath, '#events-display-name');
+    if (fileHasValidExtension(eventsPath, TEXT_FILE_EXTENSIONS)) {
+        setColumnsInfo(eventsFile, undefined, false, 'events-flash');
+    } else {
+        clearForm();
+        flashMessageOnScreen('Please upload a tsv file (.tsv, .txt)', 'error', 'events-flash');
+    }
+});
+
+/**
+ * Submits the form if there is an events file and an available hed schema
+ */
+$('#events-submit').on('click', function () {
+    if (fileIsSpecified('#events-file', 'events-flash', 'Events file is not specified.')
+        && hedSpecifiedWhenOtherIsSelected()) {
+        submitForm();
+    }
+});
+
+
+/**
+ * Clears the fields in the form.
+ */
+function clearForm() {
+    $('#events-form')[0].reset();
+    $('#events-display-name').text('');
+    clearFlashMessages();
+    hideColumnNames();
+    hideOtherHEDVersionFileUpload();
+}
+
+/**
+ * Clear the flash messages that aren't related to the form submission.
+ */
+function clearFlashMessages() {
+    clearColumnInfoFlashMessages();
+    clearHedSelectFlashMessages();
+    clearJsonInputFlashMessages();
+    flashMessageOnScreen('', 'success', 'events-flash');
+    flashMessageOnScreen('', 'success', 'events-submit-flash');
+}
+
+
+/**
+ * Prepare the validation form after the page is ready. The form will be reset to handle page refresh and
+ * components will be hidden and populated.
+ */
+function prepareForm() {
+    clearForm();
+    getHedVersions()
+    hideColumnNames();
+    hideOtherHEDVersionFileUpload();
+}
+
+/**
+ * Submit the form and return the validation results. If there are issues then they are returned in an attachment
+ * file.
+ */
+function submitForm() {
+    let eventsForm = document.getElementById("events-form");
+    let formData = new FormData(eventsForm);
+    let prefix = 'issues';
+    let eventsFile = $('#events-file')[0].files[0].name;
+    let display_name = convertToResultsName(eventsFile, prefix)
+    clearFlashMessages();
+    flashMessageOnScreen('Worksheet is being validated ...', 'success',
+        'events-submit-flash')
+    $.ajax({
+            type: 'POST',
+            url: "{{url_for('route_blueprint.get_events_results')}}",
+            data: formData,
+            contentType: false,
+            processData: false,
+            dataType: 'text',
+            success: function (download, status, xhr) {
+                getResponseSuccess(download, xhr, display_name, 'events-submit-flash')
+            },
+            error: function (download, status, xhr) {
+                getResponseFailure(download, xhr, display_name, 'events-submit-flash')
+            }
+        }
+    )
 }
\ No newline at end of file


Test patch:
diff --git a/webtools/tests/test_dictionary.py b/webtools/tests/test_dictionary.py
index 303e3fa98..ffad99db5 100644
--- a/webtools/tests/test_dictionary.py
+++ b/webtools/tests/test_dictionary.py
@@ -1,177 +1,175 @@
-import os
-import shutil
-import unittest
-from flask import Response
-from hedweb.app_factory import AppFactory, dictionary
-
-
-def test_dictionaries():
-    base_path = os.path.dirname(os.path.abspath(__file__))
-    test_dict = {'hed-xml-file': os.path.join(base_path, 'data/HED8.0.0-alpha.1.xml'),
-                 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-                 'json-path': os.path.join(base_path, 'data/short_form_valid.json'),
-                 'json-file': 'short_form_valid.json',
-                 'check-for-warnings': True}
-    return test_dict
-
-
-class Test(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
-        app = AppFactory.create_app('config.TestConfig')
-        with app.app_context():
-            from hedweb.routes import route_blueprint
-            app.register_blueprint(route_blueprint)
-            if not os.path.exists(cls.upload_directory):
-                os.mkdir(cls.upload_directory)
-            app.config['UPLOAD_FOLDER'] = cls.upload_directory
-            cls.app = app
-            cls.app.test = app.test_client()
-
-    @classmethod
-    def tearDownClass(cls):
-        shutil.rmtree(cls.upload_directory)
-
-    def test_generate_input_from_dictionary_form(self):
-        self.assertRaises(TypeError, dictionary.generate_input_from_dictionary_form, {},
-                          "An exception should be raised if an empty request is passed")
-
-    def test_dictionary_process(self):
-        from hedweb.dictionary import dictionary_process
-        from hed.util.exceptions import HedFileError
-        arguments = {'json-path': ''}
-        try:
-            a = dictionary_process(arguments)
-        except HedFileError:
-            pass
-        except Exception:
-            self.fail('dictionary_process threw the wrong exception when dictionary-path was empty')
-        else:
-            self.fail('dictionary_process should have thrown a HedFileError exception when json-path was empty')
-
-    def test_dictionary_convert(self):
-        from hedweb.dictionary import dictionary_convert
-        json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-                     'json-path': json_path, 'json-file': 'good_events.json'}
-        with self.app.app_context():
-            response = dictionary_convert(arguments)
-            headers = dict(response.headers)
-            print(headers['Category'])
-            self.assertEqual('warning', headers['Category'], "dictionary_convert issue warning if unsuccessful")
-            self.assertTrue(response.data, "good_events should not convert using HED 7.1.2.xml")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-                     'json-path': json_path, 'json-file': 'good_events.json'}
-        with self.app.app_context():
-            response = dictionary_convert(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('success', headers['Category'], "dictionary_convert should return success if converted")
-
-    def test_dictionary_validate(self):
-        from hedweb.dictionary import dictionary_validate
-        json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-                     'json-path': json_path, 'json-file': 'good_events.json'}
-        with self.app.app_context():
-            response = dictionary_validate(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('warning', headers['Category'], "dictionary_validate issues warning if validation errors")
-            self.assertTrue(response.data, "good_events should not validate using HED 7.1.2.xml")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-                     'json-path': json_path, 'json-file': 'good_events.json'}
-        with self.app.app_context():
-            response = dictionary_validate(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('success', headers['Category'], "dictionary_validate should return success if converted")
-
-    # def test_validate_dictionary_success(self):
-    #     from hed.hedweb.dictionary import dictionary_validate
-    #     from hed.schema import hed_schema_file
-    #     inputs = test_dictionaries()
-    #     response = dictionary_validate(inputs)
-    #     print(type(response) is Response)
-    #     self.assertTrue(type(response) is Response, "dictionary_validate should return a response if no error")
-
-        # hed8_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        #
-        # hed7_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        # hed7 = hed_schema_file.load_schema(hed7_path)
-        # response = dictionary_validate(inputs, hed_schema=hed7)
-        # inputs["hed-xml-file"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        # hed7 = hed_schema_file.load_schema(inputs["hed-xml_file"])
-        # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema8),
-        #                  "This dictionary should have no errors for directly created 8.0.0")
-        # schema7 = HedSchema(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml'))
-        # x = dictionary_validate(arguments, hed_schema=schema7, return_response=False)
-        # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema7,return_response=False),
-        #                  "This dictionary should have no errors for directly created 8.0.0")
-
-        # from hed.hedweb import dictionary
-        # print("First test.....")
-        # r = Response("download_text", mimetype='text/plain charset=utf-8',
-        #              headers={'Category': "success", 'Message': "This is a test of the response"})
-        # the_list = r.headers.getlist('Content-Length')
-        # print("HERE", int(the_list[0]))
-        # for prop, value in vars(r).items():
-        #     print(prop, ":", value)
-        #
-        # print("\nSecond test.....")
-        # r = Response("", mimetype='text/plain charset=utf-8',
-        #              headers={'Category': "success", 'Message': "This is empty"})
-        # for prop, value in vars(r).items():
-        #     print(prop, ":", value)
-        # headers = r.headers
-        # print(r.__dict__)
-        # print(dir(r))
-        # print('Headers....', r.headers)
-        # print("headers field", headers.__dict__)
-        # print("headers field", dir(headers))
-        # print("headers_item", headers.__getitem__('Content-Length'))
-        # the_list = headers.getlist('Content-Length')
-        # print("CL", the_list)
-        # the_len = int(the_list[0])
-        # print('len:', the_len)
-        # keys = headers.keys()
-        # print("Keys", keys)
-        # if r.response:
-        #     print(r.response)
-        # else:
-        #     print("Second response is empty")
-
-
-    # def test_generate_input_arguments_from_validation_form(self):
-    #     from hed.hedweb import dictionary
-    #     self.assertRaises(TypeError, dictionary.generate_input_from_dictionary_form, {},
-    #                       "An exception should be raised if an empty request is passed")
-    #
-    # def test_report_dictionary_validation_status(self):
-    #     self.assertTrue(1, "Testing report_dictionary_validation_status")
-    #
-    # def test_validate_dictionary(self):
-    #     from hed.hedweb.dictionary import dictionary_validate
-    #     from hed.schema import hed_schema_file
-    #     inputs = test_dictionaries()
-    #     self.assertEqual("", dictionary_validate(inputs), "This dictionary should have no errors for 8.0.0")
-    #     hed8_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-    #
-    #     hed7_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-    #     hed7 = hed_schema_file.load_schema(hed7_path)
-    #     # response = dictionary_validate(inputs, hed_schema=hed7)
-    #     # inputs["hed-xml-file"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-    #     # hed7 = hed_schema_file.load_schema(inputs["hed-xml_file"])
-    #     # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema8),
-    #     #                  "This dictionary should have no errors for directly created 8.0.0")
-    #     # schema7 = HedSchema(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml'))
-    #     # x = dictionary_validate(arguments, hed_schema=schema7, return_response=False)
-    #     # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema7,return_response=False),
-    #     #                  "This dictionary should have no errors for directly created 8.0.0")
-
-if __name__ == '__main__':
-    unittest.main()
+import os
+import shutil
+import unittest
+from hedweb.app_factory import AppFactory
+
+
+def test_dictionaries():
+    base_path = os.path.dirname(os.path.abspath(__file__))
+    test_dict = {'hed-xml-file': os.path.join(base_path, 'data/HED8.0.0-alpha.1.xml'),
+                 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                 'json-path': os.path.join(base_path, 'data/short_form_valid.json'),
+                 'json-file': 'short_form_valid.json',
+                 'check-for-warnings': True}
+    return test_dict
+
+
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hedweb.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
+
+    def test_generate_input_from_dictionary_form(self):
+        from hedweb.dictionary import generate_input_from_dictionary_form
+        self.assertRaises(TypeError, generate_input_from_dictionary_form, {},
+                          "An exception should be raised if an empty request is passed")
+
+    def test_dictionary_process(self):
+        from hedweb.dictionary import dictionary_process
+        from hed.util.exceptions import HedFileError
+        arguments = {'json-path': ''}
+        try:
+            dictionary_process(arguments)
+        except HedFileError:
+            pass
+        except Exception:
+            self.fail('dictionary_process threw the wrong exception when dictionary-path was empty')
+        else:
+            self.fail('dictionary_process should have thrown a HedFileError exception when json-path was empty')
+
+    def test_dictionary_convert(self):
+        from hedweb.dictionary import dictionary_convert
+        json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+                     'json-path': json_path, 'json-file': 'good_events.json'}
+        with self.app.app_context():
+            response = dictionary_convert(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('warning', headers['Category'], "dictionary_convert cannot convert JSOn with errors")
+            self.assertTrue(response.data, "good_events should not convert using HED 7.1.2.xml")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                     'json-path': json_path, 'json-file': 'good_events.json'}
+        with self.app.app_context():
+            response = dictionary_convert(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('success', headers['Category'], "dictionary_convert should return success if converted")
+
+    def test_dictionary_validate(self):
+        from hedweb.dictionary import dictionary_validate
+        json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+                     'json-path': json_path, 'json-file': 'good_events.json'}
+        with self.app.app_context():
+            response = dictionary_validate(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('warning', headers['Category'], "dictionary_validate issues warning if validation errors")
+            self.assertTrue(response.data, "good_events should not validate using HED 7.1.2.xml")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                     'json-path': json_path, 'json-file': 'good_events.json'}
+        with self.app.app_context():
+            response = dictionary_validate(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('success', headers['Category'], "dictionary_validate should return success if converted")
+
+    # def test_validate_dictionary_success(self):
+    #     from hed.hedweb.dictionary import dictionary_validate
+    #     from hed.schema import hed_schema_file
+    #     inputs = test_dictionaries()
+    #     response = dictionary_validate(inputs)
+    #     print(type(response) is Response)
+    #     self.assertTrue(type(response) is Response, "dictionary_validate should return a response if no error")
+
+        # hed8_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        #
+        # hed7_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        # hed7 = hed_schema_file.load_schema(hed7_path)
+        # response = dictionary_validate(inputs, hed_schema=hed7)
+        # inputs["hed-xml-file"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        # hed7 = hed_schema_file.load_schema(inputs["hed-xml_file"])
+        # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema8),
+        #                  "This dictionary should have no errors for directly created 8.0.0")
+        # schema7 = HedSchema(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml'))
+        # x = dictionary_validate(arguments, hed_schema=schema7, return_response=False)
+        # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema7,return_response=False),
+        #                  "This dictionary should have no errors for directly created 8.0.0")
+
+        # from hed.hedweb import dictionary
+        # print("First test.....")
+        # r = Response("download_text", mimetype='text/plain charset=utf-8',
+        #              headers={'Category': "success", 'Message': "This is a test of the response"})
+        # the_list = r.headers.getlist('Content-Length')
+        # print("HERE", int(the_list[0]))
+        # for prop, value in vars(r).items():
+        #     print(prop, ":", value)
+        #
+        # print("\nSecond test.....")
+        # r = Response("", mimetype='text/plain charset=utf-8',
+        #              headers={'Category': "success", 'Message': "This is empty"})
+        # for prop, value in vars(r).items():
+        #     print(prop, ":", value)
+        # headers = r.headers
+        # print(r.__dict__)
+        # print(dir(r))
+        # print('Headers....', r.headers)
+        # print("headers field", headers.__dict__)
+        # print("headers field", dir(headers))
+        # print("headers_item", headers.__getitem__('Content-Length'))
+        # the_list = headers.getlist('Content-Length')
+        # print("CL", the_list)
+        # the_len = int(the_list[0])
+        # print('len:', the_len)
+        # keys = headers.keys()
+        # print("Keys", keys)
+        # if r.response:
+        #     print(r.response)
+        # else:
+        #     print("Second response is empty")
+
+    # def test_generate_input_arguments_from_validation_form(self):
+    #     from hed.hedweb import dictionary
+    #     self.assertRaises(TypeError, dictionary.generate_input_from_dictionary_form, {},
+    #                       "An exception should be raised if an empty request is passed")
+    #
+    # def test_report_dictionary_validation_status(self):
+    #     self.assertTrue(1, "Testing report_dictionary_validation_status")
+    #
+    # def test_validate_dictionary(self):
+    #     from hed.hedweb.dictionary import dictionary_validate
+    #     from hed.schema import hed_schema_file
+    #     inputs = test_dictionaries()
+    #     self.assertEqual("", dictionary_validate(inputs), "This dictionary should have no errors for 8.0.0")
+    #     hed8_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+    #
+    #     hed7_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+    #     hed7 = hed_schema_file.load_schema(hed7_path)
+    #     # response = dictionary_validate(inputs, hed_schema=hed7)
+    #     # inputs["hed-xml-file"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+    #     # hed7 = hed_schema_file.load_schema(inputs["hed-xml_file"])
+    #     # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema8),
+    #     #                  "This dictionary should have no errors for directly created 8.0.0")
+    #     # schema7 = HedSchema(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml'))
+    #     # x = dictionary_validate(arguments, hed_schema=schema7, return_response=False)
+    #     # self.assertEqual("", dictionary_validate(arguments, hed_schema=schema7,return_response=False),
+    #     #                  "This dictionary should have no errors for directly created 8.0.0")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/webtools/tests/test_events.py b/webtools/tests/test_events.py
index e412feb00..a73a19683 100644
--- a/webtools/tests/test_events.py
+++ b/webtools/tests/test_events.py
@@ -1,107 +1,108 @@
-import os
-import shutil
-import unittest
-from flask import Response
-from hedweb.app_factory import AppFactory
-from hedweb.constants import common
-
-
-def event_input():
-    base_path = os.path.dirname(os.path.abspath(__file__))
-    test_events = {common.HED_XML_FILE: os.path.join(base_path, 'data/HED8.0.0-alpha.1.xml'),
-                   common.HED_DISPLAY_NAME: 'HED8.0.0-alpha.1.xml',
-                   common.JSON_PATH: os.path.join(base_path, 'data/short_form_valid.json'),
-                   common.JSON_FILE: 'short_form_valid.json',
-                   common.SPREADSHEET_PATH: os.path.join(base_path, 'data/ExcelMultipleSheets.xlsx'),
-                   common.SPREADSHEET_FILE: 'ExcelMultipleSheets.xlsx',
-                   common.WORKSHEET_NAME: 'LKT Events',
-                   common.HAS_COLUMN_NAMES: True,
-                   common.CHECK_FOR_WARNINGS: True
-                   }
-    return test_events
-
-
-class Test(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
-        app = AppFactory.create_app('config.TestConfig')
-        with app.app_context():
-            from hedweb.routes import route_blueprint
-            app.register_blueprint(route_blueprint)
-            if not os.path.exists(cls.upload_directory):
-                os.mkdir(cls.upload_directory)
-            app.config['UPLOAD_FOLDER'] = cls.upload_directory
-            cls.app = app
-            cls.app.test = app.test_client()
-
-    @classmethod
-    def tearDownClass(cls):
-        shutil.rmtree(cls.upload_directory)
-
-    def test_generate_input_from_events_form(self):
-        self.assertTrue(1, "Testing generate_input_from_events_form")
-
-    def test_events_process(self):
-        from hedweb.events import events_process
-        from hed.util.exceptions import HedFileError
-        arguments = {'events-path': ''}
-        try:
-            a = events_process(arguments)
-        except HedFileError:
-            pass
-        except Exception:
-            self.fail('events_process threw the wrong exception when events-path was empty')
-        else:
-            self.fail('events_process should have thrown a HedFileError exception when events-path was empty')
-
-            # def test_dictionary_convert(self):
-            #     from hed.hedweb.dictionary import dictionary_convert
-            #     json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
-            #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-            #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-            #                  'json-path': json_path, 'json-file': 'good_events.json'}
-            #     with self.app.app_context():
-            #         response = dictionary_convert(arguments)
-            #         headers = dict(response.headers)
-            #         self.assertEqual('warning', headers['Category'], "dictionary_convert issue warning if unsuccessful")
-            #         self.assertTrue(response.data, "good_events should not convert using HED 7.1.2.xml")
-            #
-            #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-            #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-            #                  'json-path': json_path, 'json-file': 'good_events.json'}
-            #     with self.app.app_context():
-            #         response = dictionary_convert(arguments)
-            #         headers = dict(response.headers)
-            #         self.assertEqual('success', headers['Category'], "dictionary_convert should return success if converted")
-
-    def test_spreadsheet_validate(self):
-        from hedweb.events import events_validate
-        events_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'bids_events.tsv')
-        json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/bids_events.json')
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-                     'events-path': events_path, 'events-file': 'bids_events.tsv',
-                     'json-path': json_path, 'json-file': 'bids_events.json'}
-
-        with self.app.app_context():
-            response = events_validate(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('success', headers['Category'],
-                             "events_validate should return success if converted")
-            self.assertFalse(response.data, "bids_events should validate using HED 7.1.2.xml")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments['hed-xml-file'] = schema_path
-        arguments['hed-display-name'] = 'HED8.0.0-alpha.1.xml'
-        with self.app.app_context():
-            response = events_validate(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('warning', headers['Category'],
-                             "events_validate has warning if validation errors")
-            self.assertTrue(response.data, "bids_events should not validate with HED8.0.0-alpha.1.xml")
-
-
-if __name__ == '__main__':
-    unittest.main()
+import os
+import shutil
+import unittest
+from flask import Response
+from hedweb.app_factory import AppFactory
+from hedweb.constants import common
+
+
+def event_input():
+    base_path = os.path.dirname(os.path.abspath(__file__))
+    test_events = {common.HED_XML_FILE: os.path.join(base_path, 'data/HED8.0.0-alpha.1.xml'),
+                   common.HED_DISPLAY_NAME: 'HED8.0.0-alpha.1.xml',
+                   common.JSON_PATH: os.path.join(base_path, 'data/short_form_valid.json'),
+                   common.JSON_FILE: 'short_form_valid.json',
+                   common.SPREADSHEET_PATH: os.path.join(base_path, 'data/ExcelMultipleSheets.xlsx'),
+                   common.SPREADSHEET_FILE: 'ExcelMultipleSheets.xlsx',
+                   common.WORKSHEET_NAME: 'LKT Events',
+                   common.HAS_COLUMN_NAMES: True,
+                   common.CHECK_FOR_WARNINGS: True
+                   }
+    return test_events
+
+
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hedweb.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
+
+    def test_generate_input_from_events_form(self):
+        self.assertTrue(1, "Testing generate_input_from_events_form")
+
+    def test_events_process(self):
+        from hedweb.events import events_process
+        from hed.util.exceptions import HedFileError
+        arguments = {'events-path': ''}
+        try:
+            a = events_process(arguments)
+        except HedFileError:
+            pass
+        except Exception:
+            self.fail('events_process threw the wrong exception when events-path was empty')
+        else:
+            self.fail('events_process should have thrown a HedFileError exception when events-path was empty')
+
+            # def test_dictionary_convert(self):
+            #     from hed.hedweb.dictionary import dictionary_convert
+            #     json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
+            #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+            #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+            #                  'json-path': json_path, 'json-file': 'good_events.json'}
+            #     with self.app.app_context():
+            #         response = dictionary_convert(arguments)
+            #         headers = dict(response.headers)
+            #         self.assertEqual('warning', headers['Category'], "dictionary_convert issue warning if unsuccessful")
+            #         self.assertTrue(response.data, "good_events should not convert using HED 7.1.2.xml")
+            #
+            #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+            #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+            #                  'json-path': json_path, 'json-file': 'good_events.json'}
+            #     with self.app.app_context():
+            #         response = dictionary_convert(arguments)
+            #         headers = dict(response.headers)
+            #         self.assertEqual('success', headers['Category'], "dictionary_convert should return success if converted")
+
+    def test_spreadsheet_validate(self):
+        from hedweb.events import events_validate
+        events_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/bids_events.tsv')
+        json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/bids_events.json')
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED7.1.2.xml',
+                     'events-path': events_path, 'events-file': 'bids_events.tsv',
+                     'json-path': json_path, 'json-file': 'bids_events.json', 'hed-option-validate': True}
+
+        with self.app.app_context():
+            response = events_validate(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('warning', headers['Category'],
+                             "events_validate should return validation errors for bids_events.json with 7.1.2")
+            self.assertTrue(response.data, "bids_events should return response data when there are validation errors")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                     'events-path': events_path, 'events-file': 'bids_events.tsv', 'json-path': json_path,
+                     'json-file': 'bids_events.json', 'hed-option-validate': True}
+        with self.app.app_context():
+            response = events_validate(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('success', headers['Category'],
+                             "events_validate should validate bids_events.json with 8.0.0-alpha.1")
+            self.assertFalse(response.data, "bids_events should have no response data when validation is successful")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/webtools/tests/test_hedstring.py b/webtools/tests/test_hedstring.py
index 0c305d0ab..ff1ed243a 100644
--- a/webtools/tests/test_hedstring.py
+++ b/webtools/tests/test_hedstring.py
@@ -1,77 +1,78 @@
-import os
-import shutil
-import unittest
-
-from hedweb.app_factory import AppFactory, hedstring
-
-
-class Test(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
-        app = AppFactory.create_app('config.TestConfig')
-        with app.app_context():
-            from hedweb.routes import route_blueprint
-            app.register_blueprint(route_blueprint)
-            if not os.path.exists(cls.upload_directory):
-                os.mkdir(cls.upload_directory)
-            app.config['UPLOAD_FOLDER'] = cls.upload_directory
-            cls.app = app
-            cls.app.test = app.test_client()
-
-    @classmethod
-    def tearDownClass(cls):
-        shutil.rmtree(cls.upload_directory)
-
-    def test_generate_input_from_hedstring_form(self):
-        self.assertRaises(TypeError, hedstring.generate_input_from_hedstring_form, {},
-                          "An exception is raised if an empty request is passed to generate_input_from_hedstring")
-
-    def test_hedstring_process(self):
-        from hedweb.hedstring import hedstring_process
-        from hed.util.exceptions import HedFileError
-        arguments = {'hedstring': ''}
-        try:
-            a = hedstring_process(arguments)
-        except HedFileError:
-            pass
-        except Exception:
-            self.fail('dictionary_process threw the wrong exception when dictionary-path was empty')
-        else:
-            self.fail('dictionary_process should have thrown a HedFileError exception when json-path was empty')
-
-    def test_hedstring_convert(self):
-        from hedweb.hedstring import hedstring_convert
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-                     'hedstring': 'Red, Blue'}
-        with self.app.app_context():
-            response = hedstring_convert(arguments)
-            self.assertEqual('warning', response['category'], "hedstring_convert issue warning if unsuccessful")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-                     'hedstring': 'Red, Blue'}
-        with self.app.app_context():
-            response = hedstring_convert(arguments)
-            self.assertEqual('success', response['category'], "hedstring_convert should return success if converted")
-
-    def test_hedstring_validate(self):
-        from hedweb.hedstring import hedstring_validate
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-                     'hedstring': 'Red, Blue'}
-        with self.app.app_context():
-            response = hedstring_validate(arguments)
-            self.assertEqual('warning', response['category'], "hedstring_validate has warning if validation errors")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-                     'hedstring': 'Red, Blue'}
-        with self.app.app_context():
-            response = hedstring_validate(arguments)
-            self.assertEqual('success', response['category'], "hedstring_validate should return success if converted")
-
-
-if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
+import os
+import shutil
+import unittest
+
+from hedweb.app_factory import AppFactory
+
+
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hedweb.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
+
+    def test_generate_input_from_hedstring_form(self):
+        from hedweb.hedstring import generate_input_from_hedstring_form
+        self.assertRaises(TypeError, generate_input_from_hedstring_form, {},
+                          "An exception is raised if an empty request is passed to generate_input_from_hedstring")
+
+    def test_hedstring_process(self):
+        from hedweb.hedstring import hedstring_process
+        from hed.util.exceptions import HedFileError
+        arguments = {'hedstring': ''}
+        try:
+            a = hedstring_process(arguments)
+        except HedFileError:
+            pass
+        except Exception:
+            self.fail('dictionary_process threw the wrong exception when dictionary-path was empty')
+        else:
+            self.fail('dictionary_process should have thrown a HedFileError exception when json-path was empty')
+
+    def test_hedstring_convert(self):
+        from hedweb.hedstring import hedstring_convert
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+                     'hedstring': 'Red, Blue'}
+        with self.app.app_context():
+            response = hedstring_convert(arguments)
+            self.assertEqual('warning', response['category'], "hedstring_convert issue warning if unsuccessful")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                     'hedstring': 'Red, Blue'}
+        with self.app.app_context():
+            response = hedstring_convert(arguments)
+            self.assertEqual('success', response['category'], "hedstring_convert should return success if converted")
+
+    def test_hedstring_validate(self):
+        from hedweb.hedstring import hedstring_validate
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+                     'hedstring': 'Red, Blue'}
+        with self.app.app_context():
+            response = hedstring_validate(arguments)
+            self.assertEqual('warning', response['category'], "hedstring_validate has warning if validation errors")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                     'hedstring': 'Red, Blue'}
+        with self.app.app_context():
+            response = hedstring_validate(arguments)
+            self.assertEqual('success', response['category'], "hedstring_validate should return success if converted")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/webtools/tests/test_routes.py b/webtools/tests/test_routes.py
index fb3ef5eb7..3ac9d5a07 100644
--- a/webtools/tests/test_routes.py
+++ b/webtools/tests/test_routes.py
@@ -1,112 +1,112 @@
-import os
-import pathlib
-import shutil
-from shutil import copyfile
-import unittest
-from unittest import mock
-from flask import Flask, current_app
-from hedweb.app_factory import AppFactory
-
-
-class Test(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
-        app = AppFactory.create_app('config.TestConfig')
-        with app.app_context():
-            from hedweb.routes import route_blueprint
-            app.register_blueprint(route_blueprint)
-            if not os.path.exists(cls.upload_directory):
-                os.mkdir(cls.upload_directory)
-            app.config['UPLOAD_FOLDER'] = cls.upload_directory
-            cls.app = app
-            cls.app.test = app.test_client()
-
-    @classmethod
-    def tearDownClass(cls):
-        shutil.rmtree(cls.upload_directory)
-
-    def test_get_columns_info_results(self):
-        response = self.app.test.post('/get-columns-info')
-        self.assertEqual(400, response.status_code, 'Columns info requires data')
-
-    def test_get_dictionary_results(self):
-        response = self.app.test.post('/dictionary-submit')
-        self.assertEqual(400, response.status_code, 'Dictionaryrequires data')
-
-    def test_get_events_results(self):
-        response = self.app.test.post('/events-submit')
-        self.assertEqual(400, response.status_code, 'Event processing requires data')
-
-    def test_get_hed_services_results(self):
-        response = self.app.test.get('/hed-services-submit')
-        self.assertEqual(405, response.status_code, 'HED services require data')
-
-    def test_get_hed_version(self):
-        response = self.app.test.post('/get-hed-version')
-        self.assertEqual(400, response.status_code, 'Returning HED version requires data')
-
-    def test_get_hedstring_results(self):
-        response = self.app.test.get('/hedstring-submit')
-        self.assertEqual(405, response.status_code, 'HED string processing require data')
-
-    def test_get_major_hed_versions(self):
-        response = self.app.test.post('/get-hed-major-versions')
-        self.assertEqual(405, response.status_code, 'Returning HED version list requires data')
-        # import hed.hedweb.constants.common_constants as constants
-        # from hed.hedweb.web_utils import find_major_hed_versions
-        # hed_info = find_major_hed_versions()
-        # self.assertTrue(constants.HED_MAJOR_VERSIONS in hed_info, "The information has key hed-major-versions")
-        # self.assertTrue('7.1.2' in hed_info[constants.HED_MAJOR_VERSIONS], "7.1.2 is a major versions")
-
-    def test_get_schema_results(self):
-        response = self.app.test.post('/schema-submit')
-        self.assertEqual(400, response.status_code, 'Schema processing requires data')
-
-    def test_get_spreadsheet_results(self):
-        response = self.app.test.post('/spreadsheet-submit')
-        self.assertEqual(400, response.status_code, 'Spreadsheet processing requires data')
-
-    def test_render_additional_examples_page(self):
-        response = self.app.test.get('/additional-examples')
-        self.assertEqual(response.status_code, 200, "The additional-examples content page should exist")
-
-    def test_render_common_errors_page(self):
-        response = self.app.test.get('/common-errors')
-        self.assertEqual(response.status_code, 200, "The common-errors content page should exist")
-
-    def test_render_dictionary_form(self):
-        response = self.app.test.get('/dictionary')
-        self.assertEqual(response.status_code, 200, "The dictionary content page should exist")
-
-    def test_render_events_form(self):
-        response = self.app.test.get('/events')
-        self.assertEqual(response.status_code, 200, "The events content page should exist")
-
-    def test_render_hed_services_form(self):
-        response = self.app.test.get('/hed-services')
-        self.assertEqual(response.status_code, 200, "The hed-services content page should exist")
-
-    def test_render_hedstring_form(self):
-        response = self.app.test.get('/hedstring')
-        self.assertEqual(response.status_code, 200, "The hedstring content page should exist")
-
-    def test_render_help_page(self):
-        response = self.app.test.get('/hed-tools-help')
-        self.assertEqual(response.status_code, 200, "The hed-tools-help content page should exist" )
-
-    def test_render_home_page(self):
-        response = self.app.test.get('/')
-        self.assertEqual(response.status_code, 200, "The root hed home page should exist")
-
-    def test_render_schema_form(self):
-        response = self.app.test.get('/schema')
-        self.assertEqual(response.status_code, 200, "The schema page should exist")
-
-    def test_render_spreadsheet_form(self):
-        response = self.app.test.get('/spreadsheet')
-        self.assertEqual(response.status_code, 200, "The spreadsheet page should exist")
-
-
-if __name__ == '__main__':
-    unittest.main()
+import os
+import pathlib
+import shutil
+from shutil import copyfile
+import unittest
+from unittest import mock
+from flask import Flask, current_app
+from hedweb.app_factory import AppFactory
+
+
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hedweb.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
+
+    def test_get_columns_info_results(self):
+        response = self.app.test.post('/get-columns-info')
+        self.assertEqual(400, response.status_code, 'Columns info requires data')
+
+    def test_get_dictionary_results(self):
+        response = self.app.test.post('/dictionary-submit')
+        self.assertEqual(400, response.status_code, 'Dictionaryrequires data')
+
+    def test_get_events_results(self):
+        response = self.app.test.post('/events-submit')
+        self.assertEqual(404, response.status_code, 'Event processing requires data')
+
+    def test_get_hed_services_results(self):
+        response = self.app.test.get('/hed-services-submit')
+        self.assertEqual(405, response.status_code, 'HED services require data')
+
+    def test_get_hed_version(self):
+        response = self.app.test.post('/get-hed-version')
+        self.assertEqual(400, response.status_code, 'Returning HED version requires data')
+
+    def test_get_hedstring_results(self):
+        response = self.app.test.get('/hedstring-submit')
+        self.assertEqual(405, response.status_code, 'HED string processing require data')
+
+    def test_get_major_hed_versions(self):
+        response = self.app.test.post('/get-hed-major-versions')
+        self.assertEqual(405, response.status_code, 'Returning HED version list requires data')
+        # import hed.hedweb.constants.common_constants as constants
+        # from hed.hedweb.web_utils import find_major_hed_versions
+        # hed_info = find_major_hed_versions()
+        # self.assertTrue(constants.HED_MAJOR_VERSIONS in hed_info, "The information has key hed-major-versions")
+        # self.assertTrue('7.1.2' in hed_info[constants.HED_MAJOR_VERSIONS], "7.1.2 is a major versions")
+
+    def test_get_schema_results(self):
+        response = self.app.test.post('/schema-submit')
+        self.assertEqual(400, response.status_code, 'Schema processing requires data')
+
+    def test_get_spreadsheet_results(self):
+        response = self.app.test.post('/spreadsheet-submit')
+        self.assertEqual(400, response.status_code, 'Spreadsheet processing requires data')
+
+    def test_render_additional_examples_page(self):
+        response = self.app.test.get('/additional-examples')
+        self.assertEqual(response.status_code, 200, "The additional-examples content page should exist")
+
+    def test_render_common_errors_page(self):
+        response = self.app.test.get('/common-errors')
+        self.assertEqual(response.status_code, 200, "The common-errors content page should exist")
+
+    def test_render_dictionary_form(self):
+        response = self.app.test.get('/dictionary')
+        self.assertEqual(response.status_code, 200, "The dictionary content page should exist")
+
+    def test_render_events_form(self):
+        response = self.app.test.get('/events')
+        self.assertEqual(response.status_code, 200, "The events content page should exist")
+
+    def test_render_hed_services_form(self):
+        response = self.app.test.get('/hed-services')
+        self.assertEqual(response.status_code, 200, "The hed-services content page should exist")
+
+    def test_render_hedstring_form(self):
+        response = self.app.test.get('/hedstring')
+        self.assertEqual(response.status_code, 200, "The hedstring content page should exist")
+
+    def test_render_help_page(self):
+        response = self.app.test.get('/hed-tools-help')
+        self.assertEqual(response.status_code, 200, "The hed-tools-help content page should exist" )
+
+    def test_render_home_page(self):
+        response = self.app.test.get('/')
+        self.assertEqual(response.status_code, 200, "The root hed home page should exist")
+
+    def test_render_schema_form(self):
+        response = self.app.test.get('/schema')
+        self.assertEqual(response.status_code, 200, "The schema page should exist")
+
+    def test_render_spreadsheet_form(self):
+        response = self.app.test.get('/spreadsheet')
+        self.assertEqual(response.status_code, 200, "The spreadsheet page should exist")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/webtools/tests/test_schema.py b/webtools/tests/test_schema.py
index 5844f7b91..1e21b592f 100644
--- a/webtools/tests/test_schema.py
+++ b/webtools/tests/test_schema.py
@@ -1,90 +1,91 @@
-import os
-import shutil
-import unittest
-from flask import Response
-from hedweb.app_factory import AppFactory, schema
-
-
-class Test(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
-        app = AppFactory.create_app('config.TestConfig')
-        with app.app_context():
-            from hedweb.routes import route_blueprint
-            app.register_blueprint(route_blueprint)
-            if not os.path.exists(cls.upload_directory):
-                os.mkdir(cls.upload_directory)
-            app.config['UPLOAD_FOLDER'] = cls.upload_directory
-            cls.app = app
-            cls.app.test = app.test_client()
-
-    @classmethod
-    def tearDownClass(cls):
-        shutil.rmtree(cls.upload_directory)
-
-    def test_generate_input_from_schema_form(self):
-        self.assertRaises(TypeError, schema.generate_input_from_schema_form, {},
-                          "An exception is raised if an empty request is passed to generate_input_from_schema")
-
-    def test_schema_process(self):
-        from hedweb.schema import schema_process
-        from hed.util.exceptions import HedFileError
-        arguments = {'schema-path': ''}
-        try:
-            a = schema_process(arguments)
-        except HedFileError:
-            pass
-        except Exception:
-            self.fail('schema_process threw the wrong exception when schema-path was empty')
-        else:
-            self.fail('schema_process should have thrown a HedFileError exception when schema-path was empty')
-
-    def test_schema_check(self):
-        from hedweb.schema import schema_check
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED7.1.2.xml', 'schema-option-check': 'true'}
-        with self.app.app_context():
-            response = schema_check(arguments)
-            self.assertTrue(response.data, "HED 7.1.2 is not HED-3G compliant")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED8.0.0-alpha.1.xml', 'schema-option-check': 'true'}
-        with self.app.app_context():
-            response = schema_check(arguments)
-            self.assertFalse(response.data, "HED8.0.0-alpha.1 is HED-3G compliant")
-
-    def test_schema_convert(self):
-        from hedweb.schema import schema_convert
-        from hed.util.exceptions import HedFileError
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED7.1.2.xml',
-                     'schema-option-check': 'true', 'schema-xml-extension': '.xml'}
-        with self.app.app_context():
-            response = schema_convert(arguments)
-            self.assertTrue(response.data, "HED 7.1.2.xml can be converted to mediawiki")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED8.0.0-alpha.1.xml',
-                     'schema-option-check': 'true'}
-        with self.app.app_context():
-            response = schema_convert(arguments)
-            self.assertTrue(response.data, "HED 8.0.0-alpha.1.xml can be converted to mediawiki")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HEDbad.xml')
-        arguments = {'schema-path': schema_path, 'schema-display-name': 'HEDbad.xml',
-                     'schema-option-check': 'true'}
-        with self.app.app_context():
-            try:
-                response = schema_convert(arguments)
-            except HedFileError:
-                pass
-            except Exception:
-                self.fail('schema_convert threw Exception instead of HedFileError for invalid schema file header')
-            else:
-                self.fail('schema_process should throw HedFileError when the schema file header was invalid')
-
-
-if __name__ == '__main__':
-    unittest.main()
+import os
+import shutil
+import unittest
+from flask import Response
+from hedweb.app_factory import AppFactory
+
+
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hedweb.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
+
+    def test_generate_input_from_schema_form(self):
+        from hedweb.schema import generate_input_from_schema_form
+        self.assertRaises(TypeError, generate_input_from_schema_form, {},
+                          "An exception is raised if an empty request is passed to generate_input_from_schema")
+
+    def test_schema_process(self):
+        from hedweb.schema import schema_process
+        from hed.util.exceptions import HedFileError
+        arguments = {'schema-path': ''}
+        try:
+            a = schema_process(arguments)
+        except HedFileError:
+            pass
+        except Exception:
+            self.fail('schema_process threw the wrong exception when schema-path was empty')
+        else:
+            self.fail('schema_process should have thrown a HedFileError exception when schema-path was empty')
+
+    def test_schema_check(self):
+        from hedweb.schema import schema_check
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED7.1.2.xml', 'schema-option-check': 'true'}
+        with self.app.app_context():
+            response = schema_check(arguments)
+            self.assertTrue(response.data, "HED 7.1.2 is not HED-3G compliant")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED8.0.0-alpha.1.xml', 'schema-option-check': 'true'}
+        with self.app.app_context():
+            response = schema_check(arguments)
+            self.assertFalse(response.data, "HED8.0.0-alpha.1 is HED-3G compliant")
+
+    def test_schema_convert(self):
+        from hedweb.schema import schema_convert
+        from hed.util.exceptions import HedFileError
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED7.1.2.xml',
+                     'schema-option-check': 'true', 'schema-xml-extension': '.xml'}
+        with self.app.app_context():
+            response = schema_convert(arguments)
+            self.assertTrue(response.data, "HED 7.1.2.xml can be converted to mediawiki")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments = {'schema-path': schema_path, 'schema-display-name': 'HED8.0.0-alpha.1.xml',
+                     'schema-option-check': 'true'}
+        with self.app.app_context():
+            response = schema_convert(arguments)
+            self.assertTrue(response.data, "HED 8.0.0-alpha.1.xml can be converted to mediawiki")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HEDbad.xml')
+        arguments = {'schema-path': schema_path, 'schema-display-name': 'HEDbad.xml',
+                     'schema-option-check': 'true'}
+        with self.app.app_context():
+            try:
+                response = schema_convert(arguments)
+            except HedFileError:
+                pass
+            except Exception:
+                self.fail('schema_convert threw Exception instead of HedFileError for invalid schema file header')
+            else:
+                self.fail('schema_process should throw HedFileError when the schema file header was invalid')
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/webtools/tests/test_spreadsheet.py b/webtools/tests/test_spreadsheet.py
index 9c28850d7..49c34baf5 100644
--- a/webtools/tests/test_spreadsheet.py
+++ b/webtools/tests/test_spreadsheet.py
@@ -1,91 +1,92 @@
-import os
-import shutil
-import unittest
-from unittest import mock
-from flask import Flask, current_app
-from hedweb.app_factory import AppFactory, spreadsheet
-
-
-class Test(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
-        app = AppFactory.create_app('config.TestConfig')
-        with app.app_context():
-            from hedweb.routes import route_blueprint
-            app.register_blueprint(route_blueprint)
-            if not os.path.exists(cls.upload_directory):
-                os.mkdir(cls.upload_directory)
-            app.config['UPLOAD_FOLDER'] = cls.upload_directory
-            cls.app = app
-            cls.app.test = app.test_client()
-
-    @classmethod
-    def tearDownClass(cls):
-        shutil.rmtree(cls.upload_directory)
-
-    def test_generate_input_from_spreadsheet_form(self):
-        self.assertRaises(TypeError, spreadsheet.generate_input_from_spreadsheet_form, {},
-                          "An exception is raised if an empty request is passed to generate_input_from_spreadsheet")
-
-    def test_spreadsheet_process(self):
-        from hedweb.spreadsheet import spreadsheet_process
-        from hed.util.exceptions import HedFileError
-        arguments = {'spreadsheet-path': ''}
-        try:
-            a = spreadsheet_process(arguments)
-        except HedFileError:
-            pass
-        except Exception:
-            self.fail('spreadsheet_process threw the wrong exception when spreadsheet-path was empty')
-        else:
-            self.fail('spreadsheet_process should have thrown a HedFileError exception when spreadsheet-path was empty')
-
-    # def test_dictionary_convert(self):
-    #     from hed.hedweb.dictionary import dictionary_convert
-    #     json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
-    #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-    #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-    #                  'json-path': json_path, 'json-file': 'good_events.json'}
-    #     with self.app.app_context():
-    #         response = dictionary_convert(arguments)
-    #         headers = dict(response.headers)
-    #         self.assertEqual('warning', headers['Category'], "dictionary_convert issue warning if unsuccessful")
-    #         self.assertTrue(response.data, "good_events should not convert using HED 7.1.2.xml")
-    #
-    #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-    #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
-    #                  'json-path': json_path, 'json-file': 'good_events.json'}
-    #     with self.app.app_context():
-    #         response = dictionary_convert(arguments)
-    #         headers = dict(response.headers)
-    #         self.assertEqual('success', headers['Category'], "dictionary_convert should return success if converted")
-
-    def test_spreadsheet_validate(self):
-        from hedweb.spreadsheet import spreadsheet_validate
-        spreadsheet_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/ExcelMultipleSheets.xlsx')
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
-        prefix_dict = {3: "Event/Long name/", 2: "Event/Label/", 4:"Event/Description/"}
-        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
-                     'spreadsheet-path': spreadsheet_path, 'spreadsheet-file': 'ExcelMultipleSheets.xlsx',
-                     'worksheet-selected': 'LKT Events', 'has-column-names': True,
-                     'tag_columns': [5], 'column-prefix-dictionary': prefix_dict}
-
-        with self.app.app_context():
-            response = spreadsheet_validate(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('success', headers['Category'], "spreadsheet_validate should return success if converted")
-            self.assertFalse(response.data, "ExcelMultipleSheets should validate using HED 7.1.2.xml")
-
-        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
-        arguments['hed-xml-file'] = schema_path
-        arguments['hed-display-name'] = 'HED8.0.0-alpha.1.xml'
-        with self.app.app_context():
-            response = spreadsheet_validate(arguments)
-            headers = dict(response.headers)
-            self.assertEqual('warning', headers['Category'], "spreadsheet_validate issues warning if validation errors")
-            self.assertTrue(response.data, "ExcelMultipleSheets should validate using HED 7.1.2.xml")
-
-
-if __name__ == '__main__':
-    unittest.main()
+import os
+import shutil
+import unittest
+from unittest import mock
+from flask import Flask, current_app
+from hedweb.app_factory import AppFactory
+
+
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hedweb.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
+
+    def test_generate_input_from_spreadsheet_form(self):
+        from hedweb.spreadsheet import generate_input_from_spreadsheet_form
+        self.assertRaises(TypeError, generate_input_from_spreadsheet_form, {},
+                          "An exception is raised if an empty request is passed to generate_input_from_spreadsheet")
+
+    def test_spreadsheet_process(self):
+        from hedweb.spreadsheet import spreadsheet_process
+        from hed.util.exceptions import HedFileError
+        arguments = {'spreadsheet-path': ''}
+        try:
+            a = spreadsheet_process(arguments)
+        except HedFileError:
+            pass
+        except Exception:
+            self.fail('spreadsheet_process threw the wrong exception when spreadsheet-path was empty')
+        else:
+            self.fail('spreadsheet_process should have thrown a HedFileError exception when spreadsheet-path was empty')
+
+    # def test_dictionary_convert(self):
+    #     from hed.hedweb.dictionary import dictionary_convert
+    #     json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/good_events.json')
+    #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+    #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+    #                  'json-path': json_path, 'json-file': 'good_events.json'}
+    #     with self.app.app_context():
+    #         response = dictionary_convert(arguments)
+    #         headers = dict(response.headers)
+    #         self.assertEqual('warning', headers['Category'], "dictionary_convert issue warning if unsuccessful")
+    #         self.assertTrue(response.data, "good_events should not convert using HED 7.1.2.xml")
+    #
+    #     schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+    #     arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+    #                  'json-path': json_path, 'json-file': 'good_events.json'}
+    #     with self.app.app_context():
+    #         response = dictionary_convert(arguments)
+    #         headers = dict(response.headers)
+    #         self.assertEqual('success', headers['Category'], "dictionary_convert should return success if converted")
+
+    def test_spreadsheet_validate(self):
+        from hedweb.spreadsheet import spreadsheet_validate
+        spreadsheet_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/ExcelMultipleSheets.xlsx')
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        prefix_dict = {3: "Event/Long name/", 2: "Event/Label/", 4: "Event/Description/"}
+        arguments = {'hed-xml-file': schema_path, 'hed-display-name': 'HED 7.1.2.xml',
+                     'spreadsheet-path': spreadsheet_path, 'spreadsheet-file': 'ExcelMultipleSheets.xlsx',
+                     'worksheet-selected': 'LKT Events', 'has-column-names': True,
+                     'tag_columns': [5], 'column-prefix-dictionary': prefix_dict}
+
+        with self.app.app_context():
+            response = spreadsheet_validate(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('success', headers['Category'], "spreadsheet_validate should return success if converted")
+            self.assertFalse(response.data, "ExcelMultipleSheets should validate using HED 7.1.2.xml")
+
+        schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+        arguments['hed-xml-file'] = schema_path
+        arguments['hed-display-name'] = 'HED8.0.0-alpha.1.xml'
+        with self.app.app_context():
+            response = spreadsheet_validate(arguments)
+            headers = dict(response.headers)
+            self.assertEqual('warning', headers['Category'], "spreadsheet_validate issues warning if validation errors")
+            self.assertTrue(response.data, "ExcelMultipleSheets should validate using HED 7.1.2.xml")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/webtools/tests/testing_assembly.py b/webtools/tests/testing_assembly.py
new file mode 100644
index 000000000..a205faf16
--- /dev/null
+++ b/webtools/tests/testing_assembly.py
@@ -0,0 +1,35 @@
+import os
+import pandas as pd
+from hed.util.event_file_input import EventFileInput
+from hed.schema.hed_schema_file import load_schema
+from hed.util.column_def_group import ColumnDefGroup
+from hed.util.error_reporter import get_printable_issue_string
+
+if __name__ == '__main__':
+    schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED8.0.0-alpha.1.xml')
+    local_hed_file = 'data/HED8.0.0-alpha.1.xml'
+    example_data_path = 'data'  # path to example data
+    events_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/bids_events.tsv')
+
+    hed_schema = load_schema(schema_path)
+    json_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data/bids_events.json")
+    column_group = ColumnDefGroup(json_path)
+    def_dict, def_issues = column_group.extract_defs(hed_schema)
+    if def_issues:
+        print(get_printable_issue_string(def_issues,
+                                         title="There should be no errors in the definitions from the sidecars:"))
+    event_file = EventFileInput(events_path, json_def_files=column_group,
+                                hed_schema=hed_schema, def_dicts=def_dict)
+
+    # for row_number, columns_to_row_dict in event_file:
+    hed_tags = []
+    onsets = []
+    for row_number, row_dict in event_file.parse_dataframe(return_row_dict=True):
+        assembled_row_hed_string = row_dict.get("HED", "")
+        onset = row_dict.get("onset", "n/a")
+        hed_tags.append(row_dict.get("HED", "").hed_string)
+        onsets.append(row_dict.get("onset", "n/a"))
+    data = {'onset': onsets, 'HED': hed_tags}
+    df = pd.DataFrame(data)
+    final_filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/myTemp.tsv')
+    df.to_csv(final_filename, '\t', index=False, header=True)