Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#22 add option to generate one-page report #29

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ include HISTORY.rst
include LICENSE
include README.rst

recursive-include flake8_html/templates *.html *.css
recursive-include flake8_html/templates *.html *.css *.js
recursive-include flake8_html/images *.svg

recursive-include tests *
Expand Down
76 changes: 53 additions & 23 deletions flake8_html/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
import codecs
import datetime
import pkgutil
try:
from urllib.parse import quote
except ImportError:
from urllib import quote

from operator import attrgetter
from collections import namedtuple, Counter
Expand Down Expand Up @@ -66,7 +70,7 @@ def find_severity(code):

IndexEntry = namedtuple(
'IndexEntry',
'filename report_name error_count highest_sev'
'filename filename_url_safe report_name error_count highest_sev report_rendered source_rendered'
)


Expand All @@ -80,6 +84,11 @@ def after_init(self):
"""Configure the plugin run."""
self.report_template = jinja2_env.get_template('file-report.html')
self.source_template = jinja2_env.get_template('annotated-source.html')
self.report_content_template = jinja2_env.get_template('file-report-content.html')
self.source_content_template = jinja2_env.get_template('annotated-source-content.html')
self.file_svg_data = quote(pkgutil.get_data('flake8_html', 'images/file.svg'))
self.back_svg_data = quote(pkgutil.get_data('flake8_html', 'images/back.svg'))
self.self_contained = self.options.self_contained_html
self.outdir = self.options.htmldir
if not self.outdir:
sys.exit('--htmldir must be given if HTML output is enabled')
Expand Down Expand Up @@ -127,12 +136,6 @@ def finished(self, filename):
filename = re.sub(r'^\./', '', filename)

highest_sev = min(sev for e, sev in self.errors)
self.files.append(IndexEntry(
filename=filename,
report_name=os.path.basename(report_filename),
error_count=len(self.errors),
highest_sev=highest_sev
))

# Build an index of errors by code/description
index = []
Expand Down Expand Up @@ -200,20 +203,35 @@ def finished(self, filename):
params = self._format_source(source)
params.update(
filename=filename,
filename_url_safe=quote(filename),
report_filename=os.path.basename(report_filename),
source_filename=os.path.basename(source_filename),
errors=by_line,
line_sevs=line_sevs,
highest_sev=highest_sev,
index=index,
title=self.options.htmltitle,
self_contained=self.self_contained,
file_svg_data=self.file_svg_data,
back_svg_data=self.back_svg_data
)
rendered = self.report_template.render(**params)
with codecs.open(report_filename, 'w', encoding='utf8') as f:
f.write(rendered)
rendered = self.source_template.render(**params)
with codecs.open(source_filename, 'w', encoding='utf8') as f:
f.write(rendered)
if not self.self_contained:
rendered = self.report_template.render(**params)
with codecs.open(report_filename, 'w', encoding='utf8') as f:
f.write(rendered)
rendered = self.source_template.render(**params)
with codecs.open(source_filename, 'w', encoding='utf8') as f:
f.write(rendered)

self.files.append(IndexEntry(
filename=filename,
filename_url_safe=quote(filename),
report_name=os.path.basename(report_filename),
error_count=len(self.errors),
highest_sev=highest_sev,
report_rendered=self.report_content_template.render(**params) if self.self_contained else None,
source_rendered=self.source_content_template.render(**params) if self.self_contained else None
))

