log viewer

This commit is contained in:
j 2011-11-01 17:08:09 +01:00
commit a0cf082dcf
10 changed files with 573 additions and 53 deletions

0
pandora/log/__init__.py Normal file
View file

144
pandora/log/managers.py Normal file
View file

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from django.db.models import Q, Manager
from ox.django.query import QuerySet
import ox
def parseCondition(condition, user):
'''
condition: {
value: "war"
}
or
condition: {
key: "year",
value: "1970-1980,
operator: "!="
}
...
'''
k = condition.get('key', 'name')
k = {
'user': 'user__username',
}.get(k, k)
if not k:
k = 'name'
v = condition['value']
op = condition.get('operator')
if not op:
op = '='
if op.startswith('!'):
op = op[1:]
exclude = True
else:
exclude = False
if op == '-':
q = parseCondition({'key': k, 'value': v[0], 'operator': '>='}, user) \
& parseCondition({'key': k, 'value': v[1], 'operator': '<'}, user)
if exclude:
return ~q
else:
return q
if k == 'id':
v = ox.from26(v)
if isinstance(v, bool): #featured and public flag
key = k
elif k in ('id', ):
key = '%s%s' % (k, {
'>': '__gt',
'>=': '__gte',
'<': '__lt',
'<=': '__lte',
}.get(op,''))
else:
key = '%s%s' % (k, {
'==': '__iexact',
'^': '__istartswith',
'$': '__iendswith',
}.get(op,'__icontains'))
key = str(key)
if exclude:
q = ~Q(**{key: v})
else:
q = Q(**{key: v})
return q
def parseConditions(conditions, operator, user):
'''
conditions: [
{
value: "war"
}
{
key: "year",
value: "1970-1980,
operator: "!="
},
{
key: "country",
value: "f",
operator: "^"
}
],
operator: "&"
'''
conn = []
for condition in conditions:
if 'conditions' in condition:
q = parseConditions(condition['conditions'],
condition.get('operator', '&'), user)
if q:
conn.append(q)
pass
else:
conn.append(parseCondition(condition, user))
if conn:
q = conn[0]
for c in conn[1:]:
if operator == '|':
q = q | c
else:
q = q & c
return q
return None
class LogManager(Manager):
def get_query_set(self):
return QuerySet(self.model)
def find(self, data, user):
'''
query: {
conditions: [
{
value: "war"
}
{
key: "year",
value: "1970-1980,
operator: "!="
},
{
key: "country",
value: "f",
operator: "^"
}
],
operator: "&"
}
'''
#join query with operator
qs = self.get_query_set()
query = data.get('query', {})
conditions = parseConditions(query.get('conditions', []),
query.get('operator', '&'),
user)
if conditions:
qs = qs.filter(conditions)
return qs

39
pandora/log/models.py Normal file
View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
from django.db import models
from django.contrib.auth.models import User
import ox
import managers
class Log(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, default=None, blank=True, null=True)
url = models.CharField(max_length=1000, default='')
line = models.IntegerField(default=0)
text = models.TextField(blank=True)
objects = managers.LogManager()
def __unicode__(self):
return u"%s" % self.id
def json(self, keys=None):
j = {
'created': self.created,
'id': ox.to26(self.id),
'line': self.line,
'modified': self.modified,
'text': self.text,
'url': self.url,
'user': self.user and self.user.username or '',
}
if keys:
for key in j.keys():
if key not in keys:
del j[key]
return j

63
pandora/log/utils.py Normal file
View file

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
import logging
import sys
class ErrorHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
"""An exception log handler that log entries into log database.
If the request is passed as the first argument to the log record,
request data will be provided in the
"""
def emit(self, record):
import traceback
from django.views.debug import ExceptionReporter
from django.conf import settings
import models
user = None
line = 0
text = ''
url = ''
try:
if sys.version_info < (2,5):
# A nasty workaround required because Python 2.4's logging
# module doesn't support passing in extra context.
# For this handler, the only extra data we need is the
# request, and that's in the top stack frame.
request = record.exc_info[2].tb_frame.f_locals['request']
else:
request = record.request
request_repr = repr(request)
if request.user.is_authenticated():
user = request.user
url = request.META.get('PATH_INFO', '')
except:
request = None
request_repr = "%s %s\n\nRequest repr() unavailable" % (record.levelname, record.msg)
if record.exc_info:
stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
stack_info = traceback.extract_tb(record.exc_info[2])
if stack_info:
url = stack_info[-1][0]
line = stack_info[-1][1]
else:
stack_trace = 'No stack trace available'
text = "%s\n\n%s" % (stack_trace, request_repr)
if text:
l = models.Log(
text=text,
line=line,
url=url
)
if user:
l.user = user
l.save()

138
pandora/log/views.py Normal file
View file

@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
import ox
from ox.utils import json
from ox.django.decorators import login_required_json, admin_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from api.actions import actions
from item import utils
import models
def log(request):
'''
param data {
url: url
line: line
text: text
}
return {
status: ...
data: {
name:
body:
}
}
'''
data = json.loads(request.POST['data'])
if request.user.is_authenticated():
user = request.user
else:
user = None
url = data.get('url', '').split('/static/')[-1]
if 'text' in data:
l = models.Log(
text=data['text'],
line=int(data.get('line', 0)),
url=url
)
if user:
l.user = user
l.save()
response = json_response()
return render_to_json_response(response)
actions.register(log)
@admin_required_json
def removeLogs(request):
'''
param data {
'ids': ,
}
can contain any of the allowed keys for place
'''
data = json.loads(request.POST['data'])
models.Log.objects.filter(id__in=[ox.from26(i) for i in data['ids']]).delete()
response = json_response()
return render_to_json_response(response)
actions.register(removeLogs, cache=False)
def parse_query(data, user):
query = {}
query['range'] = [0, 100]
query['sort'] = [{'key':'name', 'operator':'+'}]
for key in ('keys', 'group', 'list', 'range', 'sort', 'query'):
if key in data:
query[key] = data[key]
query['qs'] = models.Log.objects.find(query, user)
return query
def order_query(qs, sort):
order_by = []
for e in sort:
operator = e['operator']
if operator != '-':
operator = ''
key = {
}.get(e['key'], e['key'])
order = '%s%s' % (operator, key)
order_by.append(order)
if order_by:
qs = qs.order_by(*order_by, nulls_last=True)
return qs
@admin_required_json
def findLogs(request):
'''
param data {
query: {
conditions: [
{
key: 'user',
value: 'something',
operator: '='
}
]
operator: ","
},
sort: [{key: 'created', operator: '+'}],
range: [0, 100]
keys: []
}
'''
data = json.loads(request.POST['data'])
response = json_response()
query = parse_query(data, request.user)
qs = order_query(query['qs'], query['sort'])
qs = qs.distinct()
if 'keys' in data:
qs = qs.select_related()
qs = qs[query['range'][0]:query['range'][1]]
response['data']['items'] = [p.json(data['keys']) for p in qs]
elif 'position' in query:
ids = [i.get_id() for i in qs]
data['conditions'] = data['conditions'] + {
'value': data['position'],
'key': query['sort'][0]['key'],
'operator': '^'
}
query = parse_query(data, request.user)
qs = order_query(query['qs'], query['sort'])
if qs.count() > 0:
response['data']['position'] = utils.get_positions(ids, [qs[0].itemId])[0]
elif 'positions' in data:
ids = [ox.to26(i.id) for i in qs]
response['data']['positions'] = utils.get_positions(ids, data['positions'])
else:
response['data']['items'] = qs.count()
return render_to_json_response(response)
actions.register(findLogs, cache=False)