66:db31e73bde12
Anton Shestakov <av6@dwimlabs.net>, Sat, 25 Jun 2016 18:16:39 +0800
models: drop Check.raw field (not worth storing in the same table) Replaying checks can be done (and is easier) when storing checker output in plain files, so let's lighten up the load on the database.

next change 67:cdceebad7d96
previous change 65:db29d521fe06

incoming.py

Permissions: -rwxr-xr-x

Other formats: Feeds:
#!/usr/bin/env python
from __future__ import absolute_import
import fileinput
import logging
import os
import re
from datetime import datetime
from urlparse import urlparse
from tornado.escape import json_encode
from candolint.models import database, Project, Change, Check
rel = lambda *x: os.path.abspath(os.path.join(os.path.dirname(__file__), *x))
def parse_timestamp(value):
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S+00:00')
def parse_project_url(url):
parsed = urlparse(url)
domain = parsed.hostname
if domain is None:
domain = 'self'
parts = [part for part in parsed.path.split('/') if part]
if len(parts) > 1:
user = parts[0]
name = '/'.join(parts[1:])
else:
user = None
name = parts[0]
return domain, user, name
def match_linter_output(line):
errors = 0
warnings = 0
item = {}
m = re.match(r'^(?P<filename>.+):(?P<fileline>\d+):\d+: ', line)
if m is not None:
item['filename'] = m.group('filename')
item['fileline'] = int(m.group('fileline'))
rest = line[m.end():]
patterns = (
# pep8-specific E9xx
# https://pep8.readthedocs.io/en/latest/intro.html#error-codes
(r'^E9\d{2}', 'error'),
(r'^[EWFCN]\d{3}', 'warning'),
(r'^\[error\]', 'error'),
(r'^\[warning\]', 'warning'),
(r'^\(E\d{3}\)', 'error'),
(r'^\(W\d{3}\)', 'warning'),
)
for regex, cls in patterns:
m = re.match(regex, rest)
if m is not None:
item['cls'] = cls
if cls == 'error':
errors += 1
elif cls == 'warning':
warnings += 1
break
else:
warnings += 1
item['cls'] = 'warning'
return errors, warnings, item
def insert_check(lines):
meta_prefix = '# C&O '
state = None
result = []
errors = 0
warnings = 0
url = None
rev = None
node = None
branch = None
date = None
author = None
message = None
success = True
started = None
finished = None
for line in lines:
line = line.rstrip('\r\n')
item = {'text': line}
if line.startswith(meta_prefix):
item['cls'] = 'meta'
rest = line[len(meta_prefix):]
m = re.match('^task: (\w+)$', rest)
if m is not None:
state = m.group(1)
item['cls'] = 'task'
m = re.match('^job failed$', rest)
if m is not None:
success = False
item['cls'] = 'failure'
m = re.match('^job (started|finished): ([\dT:+-]+)$', rest)
if m is not None:
if m.group(1) == 'started':
started = parse_timestamp(m.group(2))
elif m.group(1) == 'finished':
finished = parse_timestamp(m.group(2))
m = re.match('^project URL: (\S+)$', rest)
if m is not None:
url = m.group(1)
m = re.match('^commit: (\d+):(\w+) (\w+)$', rest)
if m is not None:
rev = m.group(1)
node = m.group(2)
branch = m.group(3)
m = re.match('^commit date: (.+)$', rest)
if m is not None:
date = m.group(1)
m = re.match('^commit author: (.+)$', rest)
if m is not None:
author = m.group(1)
m = re.match('^commit message: (.+)$', rest)
if m is not None:
message = m.group(1)
elif state == 'checks':
de, dw, extra = match_linter_output(line)
errors += de
warnings += dw
item.update(extra)
result.append(item)
domain, user, name = parse_project_url(url)
with database.transaction():
project, created = Project.create_or_get(
url=url,
domain=domain,
user=user,
name=name)
change, created = Change.create_or_get(
rev=rev,
node=node,
branch=branch,
date=date,
author=author,
message=message,
project=project)
Check.create(
ordinal=Check.select().where(Check.project == project).count() + 1,
errors=errors,
warnings=warnings,
lines=json_encode(result),
success=success,
started=started,
finished=finished,
project=project,
change=change)
def main():
database.init(rel('database.sqlite'))
database.connect()
database.create_tables([Project, Change, Check], safe=True)
logger = logging.getLogger('peewee')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())
insert_check(list(fileinput.input()))
database.close()
if __name__ == '__main__':
main()