Skip to content

add CLI and reporter to generate formatting validator report in one file #4

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

Open
wants to merge 3 commits into
base: master
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
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ html2text==2014.12.29
lxml==3.4.1
parse==1.6.6
aiohttp==0.20.2
pystache==0.5.4
35 changes: 34 additions & 1 deletion validator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import logging
import sys
from enum import Enum
from collections import defaultdict
from pathlib import Path
from markdown import markdown

from . import parsers, checks, reports, fs
from . import cmd, parsers, checks, reports, fs


logger = logging.getLogger()

class Validator(object):
def __init__(self, contents, parser, check, reporter=None):
self.contents = contents
Expand Down Expand Up @@ -123,3 +127,32 @@ def text(self, base, other):

def parse():
return ContentBuilder()

def validate_formatting(settings):
errors = parse().files(settings.source, lang=settings.base_locale).xml(
query='.//string').check().md().validate()

if errors:
logger.info('Errors: %d' % len(errors))
report_uri = reports.HtmlSummaryReporter(settings.output).report(errors).uri_path
logger.info('See %s' % report_uri)
return False
else:
return True

def init_log(verbose):
log_level = logging.DEBUG if verbose else logging.INFO
handler = logging.StreamHandler(sys.stderr)
logger.setLevel(log_level)
logger.addHandler(handler)


def main():
args = cmd.read_args()
if args.version:
result = cmd.print_version()
else:
settings = cmd.read_settings(args)
init_log(settings.verbose)
valid = validate_formatting(settings)
sys.exit(0) if not valid else sys.exit(1)
55 changes: 55 additions & 0 deletions validator/cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Handles command line and calls the email parser with corrent options.
"""

import argparse
import logging
from collections import namedtuple

logger = logging.getLogger()

Settings = namedtuple('Settings', [
'source',
'base_locale',
'output',
'verbose'
])


def default_settings():
return Settings(
source='src/{lang}/*.xml',
base_locale='en',
output='errors/summary.html',
verbose=False
)


def read_args(argsargs=argparse.ArgumentParser):
settings = default_settings()
logger.debug('reading arguments list')
args = argsargs(epilog='Brought to you by KeepSafe - www.getkeepsafe.com')

args.add_argument('-s', '--source', help='args\'s source folder, default: %s' % settings.source)
args.add_argument('-o', '--output',
help='destination path for HTML report, default: %s' % settings.output)
args.add_argument('-l', '--base-locale', help='Base locale for comparision: %s' % settings.base_locale)
args.add_argument('-vv', '--verbose', help='Verbose', action='store_true')
args.add_argument('-v', '--version', help='Show version', action='store_true')
return args.parse_args()


def read_settings(args):
args = vars(args)
settings = default_settings()._asdict()
for k in settings:
if k in args and args[k] is not None:
settings[k] = args[k]
return Settings(**settings)


def print_version():
import pkg_resources
version = pkg_resources.require('content-validator')[0].version
print(version)
return True
10 changes: 10 additions & 0 deletions validator/fs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
import os
from string import Formatter
from collections import defaultdict
from enum import Enum
Expand Down Expand Up @@ -26,6 +27,15 @@ def save_report(directory, source_path, report):
with path.open('w') as fp:
fp.write(report)

def save_summary_report(filepath, content):
path = Path(filepath)
try:
path.parent.mkdir(parents=True)
except FileExistsError:
pass
with path.open('w') as fp:
fp.write(content)
return Path(os.path.abspath(filepath)).as_uri()

def _no_params_pattern(pattern):
yield list(Path().glob(pattern))
Expand Down
117 changes: 116 additions & 1 deletion validator/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import sys
import shutil
import markdown
import pystache

from .fs import save_report, read_content
from .fs import save_report, read_content, save_summary_report
from .errors import UrlDiff, MdDiff


Expand Down Expand Up @@ -147,3 +148,117 @@ def __init__(self, reporters):
def report(self, errors):
for reporter in self.reporters:
reporter.report(errors)

class HtmlSummaryReporter(object):
report_template = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>KS Content-validator</title>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<style>
ins { background: #dff0d8; }
del { background: #f2dede; }
body { padding-top: 70px; }
</style>
</head>
<body data-spy="scroll" data-target="#filelist">
<nav class="navbar navbar-default navbar-fixed-top" id="filelist">
<div class="container-fluid">
<div class="navbar-header">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Files<span class="caret"></span></a>
<ul class="dropdown-menu">
{{#files}}
<li><a href="#{{id}}">{{name}}</a></li>
{{/files}}
</ul>
</li>
</ul>
</div>
</div>
</nav>

<div class="row">
<div class="col-sm-12">
<div class="alert alert-warning" role="alert">
<h3>KeepSafe's validation tool has found some problems with the translation</h3>
<p>
The elements on the left show the reference text, the elements on the right the translation.<br />
The elements that are missing are highlighted in green, the ones which are unnecessary are highlighted in red. <br />
The tool is not always 100% accurate, sometimes it might show things that are correct as errors if there are other errors in the text. <br />
Please correct the errors you can find first. If you think the text is correct and the tool is still showing errors please contact KeepSafe's employee.
</p>
</div>

{{#files}}
<div class="panel panel-default" id="{{id}}">
<div class="panel-heading">
<h3 class="panel-title">{{name}}</h3>
</div>
<div class="panel-body">
<h5>This is how the text will look to the user</h5>
<div class="row">
<div class="col-sm-6"><pre>{{{details.lc}}}</pre></div>
<div class="col-sm-6"><pre>{{{details.rc}}}</pre></div>
</div>

<h5>This is the original text</h5>
<div class="row">
<div class="col-sm-6"><pre>{{{details.ld}}}</pre></div>
<div class="col-sm-6"><pre>{{{details.rd}}}</pre></div>
</div>
<div class="alert alert-danger" role="alert">
<ol>
{{#details.errors}}
<li>{{.}}</li>
{{/details.errors}}
</ol>
</div>
</div>
</div>
{{/files}}
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
</body>
</html>
"""

def __init__(self, output_filepath='errors/summary.html'):
self.output_filepath = output_filepath
self.uri_path = None

def report(self, errors):
files = {}
for error in errors:
filename = str(error.other.original)
files[filename] = files.get(filename, {'errors':[] })
if isinstance(error, UrlDiff):
files[filename]['errors'].append("%s returned with code %s" % (error.url, error.status_code))
if isinstance(error, MdDiff):
files[filename]['errors'] += list(error.error_msgs)
files[filename]['lc'] = markdown.markdown(error.base.parsed)
files[filename]['rc'] = markdown.markdown(error.other.parsed)
files[filename]['ld'] = error.base.diff
files[filename]['rd'] = error.other.diff
data = [{'name': k, 'details': v} for k,v in files.items()]
for i in range(0,len(data)):
data[i]['id'] = 'file_%s' % i
content = pystache.render(self.report_template, {'files': data})
self.uri_path = save_summary_report(self.output_filepath, content)
return self