<commit_before>#
# Copyright (C) 2007-2012 by <NAME>, frePPLe bvba
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This library 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 Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# file : $URL$
# revision : $LastChangedRevision$  $LastChangedBy$
# date : $LastChangedDate$

r'''
An reusable application that contains common functionality of different
frePPLe data models.

The common functionality handles:
  - user preferences: reporting buckets, report start and end dates, language, csv delimiter
  - breadcrumbs
  - login using the e-mail address
  - generic report framework
  - database utility functions, mainly to handle SQL dates in a portable way
  - date and time bucket definition
  - middelware allowing users to set their preferred language
'''

from django import template
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.admin.util import unquote, get_deleted_objects
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404, render_to_response
from django.template.response import TemplateResponse
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.html import escape, escapejs
from django.utils.translation import ugettext_lazy as _
from django.utils.text import capfirst, get_text_list
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect

from freppledb.common.models import Comment

# Make our tags built-in, so we don't have to load them any more in our
# templates with a 'load' tag.
template.add_to_builtins('freppledb.common.templatetags.base_utils')

csrf_protect_m = method_decorator(csrf_protect)

class MultiDBModelAdmin(admin.ModelAdmin):
  r'''
  This class is an enhanced version of the django regular admin model.
  It adds:
     - support for multiple databases
          - store and load history information in the right database
          - assure prefix is maintained in the URLs
          - check for related objects in the right database
     - support for changing the primary key of an object
     - different logic to determine the next page to display

  See the standard code in the file django\contrib\admin\options.py
  The level of customization is relatively high, and this code is a bit of a
  concern for future upgrades of Django...
  '''

  def save_model(self, request, obj, form, change):        
    # TODO saving the object under a new key, but which already exists
    # TODO make this work from a form with an inline admin section
    if change:
      old_pk = request.path_info.rsplit("/",2)[1]
      if old_pk != obj.pk:
        # We are renaming an existing object.        
        # a) Save the new record in the right database
        obj.save(using=request.database)
        # b) All linked fields need updating.
        for related in obj._meta.get_all_related_objects():
          related.model._base_manager.using(request.database) \
            .filter(**{related.field.name: old_pk}) \
            .update(**{related.field.name: obj})
        # c) Move the comments and audit trail to the new key
        model_type = ContentType.objects.get_for_model(obj)
        Comment.objects.using(request.database). \
          filter(content_type__pk=model_type.id, object_pk=old_pk). \
          update(object_pk=obj.pk)
        LogEntry.objects.using(request.database). \
          filter(content_type__pk=model_type.id, object_id=old_pk). \
          update(object_id=obj.pk)
        # e) Delete the old record
        self.queryset(request).get(pk=old_pk).delete() 
        return        

    # New object or existing object without key change
    obj.save(using=request.database)

  def queryset(self, request):
    return super(MultiDBModelAdmin, self).queryset(request).using(request.database)

  def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
    return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=request.database, **kwargs)

  def formfield_for_manytomany(self, db_field, request=None, **kwargs):
    return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=request.database, **kwargs)

  def log_addition(self, request, object):
    """
    Log that an object has been successfully added.
    """
    from django.contrib.admin.models import LogEntry, ADDITION
    LogEntry(
        user_id         = request.user.pk,
        content_type_id = ContentType.objects.get_for_model(object).pk,
        object_id       = force_unicode(object.pk),
        object_repr     = force_unicode(object)[:200],
        action_flag     = ADDITION
    ).save(using=request.database)

  def log_change(self, request, object, message):
    """
    Log that an object has been successfully changed.
    """
    from django.contrib.admin.models import LogEntry, CHANGE
    LogEntry(
        user_id         = request.user.pk,
        content_type_id = ContentType.objects.get_for_model(object).pk,
        object_id       = force_unicode(object.pk),
        object_repr     = force_unicode(object)[:200],
        action_flag     = CHANGE,
        change_message  = message
    ).save(using=request.database)

  def log_deletion(self, request, object, object_repr):
    """
        Log that an object will be deleted. Note that this method is called
        before the deletion.
    """<commit_msg>- Django: support for renaming objects<commit_after>#
# Copyright (C) 2007-2012 by <NAME>, frePPLe bvba
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This library 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 Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# file : $URL$
# revision : $LastChangedRevision$  $LastChangedBy$
# date : $LastChangedDate$

