Skip to content

Commit

Permalink
Merge pull request #672 from opencobra/chore-upgrade-goodtables
Browse files Browse the repository at this point in the history
upgrade goodtables
  • Loading branch information
Midnighter committed Sep 10, 2019
2 parents a84f61a + b31fe87 commit 883a722
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 216 deletions.
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ install_requires =
importlib_resources
numpydoc
pylru
goodtables ==1.0.0
goodtables ~=2.0
depinfo
requests
equilibrator_api <0.2;python_version>='3.5'
Expand Down
155 changes: 53 additions & 102 deletions src/memote/experimental/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,113 +19,64 @@

from __future__ import absolute_import

from functools import partial
from goodtables import check, Error

from goodtables import check


def check_partial(func, *args, **kwargs):
"""Create a partial to be used by goodtables."""
new_func = partial(func, *args, **kwargs)
new_func.check = func.check
return new_func


@check('gene-not-in-model', type='custom', context='body')
def gene_id_check(genes, errors, columns, row_number):
"""
Validate gene identifiers against a known set.
Parameters
----------
genes : set
The known set of gene identifiers.
errors :
Passed by goodtables.
columns :
Passed by goodtables.
row_number :
Passed by goodtables.
"""
message = ("Gene '{value}' in column {col} and row {row} does not "
"appear in the metabolic model.")
for column in columns:
if "gene" in column['header'] and column['value'] not in genes:
message = message.format(
value=column['value'],
row=row_number,
col=column['number'])
errors.append({
'code': 'bad-value',
'message': message,
'row-number': row_number,
'column-number': column['number'],
})


@check('reaction-not-in-model', type='custom', context='body')
def reaction_id_check(reactions, errors, columns, row_number):
@check('unknown-identifier', type='custom', context='body')
class UnknownIdentifier:
"""
Validate reactions identifiers against a known set.
Validate data identifiers against a known set.
Parameters
Attributes
----------
reactions : set
The known set of reaction identifiers.
errors :
Passed by goodtables.
columns :
Passed by goodtables.
row_number :
Passed by goodtables.
"""
message = ("Reaction '{value}' in column {col} and row {row} does not "
"appear in the metabolic model.")
for column in columns:
if "reaction" in column['header'] and column['value'] not in reactions:
message = message.format(
value=column['value'],
row=row_number,
col=column['number'])
errors.append({
'code': 'bad-value',
'message': message,
'row-number': row_number,
'column-number': column['number'],
})
column : str
The header of the data column to check.
identifiers : iterable of str
The known set of identifiers.

@check('metabolite-not-in-model', type='custom', context='body')
def metabolite_id_check(metabolites, errors, columns, row_number):
"""
Validate metabolite identifiers against a known set.
Parameters
----------
metabolites : set
The known set of metabolite identifiers.
errors :
Passed by goodtables.
columns :
Passed by goodtables.
row_number :
Passed by goodtables.

"""
message = ("Metabolite '{value}' in column {col} and row {row} does not "
"appear in the metabolic model.")
for column in columns:
if "metabolite" in column['header'] and \
column['value'] not in metabolites:
message = message.format(
value=column['value'],
row=row_number,
col=column['number'])
errors.append({
'code': 'bad-value',
'message': message,
'row-number': row_number,
'column-number': column['number'],
})
def __init__(self, column, identifiers, **_):
"""
Initialize the custom identfier check.
Parameters
----------
column : str
The header of the data column to check.
identifiers : iterable of str
The known set of identifiers.
"""
self.column = column
self.identifiers = frozenset(identifiers)

def check_row(self, cells):
"""Check each row in the data table."""
cell = None
for item in cells:
if item['header'] == self.column:
cell = item
break

if cell is None:
error = Error(
'unknown-identifier',
row_number=cells[0]['row-number'],
message="Checking identifiers requires the column "
"'{column}' to exist.".format(column=self.column)
)
return [error]

value = cell.get('value')
if value not in self.identifiers:
error = Error(
'unknown-identifier',
cell,
message="Value '{value}' in column {header} on row "
"{row_number} is an unknown identifier.",
message_substitutions={
'value': value,
}
)
return [error]
8 changes: 6 additions & 2 deletions src/memote/experimental/essentiality.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from cobra.flux_analysis import single_gene_deletion

from memote.experimental.experiment import Experiment
from memote.experimental.checks import check_partial, gene_id_check


__all__ = ("EssentialityExperiment",)
Expand Down Expand Up @@ -70,7 +69,12 @@ def load(self, dtype_conversion=None):
def validate(self, model, checks=[]):
"""Use a defined schema to validate the medium table format."""
custom = [
check_partial(gene_id_check, frozenset(g.id for g in model.genes))
{
"unknown-identifier": {
"column": "gene",
"identifiers": {g.id for g in model.genes}
}
}
]
super(EssentialityExperiment, self).validate(
model=model, checks=checks + custom)
Expand Down
5 changes: 4 additions & 1 deletion src/memote/experimental/experimental_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

import memote.experimental.schemata
from memote.experimental.tabular import read_tabular
# The following import is necessary in order to register the custom check.
from memote.experimental.checks import UnknownIdentifier # noqa: F401


__all__ = ("ExperimentalBase",)

Expand Down Expand Up @@ -93,7 +96,7 @@ def validate(self, model, checks=[]):
self.evaluate_report(
validate(records, headers=list(records[0]),
preset='table', schema=self.schema,
order_fields=True, custom_checks=checks))
order_fields=True, checks=checks))

@staticmethod
def evaluate_report(report):
Expand Down
10 changes: 7 additions & 3 deletions src/memote/experimental/medium.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import logging

from memote.experimental.experimental_base import ExperimentalBase
from memote.experimental.checks import check_partial, reaction_id_check


__all__ = ("Medium",)

Expand All @@ -48,8 +48,12 @@ def __init__(self, **kwargs):
def validate(self, model, checks=[]):
"""Use a defined schema to validate the medium table format."""
custom = [
check_partial(reaction_id_check,
frozenset(r.id for r in model.reactions))
{
"unknown-identifier": {
"column": "exchange",
"identifiers": {r.id for r in model.reactions}
}
}
]
super(Medium, self).validate(model=model, checks=checks + custom)

Expand Down

0 comments on commit 883a722

Please sign in to comment.