"""
``sfftk.notes.find``
=======================
Search for terms and display ontologies
"""
import math
import numbers
import sys
import textwrap
import urllib.parse
import requests
from sfftkrw.core import utils, _str, _xrange
from sfftkrw.core.print_tools import print_date
if sys.version_info[0] > 2:
from shutil import get_terminal_size
_get_terminal_size = get_terminal_size
else:
from backports.shutil_get_terminal_size import get_terminal_size
_get_terminal_size = get_terminal_size
from styled import Styled
from . import RESOURCE_LIST
__author__ = "Paul K. Korir, PhD"
__email__ = "pkorir@ebi.ac.uk, paul.korir@gmail.com"
__date__ = "2017-04-07"
__updated__ = '2018-02-14'
# todo: expand OLS options to below
# type
# Restrict a search to an entity type, one of {class,property,individual,ontology}
#
# slim
# Restrict a search to an particular set of slims by name
#
# fieldList
# Specifcy the fields to return, the defaults are {iri,label,short_form,short_form,ontology_name,ontology_prefix,
# description,type}
#
# queryFields
# Specifcy the fields to query, the defaults are {label, synonym, description, short_form, short_form, annotations,
# logical_description, iri}
#
# groupField
# Set to true to group results by unique id (IRI)
#
# local
# Set to true to only return terms that are in a defining ontology e.g. Only return matches to gene ontology terms in
# the gene ontology, and exclude ontologies where those terms are also referenced
#
# childrenOf
# You can restrict a search to children of a given term. Supply a list of IRI for the terms that you want to search
# under
#
# allChildrenOf
# You can restrict a search to all children of a given term. Supply a list of IRI for the terms that you want to search
# under (subclassOf/is-a plus any hierarchical/transitive properties like 'part of' or 'develops from')
# todo: Retrieve an ontology GET /api/ontologies/{ontology_id}
JUSTIFY = ['left', 'right', 'center']
[docs]
class SearchResource(object):
"""A resource against which to look for accessions or terms"""
def __init__(self, args, configs):
self._args = args
self._configs = configs
self._response = None
self._resource = RESOURCE_LIST[args.resource]
@property
def search_args(self):
return self._args
@property
def configs(self):
return self._configs
@property
def name(self):
return self._resource['name']
@property
def root_url(self):
return self._resource['root_url']
@property
def format(self):
return self._resource['format']
@property
def result_path(self):
return self._resource['result_path']
@property
def result_count(self):
return self._resource['result_count']
@property
def response(self):
return self._response
[docs]
def get_url(self):
"""Determine the url to search against"""
url = None
# ols
if self.name == 'OLS':
if self.search_args.list_ontologies or self.search_args.short_list_ontologies:
url = self.root_url + "ontologies?size=1000"
else:
url = self.root_url + "search?q={}&start={}&rows={}&local=true".format(
self.search_args.search_term,
self.search_args.start - 1,
self.search_args.rows,
)
if self.search_args.ontology:
url += "&ontology={}".format(self.search_args.ontology)
if self.search_args.exact:
url += "&exact=on"
if self.search_args.obsoletes:
url += "&obsoletes=on"
# go
elif self.name == 'GO':
url = self.root_url + "search?q={}&start={}&rows={}&ontology=go".format(
self.search_args.search_term,
self.search_args.start - 1,
self.search_args.rows,
)
if self.search_args.exact:
url += "&exact=on"
if self.search_args.obsoletes:
url += "&obsoletes=on"
# emdb
elif self.name == 'EMDB':
search_term = self.search_args.search_term
search_string = urllib.parse.quote(
f"title:{search_term} OR "
f"go_name:{search_term} OR "
f"sample_name:{search_term}",
safe='/:'
)
url = f"{self.root_url}{search_string}?rows={self.search_args.rows}"
# uniprot
elif self.name == "UniProt":
url = self.root_url + (
"?query={search_term}&format=tsv&size={rows}&fields=accession,id,"
"protein_name,organism_name"
).format(
search_term=self.search_args.search_term,
rows=self.search_args.rows,
)
# pdb
elif self.name == "PDB":
url = self.root_url + (
"?q={search_term}&wt=json&fl=pdb_id,title,organism_scientific_name&start={start}&"
"rows={rows}"
).format(
search_term=self.search_args.search_term,
start=self.search_args.start,
rows=self.search_args.rows,
)
# europepmc
elif self.name == "Europe PMC":
url = self.root_url + (
"search?query={search_term}&resultType=lite&cursorMark=*&pageSize={rows}&format=json"
).format(
search_term=self.search_args.search_term,
rows=self.search_args.rows,
)
# EMPIAR
elif self.name == "EMPIAR":
search_term = self.search_args.search_term
search_string = urllib.parse.quote(
f"title:{search_term} OR "
f"structure_determination_method:{search_term} OR "
f"sample_type:{search_term} OR "
f"sample_name:{search_term} OR "
f"natural_source_organism:{search_term} OR "
f"natural_source_strain_organism:{search_term} OR "
f"natural_source_organ:{search_term} OR "
f"natural_source_tissue:{search_term} OR "
f"natural_source_cell:{search_term} OR "
f"natural_source_organelle:{search_term} OR "
f"natural_source_cellular_location:{search_term} OR "
f"virus_serotype_organism:{search_term} OR "
f"virus_category:{search_term} OR "
f"virus_isolate:{search_term} OR "
f"structure_type:{search_term} OR "
f"buffer_component_name:{search_term} OR "
f"staining_material:{search_term} OR "
f"pretreatment_type:{search_term} OR "
f"vitrification_cryogen_name:{search_term} OR "
f"microscope_name:{search_term} OR "
f"image_set_name:{search_term}",
safe='/:'
)
url = self.root_url + f"{search_string}?rows={self.search_args.rows}"
return url
[docs]
def search(self, *args, **kwargs):
"""Perform a search against this resource"""
url = self.get_url()
if url is not None:
# make the search
R = requests.get(url)
if R.status_code == 200:
self._response = R.text
return SearchResults(self, *args, **kwargs)
else:
print_date("Error: server responded with {}".format(R.text))
return None
else:
print_date('Error: url is None')
return None
def _unicode(self):
return Styled(
"""Search Resource:
\rname:\t\t[[ '{name}'|fg-yellow:bold ]]
\rroot_url:\t[[ '{root_url}'|fg-yellow:bold ]]
\rsearch_url:\t[[ '{search_url}'|fg-yellow:bold ]]
\rformat:\t\t[[ '{format}'|fg-yellow:bold ]]
\rresult_path:\t[[ '{result_path}'|fg-yellow:bold ]]
\rresult_count:\t[[ '{result_count}'|fg-yellow:bold ]]""", search_url=self.get_url(),
**self._resource
)
if sys.version_info[0] > 2:
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
string = self._unicode()
return str(string)
else:
def __str__(self):
return self.__unicode__().encode('utf-8')
def __unicode__(self):
string = self._unicode()
return _str(string)
[docs]
class TableField(object):
"""Class definition for a TableField - single column in a table"""
def __init__(self, name, key=None, text=None, width=20, pc=None, justify='left', _format=None, is_index=False,
is_iterable=False, position_in_iterable=0):
"""A single field (column) in a table
:param str name: the name of the field that will appear in header; can be any object that implements __str__
:param str key: the key to use to extract the value for the field; if this is not defined then the value of
name is used instead
:param str text: a substitute for key; fixed text to appear in this field for all rows
:param int width: a positive integer for the width of this field
:param float pc: percentage width of the terminal occupied by this field
:param str justify: 'left' (default), 'right' or 'center'; how to align text in the field
:param str _format: a format string with one pair of empty braces to construct a string for each row
:param bool is_index: if true then this field will be an index (numeric value) for the row
:param bool is_iterable: if true then the value obtained using key will be from a list and position_in_iterable
index will be retrieved from the iterable
:param int position_in_iterable: if is_iterable is true then the value for this field will be retrieved from the
specified index in the iterable value
"""
# check mutex nature of key and text
try:
assert key is None or text is None
except AssertionError:
raise ValueError('key and text are mutually exclusive; only define one or none of them')
try:
assert isinstance(key, (list, tuple, set)) and len(key) > 1 and all(
map(lambda k: isinstance(k, str), key)) or key is None or isinstance(key, str)
except AssertionError:
raise ValueError('if key is a sequence (list, tuple, set) then it must have two or more strings')
# check valid type for width
try:
assert isinstance(width, numbers.Integral)
except AssertionError:
raise ValueError('field width must be int or long')
# check valid value for width
try:
assert width > 0
except AssertionError:
raise ValueError('field width must be greater than 0')
# ensure pc is valid type
try:
assert isinstance(pc, numbers.Integral) or isinstance(pc, float) or pc is None
except AssertionError:
raise ValueError('invalid type for pc (percentage): {}'.format(type(pc)))
# ensure pc is a valid value
try:
if pc is not None:
assert 0 < pc < 100
else:
assert True
except AssertionError:
raise ValueError('invalid value for pc (percentage): {}'.format(pc))
# check valid values for justify
try:
assert justify in JUSTIFY
except AssertionError:
raise ValueError("invalid value for kwarg justify: {}; should be {}".format(
justify,
', '.join(JUSTIFY),
))
# check valid value for _format
try:
assert _format is None or _format.find("{}") >= 0
except AssertionError:
raise ValueError(
"invalid value for _format: {}; it should be either None or have one and only one pair of braces".format(
_format,
))
# check valid type for position_in_iterable
try:
assert isinstance(position_in_iterable, int)
except AssertionError:
raise ValueError('field position_in_iterable must be int')
# check valid value for position_in_iterable
try:
assert position_in_iterable >= 0
except AssertionError:
raise ValueError('field position_in_iterable must be greater or equal than 0')
self._name = str(name)
if key is None and text is None:
self._key = name
else:
self._key = key
self._text = text
self._width = width
self._pc = pc
self._format = _format
self._justify = justify
self._is_index = is_index
self._is_iterable = is_iterable
self._position_in_iterable = position_in_iterable
[docs]
def justify(self, text):
"""Justify the given text
:param str text: the text to justify
"""
if self._justify == 'left':
return text.ljust(self._width)
elif self._justify == 'right':
return text.rjust(self._width)
elif self._justify == 'center':
return text.center(self._width)
if sys.version_info[0] > 2:
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
return self.justify(self._name)
else:
def __str__(self):
return self.__unicode__().encode('utf-8')
def __unicode__(self):
return self.justify(self._name)
@property
def is_index(self):
"""Is this field an index (numbered) field?"""
return self._is_index
@property
def width(self):
"""The width of the field in characters"""
return self._width
@width.setter
def width(self, width):
try:
assert isinstance(width, int)
except AssertionError:
raise ValueError('width must be an int')
self._width = width
@property
def pc(self):
"""The width of the field in percent; resolves to a character width"""
return self._pc
[docs]
def render(self, row_data, index):
"""Render this field"""
text = ''
if self.is_index:
text = _str(index)
elif self._key is not None:
if isinstance(self._key, (list, tuple, set)): # if we have a path
try:
item = row_data
for key in self._key: # for each key
item = item[key]
except KeyError:
text = '-'
else:
if self._is_iterable:
text = item[self._position_in_iterable]
else:
text = item
elif isinstance(self._key, str): # if we have a key
try:
item = row_data[self._key]
except KeyError:
text = '-'
else:
if self._is_iterable:
if item:
text = item[self._position_in_iterable]
else:
text = '-'
else:
text = item
elif self._text is not None:
text = self._text
# format
if self._format is not None:
wrapped_text = textwrap.wrap(self._format.format(text), self._width)
else:
wrapped_text = textwrap.wrap(text, self._width)
if not wrapped_text: # empty list for empty string
return [self.justify('')]
else:
return list(map(self.justify, wrapped_text))
[docs]
class Table(object):
"""Table superclass"""
column_separator = " | "
row_separator = "\n"
[docs]
class TableRow(Table):
"""Class definition for a single row in the table
Wrapping is automatically handled
"""
def __init__(self, row_data, fields, index, *args, **kwargs):
"""Initialise a ``TableRow`` object
:param row_data: the data for the different fields in the row
:param fields: an iterable of :py:class:`TableField` objects
:param index: the index for this row; externally resolved henced passed an an init var
"""
super(TableRow, self).__init__(*args, **kwargs)
self._row_data = row_data
self._fields = fields
self._index = index
self._rendered = self._render()
def _render(self):
rendered = list()
for field in self._fields:
rendered.append(field.render(self._row_data, self._index))
return rendered
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
string = ''
# get the max number of lines in this row
no_lines = 0
for f in self._rendered:
no_lines = max(len(list(f)), no_lines)
# build the stack of lines for this row
for i in _xrange(no_lines):
row = list()
for j, F in enumerate(self._fields):
try:
field = self._rendered[j][i]
except IndexError:
field = F.justify('')
row.append(field)
# don't add an extra row separator to the last line
if i == no_lines - 1:
string += self.column_separator.join(row)
else:
string += self.column_separator.join(row) + self.row_separator
return string
[docs]
class CSVRow(Table):
"""Class definition for a single row in the table"""
column_separator = "\t"
def __init__(self, row_data, fields, index, *args, **kwargs):
super().__init__(*args, **kwargs)
self._row_data = row_data
self._fields = fields
self._index = index
self._rendered = self._render()
def _render(self):
"""We undo the wrapping here"""
rendered = list()
for field in self._fields:
rendered_field = field.render(self._row_data, self._index)
stripped_rendered_field = list(map(lambda f: f.strip(), rendered_field))
# we will strip all leading and trailing whitespace and join all wrapped lines
if str(field).strip() in ['description', 'label', 'description (title)']: # the description needs spaces
rendered.append(' '.join(stripped_rendered_field).strip())
else:
rendered.append(''.join(stripped_rendered_field))
return rendered
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
return self.column_separator.join(self._rendered).strip('\n')
[docs]
class ResultsTable(Table):
"""Class that formats search results as a table"""
def __init__(self, search_results, fields, width='auto', *args, **kwargs):
"""Initialise a ResultsTable object by specifying results and fields
:param search_results: a SearchResults object
:type search_results: SearchResults
:param list fields: a list of TableField objects
:param int width: a non-negative integer
"""
# check the type of search_results
try:
assert isinstance(search_results, SearchResults)
except AssertionError:
raise ValueError('search_results must be a SearchResults object')
# ensure that we have at least one field
try:
assert fields # nice! an empty list asserts to False
except AssertionError:
raise ValueError('fields kwarg should not be empty')
# ensure that the fields kwarg is populated with TableField objects
try:
assert all(map(lambda f: isinstance(f, TableField), fields))
except AssertionError:
raise ValueError('non-TableField object in iterable fields')
# check valid type for width
try:
assert isinstance(width, numbers.Integral) or width == 'auto'
# assert isinstance(width, int) or isinstance(width, long) or width == 'auto'
except AssertionError:
raise ValueError("field width must be instance of int or long or the string 'auto'")
# check valid value for width
if isinstance(width, _str):
try:
assert width == 'auto'
except AssertionError:
raise ValueError("field width must be greater than 0 or the string 'auto'")
elif isinstance(width, numbers.Integral):
try:
assert width > 0
except AssertionError:
raise ValueError("field width must be greater than 0 or the string 'auto'")
# only one index field per table
try:
assert len(list(filter(lambda f: f.is_index, fields))) <= 1
except AssertionError:
raise ValueError(
'there is more than one field with is_index=True set; only one index field per table supported')
super(ResultsTable, self).__init__(*args, **kwargs)
self._search_results = search_results
if width == 'auto':
terminal_size = _get_terminal_size() # fallback values
if terminal_size.columns > 0:
self._width = terminal_size.columns
else:
self._width = 80
else:
self._width = width
self._fields = self._evaluate_widths(fields)
# ensure width is less than the sum of fields
total_width = sum([f.width for f in fields])
try:
assert total_width < self._width
except AssertionError:
print_date(
'total field widths greater than table width; '
'distortion will occur: table width={}; total field width={}'.format(
self._width,
total_width,
))
def _evaluate_widths(self, fields):
"""Convert percentage widths into fixed widths
:param list fields: list of TableField objects
:return list _fields: list of TableField objects
"""
_fields = list()
# we calculate the field's width by taking into account the column separator
inter_column_distance = len(fields) * len(self.column_separator)
# subtract the inter-column distance from the screen width
reduced_width = self._width - inter_column_distance
for field in fields:
if field.pc is not None:
# the field's width is now...
field.width = int(math.floor(reduced_width * field.pc / 100))
_fields.append(field)
return _fields
@property
def header(self):
header = Styled("[[ ''|fg-yellow:no-end ]]")
header += "=" * self._width + self.row_separator
header += "Search term: [[ '{}'|fg-yellow:bold ]]{}{}".format(
self._search_results.search_args.search_term,
self.row_separator,
self.row_separator,
)
header += Styled("[[ ''|fg-yellow:bold:no-end ]]")
header += self.column_separator.join(list(map(lambda f: _str(f)[:f.width], self._fields))) + self.row_separator
header += Styled("[[ ''|reset ]][[ ''|fg-yellow:no-end ]]")
header += "=" * self._width + self.row_separator
header += Styled("[[ ''|reset ]]")
return header
@property
def body(self):
index = self._search_results.search_args.start # index
if self._search_results.results is not None:
body = Styled("[[ ''|fg-yellow:no-end ]]")
for row_data in self._search_results.results:
body += _str(TableRow(row_data, self._fields, index)) + self.row_separator
body += "-" * self._width + self.row_separator
index += 1 # increment index
body += Styled("[[ ''|reset ]]")
else:
body = '\nNo data found at this time. Please try again in a few minutes.'.center(
self._width) + self.row_separator
body += self.row_separator
body += "-" * self._width + self.row_separator
return body
@property
def footer(self):
if len(self._search_results):
footer = 'Showing: {} to {} of {} results found'.format(
self._search_results.search_args.start,
min(len(self._search_results),
self._search_results.search_args.start + self._search_results.search_args.rows - 1),
len(self._search_results),
)
else:
footer = "Showing {} results per page".format(
self._search_results.search_args.rows,
)
return footer
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
string = ""
string += self.header
string += self.body
string += self.footer
return _str(string)
[docs]
class CSVTable(Table):
"""Class that formats search results as a CSV"""
column_separator = "\t"
def __init__(self, search_results, fields, width='auto', *args, **kwargs):
super().__init__(*args, **kwargs)
self._search_results = search_results
self._fields = fields
@property
def header(self):
header = ""
if self._search_results.search_args.no_header:
return header
field_names = list(map(lambda f: str(f).strip(), self._fields))
header += self.column_separator.join(field_names) + self.row_separator
return header
@property
def body(self):
index = self._search_results.search_args.start
if self._search_results.results is not None:
body = ""
for row_data in self._search_results.results:
# if the user is using --filter-rows then only include rows with the specified indexes
if self._search_results.search_args.filter_rows:
if str(index) in self._search_results.search_args.filter_rows:
row_string = str(CSVRow(row_data, self._fields, index)) + self.row_separator
body += row_string
index += 1
continue
index += 1
else:
# otherwise display all rows
row_string = str(CSVRow(row_data, self._fields, index)) + self.row_separator
body += row_string
index += 1
else:
body = '\nNo data found at this time. Please try again in a few minutes.'.center(
self._width) + self.row_separator
body += self.row_separator
body += "-" * self._width + self.row_separator
return body.strip('\n')
@property
def footer(self):
return ""
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
string = ""
string += self.header
string += self.body
string += self.footer
return _str(string)
[docs]
class SearchResults(object):
"""SearchResults class"""
_width = 180 # unreasonable default
INDEX_WIDTH = 6
LABEL_WIDTH = 20
SHORT_FORM_WIDTH = 20
ONTOLOGY_NAME_WIDTH = 15
DESCRIPTION_WIDTH = 80
TYPE_WIDTH = 18
def __init__(self, resource, as_text=False):
self._resource = resource # the resource that was searched
self._raw_response = resource.response
self._structured_response = self._structure_response()
terminal_size = _get_terminal_size() # fallback values
if terminal_size.columns > 0:
self._width = terminal_size.columns
else:
self._width = self._width
self.as_text = as_text
@property
def structured_response(self):
return self._structured_response
def _structure_response(self):
"""Structure the raw result according to the format received"""
if self._resource.format == 'json':
import json
try:
structured_results = json.loads(self._raw_response)
except ValueError:
print_date("Unable to search at this time. Please try after a few minutes.")
structured_results = None
return structured_results
elif self._resource.format == 'tsv':
try:
# split rows; split columns; dump first and last rows
_structured_results = list(map(lambda r: r.split('\t'), self._raw_response.split('\n')))[1:-1]
# make a list of dicts with the given ids
structured_results = list(map(lambda r: dict(zip(['id', 'name', 'proteins', 'organism'], r)),
_structured_results))
except ValueError:
structured_results = None
return structured_results
else:
print_date("unsupported format: {}".format(self._resource.format))
return None
@property
def search_args(self):
return self._resource.search_args
@property
def results(self):
if self.structured_response is not None:
if self._resource.result_path is not None:
return utils.get_path(self.structured_response, self._resource.result_path)
return self.structured_response
def __bytes__(self):
return self.__str__().encode('utf-8')
def __str__(self):
return self.tabulate(as_text=self.as_text)
[docs]
def tabulate(self, as_text=False):
"""Tabulate the search results"""
table = Styled("[[ ''|fg-yellow:no-end ]]") # ""
if self._resource.name == 'OLS':
# only list ontologies as short or long lists
if self.search_args.list_ontologies or self.search_args.short_list_ontologies:
if self.search_args.list_ontologies:
table += "\n" + "-" * self._width + "\n"
for ontology in utils.get_path(self.structured_response, ['_embedded', 'ontologies']):
c = ontology['config']
table += "\n"
ont = [
"Namespace: ".ljust(30) + "[[ '{}'|bold ]][[ ''|fg-yellow:no-end ]]".format(
_str(c['namespace'])),
"Pref. prefix: ".ljust(30) + _str(c['preferredPrefix']),
"Title: ".ljust(30) + _str(c['title']),
"Description: ".ljust(30) + _str(c['description']),
"Homepage: ".ljust(30) + _str(c['homepage']),
"ID: ".ljust(30) + _str(c['id']),
"Version :".ljust(30) + _str(c['version']),
]
table += "\n".join(ont)
table += "\n" + "-" * self._width
elif self.search_args.short_list_ontologies:
table += "List of ontologies\n"
table += "\n" + "-" * self._width + "\n"
for ontology in utils.get_path(self.structured_response, ['_embedded', 'ontologies']):
c = ontology['config']
ont = [
"[[ '{}'|fg-yellow:bold ]][[ ''|fg-yellow:no-end ]]".format(
_str(c['namespace']).ljust(10)),
"-",
_str(c['description'][:200]) if c['description'] else '' + "...",
]
table += "\t".join(ont) + "\n"
# list search results
else:
fields = [
TableField('index', key='index', pc=5, is_index=True, justify='right'),
TableField('label', key='label', pc=10),
TableField('resource', key='ontology_name', pc=5, justify='center'),
TableField('url', key='iri', pc=30),
TableField('accession', key='short_form', pc=10, justify='center'),
TableField('description', key='description', pc=40, is_iterable=True),
]
if as_text:
# exclude colour decoration
table = str(CSVTable(self, fields=fields))
else:
table += _str(ResultsTable(self, fields=fields))
elif self._resource.name == 'GO':
fields = [
TableField('index', key='index', pc=5, is_index=True, justify='right'),
TableField('label', key='label', pc=10),
TableField('resource', key='ontology_name', pc=5, justify='center'),
TableField('url', key='iri', pc=30),
TableField('accession', key='short_form', pc=10, justify='center'),
TableField('description', key='description', pc=40, is_iterable=True),
]
if as_text:
table = str(CSVTable(self, fields=fields))
else:
table += _str(ResultsTable(self, fields=fields))
elif self._resource.name == 'EMDB':
fields = [
TableField('index', key='index', pc=5, is_index=True, justify='right'),
TableField('label', text=self._resource.search_args.search_term, pc=10, justify='center'),
TableField('resource', text='EMDB', pc=5, justify='center'),
TableField('url', key='emdb_id', _format='https://www.ebi.ac.uk/emdb/{}', pc=30),
TableField('accession', key='emdb_id', pc=10, _format='{}', justify='center'),
TableField('description', key=['admin', 'title'], pc=40),
]
if as_text:
table = str(CSVTable(self, fields=fields))
else:
table += _str(ResultsTable(self, fields=fields))
elif self._resource.name == "UniProt":
fields = [
TableField('index', pc=5, is_index=True, justify='right'),
TableField('label', key='name', pc=10),
TableField('resource', text='UniProt', pc=5, justify='center'),
TableField('url', key='id', _format='https://www.uniprot.org/uniprot/{}', pc=30),
TableField('accession', key='id', pc=10, justify='center'),
TableField('description', key='proteins', pc=40),
# TableField('organism', key='organism', width=40),
]
table += _str(ResultsTable(self, fields=fields))
elif self._resource.name == "PDB":
fields = [
TableField('index', pc=5, is_index=True, justify='right'),
TableField('label', text=self._resource.search_args.search_term, pc=10),
TableField('resource', text='PDB', pc=5, justify='center'),
TableField('url', key='pdb_id', _format='https://www.ebi.ac.uk/pdbe/entry/pdb/{}', pc=30),
TableField('accession', key='pdb_id', pc=10, justify='center'),
# TableField('title', key='organism_scientific_name', pc=20, is_iterable=True),
TableField('description', key='title', pc=40),
]
if as_text:
table = str(CSVTable(self, fields=fields))
else:
table += _str(ResultsTable(self, fields=fields))
elif self._resource.name == 'Europe PMC':
fields = [
TableField('index', pc=5, is_index=True, justify='right'),
TableField('label (authors)', key='authorString', pc=20),
TableField('resource', text='Europe PMC', pc=10, justify='center'),
TableField('url', key='id', _format='https://europepmc.org/abstract/MED/{}', pc=30),
TableField('accession', key='id', pc=10, justify='center'),
TableField('description (title)', key='title', pc=25),
# TableField('iri (doi)', key='doi', _format='https://doi.org/{}', pc=30)
]
if as_text:
table = str(CSVTable(self, fields=fields))
else:
table += _str(ResultsTable(self, fields=fields))
elif self._resource.name == 'EMPIAR':
fields = [
TableField('index', pc=5, is_index=True, justify='right'),
TableField('label', text=self._resource.search_args.search_term, pc=10),
TableField('resource', text='EMPIAR', pc=8, justify='center'),
TableField('url', key='empiarid', _format='https://www.ebi.ac.uk/empiar/{}',
pc=30),
TableField('accession', key='empiarid', pc=10, justify='center'),
TableField('description', key='title', pc=33),
]
# EMPIAR search results are returned as a list of objects with accession->metadata
# therefore, we need to embed the accession into the metadata
structured_response = list()
for empiar_accession in self.structured_response:
self.structured_response[empiar_accession]['empiarid'] = empiar_accession
structured_response.append(self.structured_response[empiar_accession])
self._structured_response = structured_response
if as_text:
table = str(CSVTable(self, fields=fields))
else:
table += _str(ResultsTable(self, fields=fields))
# close style
if as_text:
return table
table += Styled("[[ ''|reset ]]")
return _str(table)
def __len__(self):
if self.structured_response is not None:
if self._resource.result_count is not None:
return utils.get_path(self._structured_response, self._resource.result_count)
return 0