diff --git a/.github/workflows/new_problem_check.yml b/.github/workflows/new_problem_check.yml index 5a13ee6..5f2917c 100644 --- a/.github/workflows/new_problem_check.yml +++ b/.github/workflows/new_problem_check.yml @@ -2,6 +2,8 @@ name: New Problem Check on: push: + branches: + - main paths: - "utils/new_problem.yaml" pull_request: @@ -32,6 +34,7 @@ jobs: pip install -r utils/requirements.txt - name: Run New Problem Check + if: ${{ hashFiles('utils/new_problem.yaml') != '' }} run: | python utils/validate_yaml.py utils/new_problem.yaml diff --git a/utils/README.md b/utils/README.md index 3d3fbee..dd71de7 100644 --- a/utils/README.md +++ b/utils/README.md @@ -29,3 +29,22 @@ This script checks the new content for the following: pip install -r utils/requirements.txt python utils/validate_yaml.py utils/new_problem.yaml ``` + +## new problem example + +```json +- name: template + suite/generator/single: suite + objectives: '1' + dimensionality: scalable + variable type: continuous + constraints: 'no' + dynamic: 'no' + noise: 'no' + multimodal: 'yes' + multi-fidelity: 'no' + reference: '' + implementation: '' + source (real-world/artificial): '' + textual description: 'This is a dummy template' +`` diff --git a/utils/merge_yaml.py b/utils/merge_yaml.py new file mode 100644 index 0000000..6a7dcce --- /dev/null +++ b/utils/merge_yaml.py @@ -0,0 +1,100 @@ +import yaml + +import sys +from pathlib import Path + +# Add parent directory to sys.path +parent = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(parent)) + +from .validate_yaml import read_data, validate_data, PROBLEMS_FILE + +TEMPLATE_FILE = "template.yaml" + + +def validity_check(old_data, new_data): + # Check there are not duplicate keys + duplicates = len(set(old_data[0].keys()) & set(new_data[0].keys())) > 0 + if duplicates: + print( + "::error::Duplicate problem names found between existing and new problems." + ) + return False + + # Validate new data + is_valid = validate_data(new_data) + if not is_valid: + print("::error::New problems YAML validation failed") + return False + + return True + + +def write_data(filepath, data): + try: + with open(filepath, "w") as f: + yaml.safe_dump(data, f) + print(f"::notice::Wrote data to {filepath}.") + except FileNotFoundError: + print(f"::error::File not found: {filepath}") + return False + except yaml.YAMLError as e: + print(f"::error::YAML syntax error: {e}") + return False + return True + + +def update_existing_data(existing_data, new_data): + existing_data.update(new_data) + + write_success = write_data(PROBLEMS_FILE, existing_data) + return write_success + + +def reset_to_template(file_path): + # Reset the content of the file to a template + template_data_status, template_data = read_data(TEMPLATE_FILE) + if template_data_status != 0: + return template_data_status + write_success = write_data(file_path, template_data) + return write_success + + +def merge_new_problems(new_problems_yaml_path: str): + existing_data_status, existing_data = read_data(PROBLEMS_FILE) + if not existing_data_status: + return False + new_data_status, new_data = read_data(new_problems_yaml_path) + if not new_data_status: + return False + # Validate data + is_valid = validity_check(existing_data, new_data) + if not is_valid: + return False + + # All valid, we can now just merge the dicts + assert existing_data is not None + assert new_data is not None + updated = update_existing_data(existing_data, new_data) + if not updated: + return False + + # Reset the template content + reset_status = reset_to_template(new_problems_yaml_path) + if not reset_status: + return False + + print(f"::notice::Merged {len(new_data)} new problems into {PROBLEMS_FILE}.") + return True + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python merge_yaml.py ") + sys.exit(1) + new_problems_yaml_path = sys.argv[1] + status = merge_new_problems(new_problems_yaml_path) + if not status: + sys.exit(1) + else: + sys.exit(0) diff --git a/utils/new_problem.yaml b/utils/new_problem.yaml deleted file mode 100644 index 2c300e1..0000000 --- a/utils/new_problem.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- name: template - suite/generator/single: suite - objectives: '2' - dimensionality: scalable - variable type: continuous - constraints: 'no' - dynamic: 'no' - noise: 'no' - multimodal: 'yes' - multi-fidelity: 'no' - reference: '' - implementation: '' - source (real-world/artificial): '' - textual description: 'This is a dummy template' diff --git a/utils/validate_yaml.py b/utils/validate_yaml.py index c71be85..0470154 100644 --- a/utils/validate_yaml.py +++ b/utils/validate_yaml.py @@ -108,29 +108,36 @@ def check_novelty(data): return True -def validate_yaml(filepath): - status, data = read_data(filepath) - if status != 0: - sys.exit(1) +def validate_data(data) -> bool: if not check_format(data): - sys.exit(1) - assert data is not None + return False for i, new_data in enumerate(data): # Iterate through each top-level entry # Check required and unique fields if not check_fields(new_data) or not check_novelty(new_data): print(f"::error::Validation failed for entry {i+1}.") - sys.exit(1) + return False # YAML is valid if we reach this point - print("YAML syntax is valid.") - sys.exit(0) + print("::notice::YAML syntax is valid.") + return True + + +def validate_yaml(filepath: str) -> bool: + status, data = read_data(filepath) + if status != 0: + return False + return validate_data(data) if __name__ == "__main__": - if len(sys.argv) < 2: + if len(sys.argv) != 2: print("::error::Usage: python validate_yaml.py ") sys.exit(1) filepath = sys.argv[1] - validate_yaml(filepath) + valid = validate_yaml(filepath) + if valid: + sys.exit(0) + else: + sys.exit(1)