<commit_before># Copyright 2012-2013 <NAME>
# This file is part of the Lino project.
# Lino is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# Lino is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with Lino; if not, see <http://www.gnu.org/licenses/>.

"""
The :xfile:`models` module for :mod:`lino.modlib.changes`.

It defines the :class:`Change` model
and the :func:`watch_changes` function.
It also adds a menu entry to the `Explorer` menu.
"""

import logging
logger = logging.getLogger(__name__)
#~ from lino.utils import dblogger

import datetime

from django.conf import settings
#~ from django.contrib.auth import models as auth
#~ from django.contrib.sessions import models as sessions
from django.contrib.contenttypes.models import ContentType


from django.db import models
from django.utils.translation import ugettext_lazy as _

from lino import dd
from lino.core import fields
from lino.utils.choosers import chooser


from lino.core.signals import pre_ui_delete, pre_ui_create, pre_ui_update
from lino.core.signals import pre_merge
from lino.core.signals import pre_add_child, pre_remove_child
from lino.core.signals import receiver


class ChangeTypes(dd.ChoiceList):

    """
    The list of possible choices for the `type` field
    of a :class:`Change`.
    """
    app_label = 'lino'
    verbose_name = _("Change Type")
    verbose_name_plural = _("Change Types")
add = ChangeTypes.add_item
add('C', _("Create"), 'create')
add('U', _("Update"), 'update')
add('D', _("Delete"), 'delete')
add('R', _("Remove child"), 'remove_child')
add('A', _("Add child"), 'add_child')
add('M', _("Merge"), 'merge')


class Change(dd.Model):

    """
    Each database change of a watched object will generate one Change record.
    """
    class Meta:
        verbose_name = _("Change")
        verbose_name_plural = _("Changes")

    time = models.DateTimeField()
    type = ChangeTypes.field()
    if settings.SITE.user_model:
        user = dd.ForeignKey(settings.SITE.user_model)

    object_type = models.ForeignKey(ContentType,
                                    related_name='changes_by_object',
                                    verbose_name=_("Object type"))
    object_id = dd.GenericForeignKeyIdField(object_type)
    #~ object = dd.GenericForeignKey('object_type','object_id',_("Object"),dont_merge=True)
    object = dd.GenericForeignKey('object_type', 'object_id', _("Object"))
    # see blog/2013/0123

    master_type = models.ForeignKey(ContentType,
                                    related_name='changes_by_master',
                                    verbose_name=_("Master type"))
    master_id = dd.GenericForeignKeyIdField(master_type)
    master = dd.GenericForeignKey('master_type', 'master_id', _("Master"))

    #~ summary = models.CharField(_("Summary"),max_length=200,blank=True)
    #~ description = dd.RichTextField(format='plain')
    diff = dd.RichTextField(_("Changes"), format='plain', blank=True)

    def __unicode__(self):
        # ~ return "#%s - %s" % (self.id,self.time)
        return "#%s" % self.id

    # NOTE: the following code is the same as in lino.mixins.Controllable
    # TODO: automate this behaviour in dd.GenericForeignKey
    @chooser(instance_values=True)
    def object_id_choices(cls, object_type):
        if object_type:
            return object_type.model_class().objects.all()
        return []

    def get_object_id_display(self, value):
        if self.object_type:
            try:
                return unicode(self.object_type.get_object_for_this_type(pk=value))
            except self.object_type.model_class().DoesNotExist, e:
                return "%s with pk %r does not exist" % (
                    dd.full_model_name(self.object_type.model_class()), value)

    @chooser(instance_values=True)
    def master_id_choices(cls, master_type):
        if master_type:
            return master_type.model_class().objects.all()
        return []

    def get_master_id_display(self, value):
        if self.master_type:
            try:
                return unicode(self.master_type.get_object_for_this_type(pk=value))
            except self.master_type.model_class().DoesNotExist, e:
                return "%s with pk %r does not exist" % (
                    dd.full_model_name(self.master_type.model_class()), value)
    # NOTE: the above code is the same as in lino.mixins.Controllable


class Changes(dd.Table):
    required = dd.required(user_level='admin')

    editable = False
    model = Change
    order_by = ['-time']
    detail_layout = """
    time user type master object id
    diff
    """

#~ class ChangesByObject(Changes):


class ChangesByMaster(Changes):

    """
    Slave Table showing the changes related to the current object
    """
    required = dd.required()
    column_names = 'time user type object diff object_type object_id'
    master_key = 'master'


class WatcherSpec:
    #~ def __init__(self,ignored_fields,master_key):

    def __init__(self, ignored_fields, get_master):
        self.ignored_fields = ignored_fields
        #~ self.master_key = master_key
        self.get_master = get_master
<commit_msg>Mod Changes: Add filtering parameters to Changes view<commit_after># Copyright 2012-2013 <NAME>
# This file is part of the Lino project.
# Lino is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# Lino is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with Lino; if not, see <http://www.gnu.org/licenses/>.

"""
The :xfile:`models` module for :mod:`lino.modlib.changes`.

It defines the :class:`Change` model
and the :func:`watch_changes` function.
It also adds a menu entry to the `Explorer` menu.
"""

import logging
logger = logging.getLogger(__name__)
#~ from lino.utils import dblogger

