diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml index 68255f0..2d67b51 100644 --- a/.github/workflows/validation.yml +++ b/.github/workflows/validation.yml @@ -8,6 +8,11 @@ on: jobs: setup: runs-on: ubuntu-latest + container: + image: ghcr.io/metabolicatlas/memote-docker:0.13 + volumes: + - ${{ github.workspace }}:/project:rw + options: --user root --workdir /project outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} env: @@ -19,27 +24,32 @@ jobs: with: fetch-depth: 1 - - name: Set up Python 3 - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - - name: Install dependencies - run: pip install -r requirements.txt + - name: Fetch list of repositories + run: | + git config --global --add safe.directory /__w/standard-GEM-validation/standard-GEM-validation + python -c 'import runner; runner.matrix()' - - id: set-matrix - run: echo "matrix=$(python -c 'import runner; runner.matrix()')" >> $GITHUB_OUTPUT - - - name: Cache pip directory - uses: actions/cache@v3 - id: cache + - name: Commit index of standard-GEMs + uses: stefanzweifel/git-auto-commit-action@v4 with: - path: ~/.cache/pip - key: ${{ github.sha }} + commit_user_name: validation-bot + commit_message: update index of standard-GEMs + file_pattern: index.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Define job matrix from index + id: set-matrix + run: echo "matrix=$(cat index.json)" >> $GITHUB_OUTPUT validate: needs: setup runs-on: ubuntu-latest + container: + image: ghcr.io/metabolicatlas/memote-docker:0.13 + volumes: + - ${{ github.workspace }}:/project:rw + options: --user root --workdir /project continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -55,23 +65,10 @@ jobs: with: fetch-depth: 1 - - name: Set up Python 3 - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - - name: Restore pip cache directory - uses: actions/cache@v3 - id: cache - with: - path: ~/.cache/pip - key: ${{ github.sha }} - - - name: Install dependencies - run: pip install -r requirements.txt - - name: Validate repository - run: python -c 'import runner; runner.validate("${{ matrix.gem }}")' + run: | + git config --global --add safe.directory /__w/standard-GEM-validation/standard-GEM-validation + python -c 'import runner; runner.validate("${{ matrix.gem }}")' - name: Update branch run: git pull --ff diff --git a/index.json b/index.json new file mode 100644 index 0000000..c4d67cc --- /dev/null +++ b/index.json @@ -0,0 +1 @@ +["SysBioChalmers/yeast-GEM", "SysBioChalmers/Human-GEM", "SysBioChalmers/Fruitfly-GEM", "SysBioChalmers/Mouse-GEM", "SysBioChalmers/Sco-GEM", "SysBioChalmers/Zebrafish-GEM", "iAMB-RWTH-Aachen/Opol-GSMM", "SysBioChalmers/Worm-GEM", "haowang-bioinfo/Ecoli-GEM", "SysBioChalmers/Rat-GEM", "tibbdc/vna-GEM"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index c5ff981..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -requests -yamllint -cobra -scipy -memote \ No newline at end of file diff --git a/runner.py b/runner.py index fed2198..11e6703 100644 --- a/runner.py +++ b/runner.py @@ -9,7 +9,7 @@ API_TOKEN = environ['GH_TOKEN'] MODEL_FILENAME = 'model' MODEL_FORMATS = ['.yml', '.xml', '.mat', '.json'] -RELEASES = 10 +RELEASES = 1 header_auth = {'Authorization': 'token %s' % API_TOKEN} additional_branch_tags = [] @@ -59,7 +59,8 @@ def releases(nameWithOwner): def matrix(): m = json.dumps(list(gem_repositories())) - print(m) + with open("index.json", "w") as file: + file.write(m) def gem_follows_standard(nameWithOwner, release, version): repo_standard = requests.get('https://raw.githubusercontent.com/{}/{}/.standard-GEM.md'.format(nameWithOwner, release)) @@ -86,12 +87,16 @@ def validate(nameWithOwner): if gem_is_standard: for model_format in MODEL_FORMATS: my_model = model + model_format - response = requests.get('https://raw.githubusercontent.com/{}/{}/model/{}'.format(nameWithOwner, model_release, my_model)) - with open(my_model, 'w') as file: - file.write(response.text) + response = requests.get('https://raw.githubusercontent.com/{}/{}/model/{}'.format(nameWithOwner, model_release, my_model), timeout=10) + if response.ok: + with open(my_model, 'w') as file: + file.write(response.text) test_results.update(tests.yaml.validate(model)) - test_results.update(tests.cobra.load(model)) - test_results.update(tests.cobra.validateSBML(model)) + test_results.update(tests.cobra.loadYaml(model)) + test_results.update(tests.cobra.loadSbml(model)) + test_results.update(tests.cobra.loadMatlab(model)) + test_results.update(tests.cobra.loadJson(model)) + test_results.update(tests.cobra.validateSbml(model)) test_results.update(tests.memote.scoreAnnotationAndConsistency(model)) else: print('is not following standard') diff --git a/tests/cobra.py b/tests/cobra.py index 91c6f7c..1f88b10 100644 --- a/tests/cobra.py +++ b/tests/cobra.py @@ -1,29 +1,60 @@ import cobra import json -from os.path import exists -def load(model_filename): - print(' load model with cobrapy') - is_valid_cobrapy = False +def loadYaml(model_name): + print('load yaml') + is_valid = False errors = '' try: - cobra.io.load_yaml_model(model_filename + '.yml') - cobra.io.read_sbml_model(model_filename + '.xml') - if exists(model_filename + '.mat'): - cobra.io.load_matlab_model(model_filename + '.mat') - if exists(model_filename + '.json'): - cobra.io.load_json_model(model_filename + '.json') - is_valid_cobrapy = True + cobra.io.load_yaml_model(model_name + '.yml') + is_valid = True except Exception as e: errors = json.dumps(str(e)) print(e) - return {'cobrapy-load': { cobra.__version__ : is_valid_cobrapy, 'errors': errors } } + return {'cobrapy-load-yaml': { cobra.__version__ : is_valid, 'errors': errors } } -def validateSBML(model_filename): - print(' validate sbml with cobrapy') +def loadSbml(model_name): + print('load sbml') + is_valid = False + errors = '' + try: + cobra.io.read_sbml_model(model_name + '.xml') + is_valid = True + except Exception as e: + errors = json.dumps(str(e)) + print(e) + return {'cobrapy-load-sbml': { cobra.__version__ : is_valid, 'errors': errors } } + +def loadMatlab(model_name): + print('load matlab') + is_valid = False + errors = '' + try: + cobra.io.load_matlab_model(model_name + '.mat') + is_valid = True + except Exception as e: + errors = json.dumps(str(e)) + print(e) + return {'cobrapy-load-matlab': { cobra.__version__ : is_valid, 'errors': errors } } + +def loadJson(model_name): + print('load json') + is_valid = False + errors = '' + try: + cobra.io.load_json_model(model_name + '.json') + is_valid = True + except Exception as e: + errors = json.dumps(str(e)) + print(e) + return {'cobrapy-load-json': { cobra.__version__ : is_valid, 'errors': errors } } + + +def validateSbml(model_name): + print('validate sbml with cobrapy') is_valid_sbml = False try: - _, result = cobra.io.sbml.validate_sbml_model(model_filename + '.xml') + _, result = cobra.io.sbml.validate_sbml_model(model_name + '.xml') if result != {}: raise Exception(result) except Exception as e: diff --git a/tests/memote.py b/tests/memote.py index f140b17..36e5259 100644 --- a/tests/memote.py +++ b/tests/memote.py @@ -2,15 +2,16 @@ import json import memote -def scoreAnnotationAndConsistency(model_filename): - print(' memote scoring') - memote_score = 'Scoring failed' +def scoreAnnotationAndConsistency(model_name): + print('memote scoring') + memote_score = False errors = '' try: - model = cobra.io.read_sbml_model(model_filename + '.xml') - _, results = memote.suite.api.test_model(model, None, True, None, {"basic", "annotation", "consistency"}) - processed_results = memote.suite.api.snapshot_report(results, None, False) + model = cobra.io.read_sbml_model(model_name + '.xml') + _, results = memote.suite.api.test_model(model=model, results=True, exclusive=['test_stoichiometric_consistency', 'test_reaction_mass_balance', 'test_reaction_charge_balance', 'test_find_disconnected', 'test_find_reactions_unbounded_flux_default_condition', 'test_metabolite_annotation_presence', 'test_metabolite_annotation_overview', 'test_metabolite_annotation_wrong_ids', 'test_metabolite_id_namespace_consistency', 'test_reaction_annotation_presence', 'test_reaction_annotation_overview', 'test_reaction_annotation_wrong_ids', 'test_reaction_id_namespace_consistency', 'test_gene_product_annotation_presence', 'test_gene_product_annotation_overview', 'test_gene_product_annotation_wrong_ids', 'test_model_id_presence', 'test_metabolites_presence', 'test_reactions_presence', 'test_genes_presence', 'test_compartments_presence', 'test_metabolic_coverage', 'test_unconserved_metabolites', 'test_inconsistent_min_stoichiometry', 'test_find_unique_metabolites', 'test_find_duplicate_metabolites_in_compartments', 'test_metabolites_charge_presence', 'test_metabolites_formula_presence', 'test_find_medium_metabolites', 'test_find_pure_metabolic_reactions', 'test_find_constrained_pure_metabolic_reactions', 'test_find_transport_reactions', 'test_find_constrained_transport_reactions', 'test_find_candidate_irreversible_reactions', 'test_find_reactions_with_partially_identical_annotations', 'test_find_duplicate_reactions', 'test_find_reactions_with_identical_genes']) + processed_results = memote.suite.api.snapshot_report(results, config=None, html=False) results_json = json.loads(processed_results) + print(results_json['score']) memote_score = results_json['score']['total_score'] except Exception as e: errors = json.dumps(str(e)) diff --git a/tests/yaml.py b/tests/yaml.py index 9c1d11a..6272266 100644 --- a/tests/yaml.py +++ b/tests/yaml.py @@ -2,18 +2,19 @@ from yamllint.config import YamlLintConfig import json -def validate(model_filename): - print(' validate YAML with yamllint') - is_valid_yaml = False +def validate(model_name): + print('validate YAML with yamllint') + is_valid = False errors = '' try: conf = YamlLintConfig('{extends: default, rules: {line-length: disable}}') - with open(model_filename + '.yml', 'r') as file: + with open(model_name + '.yml', 'r') as file: errors = list(map(str, yamllint.linter.run(file, conf))) if len(errors) == 0: - is_valid_yaml = True + is_valid = True else: errors = json.dumps(errors) except Exception as e: + errors = json.dumps(str(e)) print(e) - return {'yamllint': { yamllint.__version__ : str(is_valid_yaml), 'errors': errors } } \ No newline at end of file + return {'yamllint': { yamllint.__version__ : is_valid, 'errors': errors } } \ No newline at end of file