def get_report_filename(self, filename, suffix=''):
"""Generate a path in the output directory for the source file given.
Expand All @@ -238,10 +256,19 @@ def _format_source(self, source):
'css': formatter.get_style_defs()
}

def _render_css(self):
formatter = HtmlFormatter(nowrap=True)
tmpl = jinja2_env.get_template('styles.css')

return tmpl.render(
pygments_css=formatter.get_style_defs()
)

def stop(self):
"""After the flake8 run, write the stylesheet and index."""
self.write_styles()
self.write_images()
if not self.self_contained:
self.write_styles()
self.write_images()
self.write_index()

def write_images(self):
Expand All @@ -254,16 +281,9 @@ def write_images(self):

def write_styles(self):
"""Write the stylesheet."""
formatter = HtmlFormatter(nowrap=True)
tmpl = jinja2_env.get_template('styles.css')

rendered = tmpl.render(
pygments_css=formatter.get_style_defs()
)

stylesheet = os.path.join(self.outdir, 'styles.css')
with codecs.open(stylesheet, 'w', encoding='utf8') as f:
f.write(rendered)
f.write(self._render_css())

def write_index(self):
"""Write the index file."""
Expand All @@ -281,6 +301,8 @@ def write_index(self):
versions=self.option_manager.generate_versions(),
highest_sev=highest_sev,
title=self.options.htmltitle,
self_contained=self.self_contained,
rendered_css=self._render_css()
)
indexfile = os.path.join(self.outdir, 'index.html')
with codecs.open(indexfile, 'w', encoding='utf8') as f:
Expand All @@ -307,3 +329,11 @@ def add_options(cls, options):
default=False,
parse_from_config=True
)
options.add_option(
'--self-contained-html',
help="Create a self-contained html file containing all "
"necessary styles, scripts, and images",
default=False,
parse_from_config=True,
action='store_true'
)
37 changes: 37 additions & 0 deletions flake8_html/templates/annotated-source-content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div id="source-{{ filename_url_safe }}" class="source page">
<h1>
{% if self_contained %}
<a href="#" onclick="switchToPage('{{ filename_url_safe }}', {{ highest_sev }})">
{% else %}
<a href="{{ report_filename }}">
{% endif %}
<img src="{{ 'data:image/svg+xml;utf-8,'+back_svg_data if self_contained else 'back.svg' }}"
alt="&#x2B05;">
{{filename}} source
</a>
</h1>

<div id="doc">
{% for line in html_lines -%}
{%- set line_errors = errors[loop.index] -%}
{%- set line_sev = line_sevs[loop.index] -%}
<div id="l{{ loop.index }}"
class="code sev-{{ line_sev }} {% if line_errors %} le{% endif %}">
{%- if line_errors %}
<ul class="violations">
{% for (sev, code, text), count in line_errors.items() %}
<li>
<span class="count sev-{{ sev }}">
{{ code }}
</span>
{{ text|sentence }}
{%- if count > 1 %} (in {{ count }} places){% endif -%}
</li>
{% endfor %}
</ul>
{%- endif -%}
<tt><i>{{loop.index}}</i> {{ line or '&nbsp;'|safe }}</tt>
</div>
{% endfor %}
</div>
</div>
48 changes: 6 additions & 42 deletions flake8_html/templates/annotated-source.html
Original file line number Diff line number Diff line change
@@ -1,43 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>{{ filename }} - flake8 annotated source</title>
<meta http-equiv="Content-Type" value="text/html; charset=UTF-8">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="masthead" class="sev-{{ highest_sev }}"></div>
<div id="page">
<h1>
<a href="{{ report_filename }}">
<img src="back.svg" alt="&#x2B05;">
{{filename}} source
</a>
</h1>
{% extends "base.html" %}

<div id="doc">
{% for line in html_lines -%}
{%- set line_errors = errors[loop.index] -%}
{%- set line_sev = line_sevs[loop.index] -%}
<div id="l{{ loop.index }}"
class="code sev-{{ line_sev }} {% if line_errors %} le{% endif %}">
{%- if line_errors %}
<ul class="violations">
{% for (sev, code, text), count in line_errors.items() %}
<li>
<span class="count sev-{{ sev }}">
{{ code }}
</span>
{{ text|sentence }}
{%- if count > 1 %} (in {{ count }} places){% endif -%}
</li>
{% endfor %}
</ul>
{%- endif -%}
<tt><i>{{loop.index}}</i> {{ line or '&nbsp;'|safe }}</tt>
</div>
{% endfor %}
</div>
</div>
</body>
</html>
{% block title %}{{ filename }} - flake8 annotated source{% endblock %}

{% block content %}
{% include "annotated-source-content.html" %}
{% endblock %}
24 changes: 24 additions & 0 deletions flake8_html/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %}</title>
<meta http-equiv="Content-Type" value="text/html; charset=UTF-8">

{% if self_contained %}
<style>{{ rendered_css }}</style>
{% else %}
<link rel="stylesheet" href="styles.css">
{% endif %}

{% block script %}
{% endblock %}

{% endblock %}
</head>
<body>
<div id="masthead" class="sev-{{ highest_sev }}" data-sev="{{ highest_sev }}"></div>
{% block content %}
{% endblock %}
</body>
</html>
25 changes: 25 additions & 0 deletions flake8_html/templates/bind-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function bindHandler(link) {
var code = link.getAttribute('data-code');
var key = location.pathname + '#' + code;

var ul = link.parentNode.querySelector('.details');
if (sessionStorage[key] != 'open') {
ul.style.display = 'none';
}
link.addEventListener('click', function(event) {
if (!ul.style.display || ul.style.display == 'none') {
ul.style.display = 'block';
sessionStorage[key] = 'open';
} else {
ul.style.display = 'none';
sessionStorage[key] = 'closed';
}
});
}

window.addEventListener('DOMContentLoaded', function() {
var links = document.querySelectorAll('.file-report > #index > li > a');
for (var i = 0; i < links.length; i++) {
bindHandler(links[i]);
}
});
53 changes: 53 additions & 0 deletions flake8_html/templates/file-report-content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<div id="{{ filename_url_safe }}" class="file-report page">
<p id="srclink">
{% if self_contained %}
<a title="View full annotated source"
href="#" onclick="switchToPage('source-{{ filename_url_safe }}', {{ highest_sev }})">
{% else %}
<a title="View full annotated source"
href="{{ source_filename }}">
{% endif %}
<img src="{{ 'data:image/svg+xml;utf-8,'+file_svg_data if self_contained else 'file.svg' }}"
alt="&#x2261;">
</a>
</p>
<h1>
{% if self_contained %}
<a href="#" onclick="switchToPage('index-page')">
{% else %}
<a href="index.html">
{% endif %}
<img src="{{ 'data:image/svg+xml;utf-8,'+back_svg_data if self_contained else 'back.svg' }}"
alt="&#x2B05;">
{{ filename }}
</a>
</h1>

<ul id="index">
{% for sev, count, code, text, line, msg_count, expand, errs in index %}
<li>
<a data-code="{{ code }}">
<span class="count sev-{{ sev }}">
{{ count }}
</span>
<strong>{{ code }}:</strong> {{ text }}
{% if msg_count > 1 %}
(and {{ msg_count - 1 }} similar)
{% endif %}
</a>
<ul class="details">
{% for lineno, etext, count in errs -%}
<li>
{% if expand %}
<p>{{etext|sentence}}{% if count > 1 %} (in {{ count }} places){% endif %}:</p>
{% endif %}
<a href="{{ source_filename }}#l{{ lineno }}">
<tt><i>{{lineno}}</i> {{ html_lines[lineno - 1] or '&nbsp;'|safe }}</tt>
</a>
</li>
{%- endfor %}
</ul>
</li>
{% endfor %}
</ul>
</div>
Loading