conans/search/binary_html_table.py
import os
from collections import OrderedDict, defaultdict
from conans.model.ref import PackageReference
from conans.util.files import save
from conans import __version__ as client_version
class RowResult(object):
def __init__(self, remote, reference, data):
self.remote = remote
self.reference = reference
self._data = data
@property
def recipe(self):
return self.reference
@property
def package_id(self):
return self._data['id']
@property
def outdated(self):
return self._data['outdated']
def row(self, headers):
""" Returns package data according to headers """
assert isinstance(headers, Headers), "Wrong type: {}".format(type(headers))
for it in headers.keys:
try:
yield getattr(self, it)
except AttributeError:
yield self._data[it]
for it in headers.settings:
yield self._data['settings'].get(it, None)
for it in headers.options:
yield self._data['options'].get(it, None)
if headers.requires:
prefs = [PackageReference.loads(it) for it in self._data['requires']]
yield ', '.join(map(str, [it.ref for it in prefs]))
class Headers(object):
_preferred_ordering = ['os', 'arch', 'compiler', 'build_type']
def __init__(self, settings, options, requires, keys):
# Keys: columns to classify
self.keys = keys
self.options = options
self.requires = requires
# - Order settings
_settings = defaultdict(list)
for it in settings:
try:
category, _ = it.split('.', 1)
except ValueError:
_settings[it].append(it)
else:
_settings[category].append(it)
self.settings = []
for it in self._preferred_ordering:
if it in _settings:
self.settings.extend(sorted(_settings[it]))
for it, values in _settings.items():
if it not in self._preferred_ordering:
self.settings.extend(sorted(values))
def row(self, n_rows=2):
"""
Retrieve list of headers as a single list (1-row) or as a list of tuples with
settings organized by categories (2-row).
Example output:
1-row: ['os', 'arch', 'compiler', 'compiler.version', 'compiler.libcxx', 'build_type']
2-row: [('os', ['']), ('arch', ['']), ('compiler', ['', 'version', 'libcxx']),]
"""
headers = list(self.keys)
if n_rows == 1:
headers.extend(self.settings + self.options)
if self.requires:
headers.append('requires')
return headers
elif n_rows == 2:
headers = [(it, ['']) for it in headers]
settings = self._group_settings(self.settings)
headers.extend(settings)
headers.append(('options', self.options))
if self.requires:
headers.append(('requires', ['']))
return headers
else:
raise NotImplementedError("not yet")
@staticmethod
def _group_settings(settings):
"""
From one row to two-rows using '.' as separator
"""
ret = OrderedDict()
for setting in settings:
try:
category, value = setting.split(".", 1)
except ValueError:
ret.setdefault(setting, []).append('')
else:
ret.setdefault(category, []).append(value)
return [(key, values) for key, values in ret.items()]
class Results(object):
def __init__(self, results):
self._results = results
# Collect data inspecting the packages
_settings = set()
_options = set()
_remotes = set()
self.requires = False
for it in results:
_remotes.add(it['remote'])
for p in it['items'][0]['packages']:
_settings = _settings.union(list(p['settings'].keys()))
_options = _options.union(list(p['options'].keys()))
if len(p['requires']):
self.requires = True
self.settings = list(_settings)
self.options = list(_options)
self.remotes = list(_remotes)
def get_headers(self, keys=('remote', 'reference', 'outdated', 'package_id')):
return Headers(self.settings, self.options, self.requires, keys=keys)
def packages(self):
for it in self._results:
remote = it['remote']
reference = it['items'][0]['recipe']['id']
for p in it['items'][0]['packages']:
r = RowResult(remote, reference, p)
yield r
def html_binary_graph(search_info, reference, table_filename, template):
# Adapt data to the template (think twice about the format before documenting)
search = {'reference': str(reference)}
results = Results(search_info)
# Render and save
template_folder = os.path.dirname(template.filename)
content = template.render(search=search, results=results, base_template_path=template_folder,
version=client_version)
save(table_filename, content)