src\wily\commands\rank.py
-- -- --- --- --- --- --- --- ------- ------- ------- | """
-- -- --- --- --- --- --- --- ------- ------- ------- | Rank command.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | The report command gives a table of files sorted according their ranking scheme
-- -- --- --- --- --- --- --- ------- ------- ------- | of a specified metric.
-- -- --- --- --- --- --- --- ------- ------- ------- | Will compare the values between files and return a sorted table.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | TODO: Layer on Click invocation in operators section, __main__.py file
-- -- --- --- --- --- --- --- ------- ------- ------- | """
-- -- --- --- --- --- --- --- ------- ------- ------- | import operator as op
-- -- --- --- --- --- --- --- ------- ------- ------- | import os
-- -- --- --- --- --- --- --- ------- ------- ------- | from pathlib import Path
-- -- --- --- --- --- --- --- ------- ------- ------- | from sys import exit
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | import radon.cli.harvest
-- -- --- --- --- --- --- --- ------- ------- ------- | import tabulate
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily import format_date, format_revision, logger
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.archivers import resolve_archiver
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.config import DEFAULT_PATH, WilyConfig
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.helper import get_maxcolwidth, get_style
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.operators import resolve_metric_as_tuple
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.state import State
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | def rank(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | config: WilyConfig,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | path: str,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | metric: str,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | revision_index: str,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | limit: int,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | threshold: int,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | descending: bool,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | wrap: bool,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | ) -> None:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | """
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | Rank command ordering files, methods or functions using metrics.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param config: The configuration.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param path: The path to the file.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param metric: Name of the metric to report on.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param revision_index: Version of git repository to revert to.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param limit: Limit the number of items in the table.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param threshold: For total values beneath the threshold return a non-zero exit code.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param descending: Rank in descending order
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :param wrap: Wrap output
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | :return: Sorted table of all files in path, sorted in order of metric.
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | """
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.debug("Running rank command")
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | data = []
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | operator, resolved_metric = resolve_metric_as_tuple(metric)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | operator = operator.name
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | state = State(config)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | if not revision_index:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | target_revision = state.index[state.default_archiver].last_revision
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | else:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | rev = (
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | resolve_archiver(state.default_archiver)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | .archiver_cls(config)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | .find(revision_index)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.debug(f"Resolved {revision_index} to {rev.key} ({rev.message})")
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | try:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | target_revision = state.index[state.default_archiver][rev.key]
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | except KeyError:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.error(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | f"Revision {revision_index} is not in the cache, make sure you have run wily build."
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | exit(1)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.info(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | f"-----------Rank for {resolved_metric.description} for {format_revision(target_revision.revision.key)} by {target_revision.revision.author_name} on {format_date(target_revision.revision.date)}.------------"
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | if path is None:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | files = target_revision.get_paths(config, state.default_archiver, operator)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.debug(f"Analysing {files}")
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | else:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | # Resolve target paths when the cli has specified --path
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | if config.path != DEFAULT_PATH:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | targets = [str(Path(config.path) / Path(path))]
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | else:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | targets = [path]
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | # Expand directories to paths
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | files = [
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | os.path.relpath(fn, config.path)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | for fn in radon.cli.harvest.iter_filenames(targets)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | ]
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.debug(f"Targeting - {files}")
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | for item in files:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | for archiver in state.archivers:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | try:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.debug(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | f"Fetching metric {resolved_metric.name} for {operator} in {str(item)}"
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | val = target_revision.get(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | config, archiver, operator, str(item), resolved_metric.name
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | value = val
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | data.append((item, value))
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | except KeyError:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.debug(f"Could not find file {item} in index")
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | # Sort by ideal value
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | data = sorted(data, key=op.itemgetter(1), reverse=descending)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | if limit:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | data = data[:limit]
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | if not data:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | return
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | # Tack on the total row at the end
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | total = resolved_metric.aggregate(rev[1] for rev in data)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | data.append(["Total", total])
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | headers = ("File", resolved_metric.description)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | maxcolwidth = get_maxcolwidth(headers, wrap)
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | style = get_style()
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | print(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | tabulate.tabulate(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | headers=headers,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | tabular_data=data,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | tablefmt=style,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | maxcolwidths=maxcolwidth,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | maxheadercolwidths=maxcolwidth,
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 |
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | if threshold and total < threshold:
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | logger.error(
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | f"Total value below the specified threshold: {total} < {threshold}"
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | )
-- 14 006 010 007 012 016 019 0076.00 0273.60 0003.60 | exit(1)