r'''
An reusable application that contains common functionality of different
frePPLe data models.

The common functionality handles:
  - user preferences: reporting buckets, report start and end dates, language, csv delimiter
  - breadcrumbs
  - login using the e-mail address
  - generic report framework
  - database utility functions, mainly to handle SQL dates in a portable way
  - date and time bucket definition
  - middelware allowing users to set their preferred language
'''

from django import template
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.contrib import admin
from django.contrib.admin.models import LogEntry, CHANGE
from django.contrib.admin.util import unquote, get_deleted_objects
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404, render_to_response
from django.template.response import TemplateResponse
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.html import escape, escapejs
from django.utils.translation import ugettext_lazy as _
from django.utils.text import capfirst, get_text_list
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect

from freppledb.common.models import Comment

# Make our tags built-in, so we don't have to load them any more in our
# templates with a 'load' tag.
template.add_to_builtins('freppledb.common.templatetags.base_utils')

csrf_protect_m = method_decorator(csrf_protect)

class MultiDBModelAdmin(admin.ModelAdmin):
  r'''
  This class is an enhanced version of the django regular admin model.
  It adds:
     - support for multiple databases
          - store and load history information in the right database
          - assure prefix is maintained in the URLs
          - check for related objects in the right database
     - support for changing the primary key of an object
     - different logic to determine the next page to display

  See the standard code in the file django\contrib\admin\options.py
  The level of customization is relatively high, and this code is a bit of a
  concern for future upgrades of Django...
  '''
    
  def save_form(self, request, form, change):        
    obj = super(MultiDBModelAdmin, self).save_form(request, form, change)
    if change:
      old_pk = unquote(request.path_info.rsplit("/",2)[1])
      if old_pk != obj.pk:
        # The object was renamed. We continue handling the updates on the
        # old object. Only at the very end we will rename whatever needs to
        # be renamed.
        obj.new_pk = obj.pk       
        obj.pk = old_pk 
    return obj

  def queryset(self, request):
    return super(MultiDBModelAdmin, self).queryset(request).using(request.database)

  def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
    return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=request.database, **kwargs)

  def formfield_for_manytomany(self, db_field, request=None, **kwargs):
    return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=request.database, **kwargs)

  def log_addition(self, request, object):
    """
    Log that an object has been successfully added.
    """
    from django.contrib.admin.models import LogEntry, ADDITION
    LogEntry(
        user_id         = request.user.pk,
        content_type_id = ContentType.objects.get_for_model(object).pk,
        object_id       = force_unicode(object.pk),
        object_repr     = force_unicode(object)[:200],
        action_flag     = ADDITION
    ).save(using=request.database)

  def log_change(self, request, object, message):
    """
    Log that an object has been successfully changed.
    """    
    if hasattr(object, 'new_pk'):
      # We are renaming an existing object.        
      # a) Save the new record in the right database
      old_pk = object.pk
      object.pk = object.new_pk
      object.save(using=request.database)
      # b) All linked fields need updating.
      for related in object._meta.get_all_related_objects():
        related.model._base_manager.using(request.database) \
          .filter(**{related.field.name: old_pk}) \
          .update(**{related.field.name: object})
      # c) Move the comments and audit trail to the new key
      model_type = ContentType.objects.get_for_model(object)
      Comment.objects.using(request.database). \
        filter(content_type__pk=model_type.id, object_pk=old_pk). \
        update(object_pk=object.pk)
      LogEntry.objects.using(request.database). \
        filter(content_type__pk=model_type.id, object_id=old_pk). \
        update(object_id=object.pk)
      # e) Delete the old record
      self.queryset(request).get(pk=old_pk).delete()             
    LogEntry(
        user_id         = request.user.pk,
        content_type_id = ContentType.objects.get_for_model(object).pk,
        object_id       = force_unicode(object.pk),
        object_repr     = force_unicode(object)[:200],
        action_flag     = CHANGE,
        change_message  = message
    ).save(using=request.database)

  def log_deletion(self, request, object, object_repr):
    """
        Log that an object will be deleted. Note that this method is called
        before the deletion.
    """
{'fraction_non_alphanumeric': 0.07758138046868751, 'fraction_numerical': 0.00367911701191714, 'mean_word_length': 3.4962243797195254, '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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6976164', 'n_tokens_mistral': 3519, 'n_tokens_neox': 3419, 'n_words': 1190}