198 lines
5.6 KiB
Python
198 lines
5.6 KiB
Python
from operator import __add__, __or__
|
|
|
|
from django.db import models, connection
|
|
from django.db.models import F, Value
|
|
from django.db.models.query import QuerySet, Q
|
|
from django.core.exceptions import ValidationError
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.contrib.postgres.fields import JSONField
|
|
from django.contrib.postgres.search import TrigramDistance
|
|
|
|
|
|
from .search import Unaccent, Lower, JSONTextRef
|
|
from zoo.zoo_meta.validators import schema_validator
|
|
|
|
|
|
class Transaction(models.Model):
|
|
created = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('created'))
|
|
meta = JSONField(
|
|
verbose_name=_('meta'),
|
|
blank=True,
|
|
null=True)
|
|
content = JSONField(
|
|
verbose_name=_('content'),
|
|
blank=True,
|
|
null=True)
|
|
|
|
def __unicode__(self):
|
|
return unicode(self.id)
|
|
|
|
class Meta:
|
|
ordering = ('id',)
|
|
verbose_name = _('transaction')
|
|
verbose_name_plural = _('transactions')
|
|
|
|
|
|
class EntityQuerySet(QuerySet):
|
|
def content_search(self, schema, limit=0.3, **kwargs):
|
|
qs = self
|
|
qs = qs.filter(schema=schema)
|
|
filters = []
|
|
connection.cursor().execute('SELECT SET_LIMIT(%s)', (limit,))
|
|
for key, value in kwargs.iteritems():
|
|
filters.append(Q(**{
|
|
'content__' + key + '__unaccent__lower__trigram_similar':
|
|
Lower(Unaccent(Value(value))),
|
|
}))
|
|
qs = qs.filter(reduce(__or__, filters))
|
|
expressions = []
|
|
ordering = []
|
|
for key, value in kwargs.iteritems():
|
|
ordering.append(Lower(Unaccent(JSONTextRef(F('content'), *key.split('__')))))
|
|
expressions.append(TrigramDistance(
|
|
Lower(Unaccent(JSONTextRef(F('content'), *key.split('__')))),
|
|
Lower(Unaccent(Value(value)))))
|
|
expression = reduce(__add__, expressions)
|
|
qs = qs.annotate(similarity=expression / len(kwargs))
|
|
qs = qs.order_by('similarity', *ordering)
|
|
return qs
|
|
|
|
|
|
class CommonData(models.Model):
|
|
def clean(self):
|
|
if self.schema:
|
|
try:
|
|
schema_validator(self.schema.schema)(self.content)
|
|
except ValidationError, e:
|
|
raise ValidationError({'content': e})
|
|
|
|
def save(self, *args, **kwargs):
|
|
# ensure we have a strict serialization of transactions
|
|
# move elsewhere later
|
|
with connection.cursor() as cursor:
|
|
cursor.execute('LOCK TABLE %s' % Transaction._meta.db_table)
|
|
tr = Transaction.objects.create()
|
|
if self.id:
|
|
self.modified = tr
|
|
else:
|
|
self.created = tr
|
|
return super(CommonData, self).save(*args, **kwargs)
|
|
|
|
def __unicode__(self):
|
|
return unicode(self.id)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
class Entity(CommonData):
|
|
schema = models.ForeignKey(
|
|
'zoo_meta.EntitySchema',
|
|
verbose_name=_('schema'))
|
|
created = models.ForeignKey(
|
|
Transaction,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('created'),
|
|
related_name='created_entities')
|
|
modified = models.ForeignKey(
|
|
Transaction,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('modified'),
|
|
related_name='modified_entities')
|
|
deleted = models.ForeignKey(
|
|
Transaction,
|
|
verbose_name=_('deleted'),
|
|
blank=True,
|
|
null=True,
|
|
related_name='deleted_entities')
|
|
meta = JSONField(
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('meta'))
|
|
content = JSONField(
|
|
blank=True,
|
|
null=False,
|
|
verbose_name=_('content'))
|
|
|
|
objects = EntityQuerySet.as_manager()
|
|
|
|
class Meta:
|
|
ordering = ('created',)
|
|
verbose_name = _('entity')
|
|
verbose_name_plural = _('entities')
|
|
|
|
|
|
class Relation(CommonData):
|
|
schema = models.ForeignKey(
|
|
'zoo_meta.RelationSchema',
|
|
verbose_name=_('schema'))
|
|
left = models.ForeignKey(
|
|
'Entity',
|
|
verbose_name=_('left'),
|
|
related_name='left_relations')
|
|
right = models.ForeignKey(
|
|
'Entity',
|
|
verbose_name=_('right'),
|
|
related_name='right_relations')
|
|
created = models.ForeignKey(
|
|
Transaction,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('created'),
|
|
related_name='created_relations')
|
|
modified = models.ForeignKey(
|
|
Transaction,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('modified'),
|
|
related_name='modified_relations')
|
|
deleted = models.ForeignKey(
|
|
Transaction,
|
|
verbose_name=_('deleted'),
|
|
blank=True,
|
|
null=True,
|
|
related_name='deleted_relations')
|
|
meta = JSONField(
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('meta'))
|
|
content = JSONField(
|
|
blank=True,
|
|
null=False,
|
|
verbose_name=_('content'))
|
|
|
|
class Meta:
|
|
ordering = ('created',)
|
|
verbose_name = _('relation')
|
|
verbose_name_plural = _('relations')
|
|
|
|
|
|
class Log(models.Model):
|
|
entity = models.ForeignKey(
|
|
'Entity',
|
|
verbose_name=_('entity'))
|
|
transaction = models.ForeignKey(
|
|
'Transaction',
|
|
verbose_name=_('transaction'))
|
|
timestamp = models.DateTimeField(
|
|
auto_now_add=True,
|
|
db_index=True,
|
|
verbose_name=_('timestamp'))
|
|
content = JSONField(
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('content'))
|
|
url = models.URLField(
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('url'))
|
|
|
|
class Meta:
|
|
ordering = ('timestamp',)
|
|
verbose_name = _('log')
|
|
verbose_name_plural = _('logs')
|