import datetime

from django.conf import settings
#~ from django.contrib.auth import models as auth
#~ from django.contrib.sessions import models as sessions
from django.contrib.contenttypes.models import ContentType


from django.db import models
from django.utils.translation import ugettext_lazy as _

from lino import dd
from lino.core import fields
from lino.utils.choosers import chooser


from lino.core.signals import pre_ui_delete, pre_ui_create, pre_ui_update
from lino.core.signals import pre_merge
from lino.core.signals import pre_add_child, pre_remove_child
from lino.core.signals import receiver


class ChangeTypes(dd.ChoiceList):

    """
    The list of possible choices for the `type` field
    of a :class:`Change`.
    """
    app_label = 'lino'
    verbose_name = _("Change Type")
    verbose_name_plural = _("Change Types")
add = ChangeTypes.add_item
add('C', _("Create"), 'create')
add('U', _("Update"), 'update')
add('D', _("Delete"), 'delete')
add('R', _("Remove child"), 'remove_child')
add('A', _("Add child"), 'add_child')
add('M', _("Merge"), 'merge')


class Change(dd.Model):

    """
    Each database change of a watched object will generate one Change record.
    """
    class Meta:
        verbose_name = _("Change")
        verbose_name_plural = _("Changes")

    time = models.DateTimeField()
    type = ChangeTypes.field()
    if settings.SITE.user_model:
        user = dd.ForeignKey(settings.SITE.user_model)

    object_type = models.ForeignKey(ContentType,
                                    related_name='changes_by_object',
                                    verbose_name=_("Object type"))
    object_id = dd.GenericForeignKeyIdField(object_type)
    #~ object = dd.GenericForeignKey('object_type','object_id',_("Object"),dont_merge=True)
    object = dd.GenericForeignKey('object_type', 'object_id', _("Object"))
    # see blog/2013/0123

    master_type = models.ForeignKey(ContentType,
                                    related_name='changes_by_master',
                                    verbose_name=_("Master type"))
    master_id = dd.GenericForeignKeyIdField(master_type)
    master = dd.GenericForeignKey('master_type', 'master_id', _("Master"))

    #~ summary = models.CharField(_("Summary"),max_length=200,blank=True)
    #~ description = dd.RichTextField(format='plain')
    diff = dd.RichTextField(_("Changes"), format='plain', blank=True)

    def __unicode__(self):
        # ~ return "#%s - %s" % (self.id,self.time)
        return "#%s" % self.id

    # NOTE: the following code is the same as in lino.mixins.Controllable
    # TODO: automate this behaviour in dd.GenericForeignKey
    @chooser(instance_values=True)
    def object_id_choices(cls, object_type):
        if object_type:
            return object_type.model_class().objects.all()
        return []

    def get_object_id_display(self, value):
        if self.object_type:
            try:
                return unicode(self.object_type.get_object_for_this_type(pk=value))
            except self.object_type.model_class().DoesNotExist, e:
                return "%s with pk %r does not exist" % (
                    dd.full_model_name(self.object_type.model_class()), value)

    @chooser(instance_values=True)
    def master_id_choices(cls, master_type):
        if master_type:
            return master_type.model_class().objects.all()
        return []

    def get_master_id_display(self, value):
        if self.master_type:
            try:
                return unicode(self.master_type.get_object_for_this_type(pk=value))
            except self.master_type.model_class().DoesNotExist, e:
                return "%s with pk %r does not exist" % (
                    dd.full_model_name(self.master_type.model_class()), value)
    # NOTE: the above code is the same as in lino.mixins.Controllable


class Changes(dd.Table):
    parameters = {
        'type': ChangeTypes.field(force_selection=False, blank=True),
        'date': models.DateField(_("Only changes from"), blank=True),
    }
    if settings.SITE.user_model:
        parameters['user'] = dd.ForeignKey(settings.SITE.user_model, blank=True)

    required = dd.required(user_level='admin')

    editable = False
    model = Change
    order_by = ['-time']
    detail_layout = """
    time user type master object id
    diff
    """

    @classmethod
    def get_request_queryset(cls, ar):
        qs = super(Changes, cls).get_request_queryset(ar)
        if not isinstance(qs, list):
            if ar.param_values.type:
                qs = qs.filter(type=ar.param_values.type)
            if ar.param_values.date:
                qs = qs.filter(time__range=(ar.param_values.date, ar.param_values.date+datetime.timedelta(1)))
            if settings.SITE.user_model and ar.param_values.user:
                qs = qs.filter(user=ar.param_values.user)
        return qs


class ChangesByMaster(Changes):

    """
    Slave Table showing the changes related to the current object
    """
    required = dd.required()
    column_names = 'time user type object diff object_type object_id'
    master_key = 'master'


class WatcherSpec:
    #~ def __init__(self,ignored_fields,master_key):

    def __init__(self, ignored_fields, get_master):
        self.ignored_fields = ignored_fields
        #~ self.master_key = master_key
        self.get_master = get_master

{'fraction_non_alphanumeric': 0.10562379877997827, 'fraction_numerical': 0.003426088409793599, 'mean_word_length': 3.1729428172942815, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7294107', 'n_tokens_mistral': 3631, 'n_tokens_neox': 3478, 'n_words': 969}