diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 4d4708e..d9142ad 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -1,8 +1,23 @@ name: test-suite on: [push, pull_request] jobs: + lint-python: + name: lint-python + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: actions/setup-python@v1 + with: + python-version: '3.6' + architecture: 'x64' + - name: Install flake8 + run: | + pip install flake8 + - name: Lint python with flake8 + run: | + flake8 . --max-complexity=10 --statistics test-master: - name: pytest + name: test-asreview-latest runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -17,16 +32,15 @@ jobs: with: python-version: '3.6' architecture: 'x64' - - name: Install packages and run tests + - name: Install packages and run tests run: | pip install pytest pip install --upgrade setuptools>=41.0.0 pip install ./asr-core[all] pip install ./asr-plot pytest asr-plot/tests - test-older: - name: pytest + name: test-asreview-0-7-2 runs-on: ubuntu-latest strategy: matrix: diff --git a/asreviewcontrib/visualization/__init__.py b/asreviewcontrib/visualization/__init__.py index 855efa8..203adf2 100644 --- a/asreviewcontrib/visualization/__init__.py +++ b/asreviewcontrib/visualization/__init__.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from asreviewcontrib.visualization.plot import Plot -from asreviewcontrib.visualization.entrypoint import PlotEntryPoint +from asreviewcontrib.visualization.plot import Plot # noqa +from asreviewcontrib.visualization.entrypoint import PlotEntryPoint # noqa from ._version import get_versions __version__ = get_versions()['version'] diff --git a/asreviewcontrib/visualization/_version.py b/asreviewcontrib/visualization/_version.py index ca1e8e1..a29da92 100644 --- a/asreviewcontrib/visualization/_version.py +++ b/asreviewcontrib/visualization/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -7,7 +6,6 @@ # This file is released into the public domain. Generated by # versioneer-0.18 (https://github.com/warner/python-versioneer) - """Git implementation of _version.py.""" import errno @@ -64,10 +62,15 @@ def decorate(f): HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, +def run_command(commands, + args, + cwd=None, + verbose=False, + hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) @@ -76,10 +79,12 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] @@ -91,7 +96,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, return None, None else: if verbose: - print("unable to find command, tried %s" % (commands,)) + print("unable to find command, tried %s" % (commands, )) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: @@ -116,9 +121,13 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None + } else: rootdirs.append(root) root = os.path.dirname(root) # up a level @@ -201,16 +210,23 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): r = ref[len(tag_prefix):] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None + } @register_vcs_handler("git", "pieces_from_vcs") @@ -225,7 +241,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], + cwd=root, hide_stderr=True) if rc != 0: if verbose: @@ -234,9 +251,10 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], + describe_out, rc = run_command(GITS, [ + "describe", "--tags", "--dirty", "--always", "--long", "--match", + "%s*" % tag_prefix + ], cwd=root) # --long was added in git-1.5.5 if describe_out is None: @@ -269,8 +287,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = ("unable to parse git-describe output: '%s'" % + describe_out) return pieces # tag @@ -279,8 +297,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" % + (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] @@ -330,8 +348,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -445,11 +462,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None + } if not style or style == "default": style = "pep440" # the default @@ -469,9 +488,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date") + } def get_versions(): @@ -498,10 +521,13 @@ def get_versions(): for i in cfg.versionfile_source.split('/'): root = os.path.dirname(root) except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None + } try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -515,6 +541,10 @@ def get_versions(): except NotThisMethod: pass - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None + } diff --git a/asreviewcontrib/visualization/entrypoint.py b/asreviewcontrib/visualization/entrypoint.py index 0558449..f6a9655 100644 --- a/asreviewcontrib/visualization/entrypoint.py +++ b/asreviewcontrib/visualization/entrypoint.py @@ -63,8 +63,8 @@ def execute(self, argv): prefix = args_dict["prefix"] data_paths = args_dict["data_paths"] - keys = args_dict["keys"] + ( - len(data_paths)-len(args_dict["keys"]))*[None] + keys = args_dict["keys"] + \ + (len(data_paths) - len(args_dict["keys"])) * [None] paths = dict(zip(data_paths, keys)) with Plot.from_paths(paths, prefix=prefix) as plot: @@ -76,72 +76,75 @@ def execute(self, argv): return if "inclusion" in types: - inclusion_plot(plot, output=output, result_format=result_format) # noqa + inclusion_plot(plot, + output=output, + result_format=result_format) # noqa if "discovery" in types: - discovery_plot(plot, output=output, result_format=result_format) # noqa + discovery_plot(plot, + output=output, + result_format=result_format) # noqa if "limit" in types: - limit_plot(plot, output=output, result_format=result_format) # noqa + limit_plot(plot, output=output, + result_format=result_format) # noqa if "progression" in types: - progression_plot(plot, output=output, + progression_plot(plot, + output=output, result_format=result_format, sigma=args_dict["sigma"]) def _parse_arguments(version="Unknown"): parser = argparse.ArgumentParser(prog='asreview plot') + parser.add_argument('data_paths', + metavar='DATA_PATHS', + type=str, + nargs='+', + help='A combination of data directories or files.') parser.add_argument( - 'data_paths', - metavar='DATA_PATHS', - type=str, - nargs='+', - help='A combination of data directories or files.' - ) - parser.add_argument( - "-V", "--version", + "-V", + "--version", action="version", version=version, ) parser.add_argument( - "-t", "--type", + "-t", + "--type", type=str, default="all", help="Type of plot to make. Available plot types: inclusion, " "discovery, limit and progression. " "Separate by commas (no spaces) for" - " multiple plots. By default plots all types in sequence." - ) + " multiple plots. By default plots all types in sequence.") parser.add_argument( - "-a", "--absolute-values", + "-a", + "--absolute-values", dest="absolute_format", action='store_true', - help='Use absolute values on the axis instead of percentages.' - ) + help='Use absolute values on the axis instead of percentages.') parser.add_argument( "--prefix", default="", help='Filter files in the data directory to only contain files' - 'starting with a prefix.' - ) + 'starting with a prefix.') parser.add_argument( - "-o", "--output", + "-o", + "--output", default=None, help='Save the plot to a file. If multiple plots are made, only one' - ' is saved (non-deterministically). File formats are detected ' - ' by the matplotlib library, check there to see available ' - 'formats.' - ) + ' is saved (non-deterministically). File formats are detected ' + ' by the matplotlib library, check there to see available ' + 'formats.') + parser.add_argument("-s", + "--sigma", + default=25, + type=int, + help="Smoothing width for the progression plot.") parser.add_argument( - "-s", "--sigma", - default=25, - type=int, - help="Smoothing width for the progression plot." - ) - parser.add_argument( - "-k", "--keys", + "-k", + "--keys", default=[], nargs="*", type=str, help="Set the key of each curve. The order should be the same as those" - " of the data paths." - ) + " of the data paths.") return parser diff --git a/asreviewcontrib/visualization/plot_discovery.py b/asreviewcontrib/visualization/plot_discovery.py index 9a4d7ff..241944c 100644 --- a/asreviewcontrib/visualization/plot_discovery.py +++ b/asreviewcontrib/visualization/plot_discovery.py @@ -14,21 +14,19 @@ def __init__(self, analyses, result_format="percentage"): avg_times.append(list(results.values())) if result_format == "number": - self.ax.hist( - avg_times, - 30, - histtype='bar', - density=False, - label=self.analyses.keys()) + self.ax.hist(avg_times, + 30, + histtype='bar', + density=False, + label=self.analyses.keys()) self.ax.set_xlabel("# Reviewed") self.ax.set_ylabel("# of papers included") else: - self.ax.hist( - avg_times, - 30, - histtype='bar', - density=True, - label=self.analyses.keys()) + self.ax.hist(avg_times, + 30, + histtype='bar', + density=True, + label=self.analyses.keys()) self.ax.set_xlabel("% Reviewed") self.ax.set_ylabel("Fraction of papers included") diff --git a/asreviewcontrib/visualization/plot_inclusions.py b/asreviewcontrib/visualization/plot_inclusions.py index d4e043c..5674f0a 100644 --- a/asreviewcontrib/visualization/plot_inclusions.py +++ b/asreviewcontrib/visualization/plot_inclusions.py @@ -60,10 +60,8 @@ def __init__(self, analyses, result_format="percentage", thick=None): self.fig.tight_layout() def add_WSS(self, *args, **kwargs): # noqa - warnings.warn( - "add_WSS is deprecated, use add_wss instead", - DeprecationWarning - ) + warnings.warn("add_WSS is deprecated, use add_wss instead", + DeprecationWarning) self.add_wss(*args, **kwargs) def add_wss(self, @@ -82,8 +80,9 @@ def add_wss(self, return text = f"WSS@{value}%" - wss_val, wss_x, wss_y = analysis.wss( - value, x_format=self.result_format, **kwargs) + wss_val, wss_x, wss_y = analysis.wss(value, + x_format=self.result_format, + **kwargs) if wss_x is None or wss_y is None: return @@ -100,10 +99,8 @@ def add_wss(self, self.ax.text(*text_at, text, color=text_col, bbox=bbox) def add_RRF(self, *args, **kwargs): # noqa - warnings.warn( - "add_RRF is deprecated, use add_rrf instead", - DeprecationWarning - ) + warnings.warn("add_RRF is deprecated, use add_rrf instead", + DeprecationWarning) self.add_rrf(*args, **kwargs) def add_rrf(self, @@ -120,8 +117,9 @@ def add_rrf(self, if value is None: return - rrf_val, rrf_x, rrf_y = analysis.rrf( - value, x_format=self.result_format, **kwargs) + rrf_val, rrf_x, rrf_y = analysis.rrf(value, + x_format=self.result_format, + **kwargs) if rrf_x is None or rrf_y is None: return @@ -163,5 +161,5 @@ def add_random(self, text_at=None, col='black', add_text=True): n_initial = analysis.inc_found[False]["n_initial"] max_y = analysis.inc_found[False]["inc_after_init"] label_after_init = n_labels - n_initial - y_vals = max_y * np.array(xlim)/label_after_init + y_vals = max_y * np.array(xlim) / label_after_init self.ax.plot(xlim, y_vals, color='black', ls="--") diff --git a/asreviewcontrib/visualization/plot_limit.py b/asreviewcontrib/visualization/plot_limit.py index 15b1e6f..3464cdd 100644 --- a/asreviewcontrib/visualization/plot_limit.py +++ b/asreviewcontrib/visualization/plot_limit.py @@ -24,11 +24,10 @@ def __init__(self, for i_limit, limit in enumerate(res["limits"]): ls = linestyles[i_limit % len(linestyles)] - my_plot, = self.ax.plot( - x_range, - np.array(limit) + np.array(x_range), - color=col, - ls=ls) + my_plot, = self.ax.plot(x_range, + np.array(limit) + np.array(x_range), + color=col, + ls=ls) if i_limit == 0: self.legend_plt.append(my_plot) self.legend_name.append(f"{data_key}") diff --git a/asreviewcontrib/visualization/plot_progression.py b/asreviewcontrib/visualization/plot_progression.py index 620f8d6..783e19a 100644 --- a/asreviewcontrib/visualization/plot_progression.py +++ b/asreviewcontrib/visualization/plot_progression.py @@ -40,8 +40,8 @@ def __init__(self, smooth_inc_perc = [] for i in range(len(dy_inc)): - idx = np.arange( - max(0, i - window), min(len(dy_inc), i + window + 1)) + idx = np.arange(max(0, i - window), + min(len(dy_inc), i + window + 1)) factor = _gaussian_window(idx - i, sigma) smooth_inc_perc.append(np.sum(dy_inc[idx] * factor)) @@ -49,8 +49,10 @@ def __init__(self, x_values = 100 * inc_found[0] / len(analysis.labels) else: x_values = inc_found[0] - myplot, = self.ax.plot( - x_values, 100 * np.array(smooth_inc_perc), color=col, lw=lw) + myplot, = self.ax.plot(x_values, + 100 * np.array(smooth_inc_perc), + color=col, + lw=lw) if self.thick[data_key]: self.legend_plt.append(myplot) self.legend_name.append(data_key) diff --git a/setup.cfg b/setup.cfg index 15f25ca..1be3d79 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,3 +5,11 @@ versionfile_source = asreviewcontrib/visualization/_version.py versionfile_build = asreviewcontrib/visualization/_version.py tag_prefix = v parentdir_prefix = asreview-visualization- + +[flake8] +max-line-length = 88 +ignore = + E402, # module level import not at top of file +exclude = + versioneer.py, + asreviewcontrib/visualization/_version.py diff --git a/tests/test_plot.py b/tests/test_plot.py index be1c26a..224d8bc 100644 --- a/tests/test_plot.py +++ b/tests/test_plot.py @@ -14,8 +14,11 @@ def create_state_file(data_fp, state_fp): except FileNotFoundError: pass - review_simulate(str(data_fp), state_file=state_fp, n_prior_included=1, - n_prior_excluded=1, model="nb", + review_simulate(str(data_fp), + state_file=state_fp, + n_prior_included=1, + n_prior_excluded=1, + model="nb", feature_extraction="tfidf") @@ -39,16 +42,12 @@ def plot_clean(dirs, files): pass -COMBINATIONS = [ - (pt, numbers) - for pt in ["inclusion", "progress", "discovery", "limit"] - for numbers in [True, False] -] +COMBINATIONS = [(pt, numbers) + for pt in ["inclusion", "progress", "discovery", "limit"] + for numbers in [True, False]] -@mark.parametrize( - "plot_type,numbers", COMBINATIONS -) +@mark.parametrize("plot_type,numbers", COMBINATIONS) def test_plots(request, plot_type, numbers): test_dir = request.fspath.dirname output_dir = Path(test_dir, "output") @@ -61,7 +60,7 @@ def test_plots(request, plot_type, numbers): picture_fp = Path(output_dir, "test.png") if (plot_type, numbers) == COMBINATIONS[0]: - plot_setup(data_fp, state_dirs+[output_dir], h5_files+json_files) + plot_setup(data_fp, state_dirs + [output_dir], h5_files + json_files) data_combis = [ [h5_dir, *h5_files],