306:edefddec933d
Anton Shestakov <av6@dwimlabs.net>, Sun, 24 Sep 2017 12:25:05 +0800
viewer: use subqueries to get data on index page Before, there were bare columns in the aggregate query, their values were undefined (but it somehow worked), as SQLite docs say. Good news is that now this bigger query uses (project_id, ordinal) index and is really fast.

next change 314:a3c2e221cadf
previous change 145:9c65d66fcda1

incoming.py

Permissions: -rwxr-xr-x

Other formats: Feeds:
#!/usr/bin/env python
from __future__ import absolute_import
import logging
import re
import sys
from argparse import ArgumentParser, FileType
from urlparse import urlparse
from tornado.escape import json_encode
from candolint.models import database, Project, Change, Check
from candolint.utils import parse_timestamp, rel
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<line_number>\d+):\d+: ', line)
if m is not None:
item['filename'] = m.group('filename')
item['line_number'] = int(m.group('line_number'))
item['link_start'] = m.start('filename')
item['link_end'] = m.end('line_number')
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'),
# luacheck
(r'^\(E\d{3}\)', 'error'),
(r'^\(W\d{3}\)', 'warning'),
# jshint
(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))
state = None
m = re.match('^project URL: (\S+)$', rest)
if m is not None:
url = m.group(1)
m = re.match('^commit: (?:(\d+):)?(\w+)$', rest)
if m is not None:
rev = m.group(1)
node = m.group(2)
m = re.match('^commit branch: (.+)$', rest)
if m is not None:
branch = m.group(1)
m = re.match('^commit ref names:.* HEAD -> (\S+)(, |$)', rest)
if m is not None:
branch = m.group(1)
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)
if state is not None:
item['task'] = state
result.append(item)
if not url or not node or not branch:
return
domain, user, name = parse_project_url(url)
with database.transaction():
project, created = Project.manual_upsert(
url=url,
domain=domain,
user=user,
name=name)
change, created = Change.manual_upsert(
rev=rev,
node=node,
branch=branch,
date=date,
author=author,
message=message,
project=project)
Check.create(
ordinal=Check.get_next_ordinal(project),
errors=errors,
warnings=warnings,
lines=json_encode(result),
success=success,
started=started,
finished=finished,
project=project,
change=change)
def main():
parser = ArgumentParser()
parser.add_argument(
'file', type=FileType('r'), nargs='?', default=sys.stdin,
help='file to read from (if - or not specified, read from stdin)')
parser.add_argument(
'-d', '--debug', action='store_true',
help='enable debugging output')
args = parser.parse_args()
database.init(rel('database.sqlite'))
database.connect()
database.create_tables([Project, Change, Check], safe=True)
if args.debug:
logger = logging.getLogger('peewee')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())
insert_check(args.file)
database.close()
if __name__ == '__main__':
main()