<commit_before>from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.management import call_command
from django.db import models, connections, transaction
from django.urls import reverse

from django_tenants.clone import CloneSchema
from .postgresql_backend.base import _check_schema_name
from .signals import post_schema_sync, schema_needs_to_be_sync
from .utils import get_creation_fakes_migrations, get_tenant_base_schema
from .utils import schema_exists, get_tenant_domain_model, get_public_schema_name, get_tenant_database_alias

class TenantMixin(models.Model):
    """
    All tenant models must inherit this class.
    """

    auto_drop_schema = False
    """
    USE THIS WITH CAUTION!
    Set this flag to true on a parent class if you want the schema to be
    automatically deleted if the tenant row gets deleted.
    """

    auto_create_schema = True
    """
    Set this flag to false on a parent class if you don't want the schema
    to be automatically created upon save.
    """

    schema_name = models.CharField(max_length=63, unique=True,
                                   validators=[_check_schema_name])

    domain_url = None
    """
    Leave this as None. Stores the current domain url so it can be used in the logs
    """

    class Meta:
        abstract = True

    def __enter__(self):
        """
        Syntax sugar which helps in celery tasks, cron jobs, and other scripts

        Usage:
            with Tenant.objects.get(schema_name='test') as tenant:
                # run some code in tenant test
            # run some code in previous tenant (public probably)
        """
        connection = connections[get_tenant_database_alias()]
        self._previous_tenant = connection.tenant
        self.activate()

    def __exit__(self, exc_type, exc_val, exc_tb):
        connection = connections[get_tenant_database_alias()]

        connection.set_tenant(self._previous_tenant)

    def activate(self):
        """
        Syntax sugar that helps at django shell with fast tenant changing

        Usage:
            Tenant.objects.get(schema_name='test').activate()
        """
        connection = connections[get_tenant_database_alias()]
        connection.set_tenant(self)

    @classmethod
    def deactivate(cls):
        """
        Syntax sugar, return to public schema

        Usage:
            test_tenant.deactivate()
            # or simpler
            Tenant.deactivate()
        """
        connection = connections[get_tenant_database_alias()]
        connection.set_schema_to_public()

    def save(self, verbosity=1, *args, **kwargs):
        connection = connections[get_tenant_database_alias()]
        is_new = self.pk is None
        has_schema = hasattr(connection, 'schema_name')
        if has_schema and is_new and connection.schema_name != get_public_schema_name():
            raise Exception("Can't create tenant outside the public schema. "
                            "Current schema is %s." % connection.schema_name)
        elif has_schema and not is_new and connection.schema_name not in (self.schema_name, get_public_schema_name()):
            raise Exception("Can't update tenant outside it's own schema or "
                            "the public schema. Current schema is %s."
                            % connection.schema_name)

        super().save(*args, **kwargs)

        if has_schema and is_new and self.auto_create_schema:
            try:
                self.create_schema(check_if_exists=True, verbosity=verbosity)
                post_schema_sync.send(sender=TenantMixin, tenant=self.serializable_fields())
            except Exception:
                # We failed creating the tenant, delete what we created and
                # re-raise the exception
                self.delete(force_drop=True)
                raise
        elif is_new:
            # although we are not using the schema functions directly, the signal might be registered by a listener
            schema_needs_to_be_sync.send(sender=TenantMixin, tenant=self.serializable_fields())
        elif not is_new and self.auto_create_schema and not schema_exists(self.schema_name):
            # Create schemas for existing models, deleting only the schema on failure
            try:
                self.create_schema(check_if_exists=True, verbosity=verbosity)
                post_schema_sync.send(sender=TenantMixin, tenant=self.serializable_fields())
            except Exception:
                # We failed creating the schema, delete what we created and
                # re-raise the exception
                self._drop_schema()
                raise

    def serializable_fields(self):
        """ in certain cases the user model isn't serializable so you may want to only send the id """
        return self

    def _drop_schema(self, force_drop=False):
        """ Drops the schema"""
        connection = connections[get_tenant_database_alias()]
        has_schema = hasattr(connection, 'schema_name')
        if has_schema and connection.schema_name not in (self.schema_name, get_public_schema_name()):
            raise Exception("Can't delete tenant outside it's own schema or "
                            "the public schema. Current schema is %s."
                            % connection.schema_name)

        if has_schema and schema_exists(self.schema_name) and (self.auto_drop_schema or force_drop):
            self.pre_drop()
            cursor = connection.cursor()
            cursor.execute('DROP SCHEMA %s CASCADE' % self.schema_name)

    def pre_drop(self):
        """
        This is a routine which you could override to backup the tenant schema before dropping.
        :return:
        """

    def delete(self, force_drop=False, *args, **kwargs):
        """
        Deletes this row. Drops the tenant's schema if the attribute
        auto_drop_schema set to True.
        """
        self._drop_schema(force_drop)
        super().delete(*args, **kwargs)

    def create_schema(self, check_if_exists=False, sync_schema=True,
                      verbosity=1):
        """
        Creates the schema 'schema_name' for this tenant. Optionally checks if
        the schema already exists before creating it. Returns true if the
        schema was created, false otherwise.
        """

        # safety check
        connection = connections[get_tenant_database_alias()]
        _check_schema_name(self.schema_name)
        cursor = connection.cursor()

        if check_if_exists and schema_exists(self.schema_name):
            return False

        fake_migrations = get_creation_fakes_migrations()

        if sync_schema:
            if fake_migrations:
                # copy tables and data from provided model schema
                base_schema = get_tenant_base_schema()
                clone_schema = CloneSchema()
                clone_schema.clone_schema(base_schema, self.schema_name)

                call_command('migrate_schemas',
                             tenant=True,
                             fake=True,
                             schema_name=self.schema_name,
                             interactive=False,
                             verbosity=verbosity)
            else:
                # create the schema
                cursor.execute('CREATE SCHEMA %s' % self.schema_name)
                call_command('migrate_schemas',
                             tenant=True,
                             schema_name=self.schema_name,
                             interactive=False,
                             verbosity=verbosity)

        connection.set_schema_to_public()

    def get_primary_domain(self):
        """
        Returns the primary domain of the tenant
        """
        try:
            domain = self.domains.get(is_primary=True)
            return domain
        except get_tenant_domain_model().DoesNotExist:
            return None

    def reverse(self, request, view_name):
        """
        Returns the URL of this tenant.
        """
        http_type = 'https://' if request.is_secure() else 'http://'

        domain = get_current_site(request).domain

        url = ''.join((http_type, self.schema_name, '.', domain, reverse(view_name)))

        return url

class DomainMixin(models.Model):
    """
    All models that store the domains must inherit this class
    """
    domain = models.CharField(max_length=253, unique=True, db_index=True)
    tenant = models.ForeignKey(settings.TENANT_MODEL, db_index=True, related_name='domains',
                               on_delete=models.CASCADE)

    # Set this to true if this is the primary domain
    is_primary = models.BooleanField(default=True)

    @transaction.atomic
    def save(self, *args, **kwargs):
        # Get all other primary domains with the same tenant
        domain_list = self.__class__.objects.filter(tenant=self.tenant, is_primary=True).exclude(pk=self.pk)
        # If we have no primary domain yet, set as primary domain by default
        self.is_primary = self.is_primary or (not domain_list.exists())
        if self.is_primary:
            # Remove primary status of existing domains for tenant
            domain_list.update(is_primary=False)
        super().save(*args, **kwargs)

    class Meta:
        abstract = True
<commit_msg>Fix nested with: behavior<commit_after>from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.management import call_command
from django.db import models, connections, transaction
from django.urls import reverse

from django_tenants.clone import CloneSchema
from .postgresql_backend.base import _check_schema_name
from .signals import post_schema_sync, schema_needs_to_be_sync
from .utils import get_creation_fakes_migrations, get_tenant_base_schema
from .utils import schema_exists, get_tenant_domain_model, get_public_schema_name, get_tenant_database_alias

class TenantMixin(models.Model):
    """
    All tenant models must inherit this class.
    """

    auto_drop_schema = False
    """
    USE THIS WITH CAUTION!
    Set this flag to true on a parent class if you want the schema to be
    automatically deleted if the tenant row gets deleted.
    """

    auto_create_schema = True
    """
    Set this flag to false on a parent class if you don't want the schema
    to be automatically created upon save.
    """

    schema_name = models.CharField(max_length=63, unique=True,
                                   validators=[_check_schema_name])

    domain_url = None
    """
    Leave this as None. Stores the current domain url so it can be used in the logs
    """

    _previous_tenant = []

    class Meta:
        abstract = True

    def __enter__(self):
        """
        Syntax sugar which helps in celery tasks, cron jobs, and other scripts

        Usage:
            with Tenant.objects.get(schema_name='test') as tenant:
                # run some code in tenant test
            # run some code in previous tenant (public probably)
        """
        connection = connections[get_tenant_database_alias()]
        self._previous_tenant.append(connection.tenant)
        self.activate()

    def __exit__(self, exc_type, exc_val, exc_tb):
        connection = connections[get_tenant_database_alias()]

        connection.set_tenant(self._previous_tenant.pop())

    def activate(self):
        """
        Syntax sugar that helps at django shell with fast tenant changing

        Usage:
            Tenant.objects.get(schema_name='test').activate()
        """
        connection = connections[get_tenant_database_alias()]
        connection.set_tenant(self)

    @classmethod
    def deactivate(cls):
        """
        Syntax sugar, return to public schema

        Usage:
            test_tenant.deactivate()
            # or simpler
            Tenant.deactivate()
        """
        connection = connections[get_tenant_database_alias()]
        connection.set_schema_to_public()

    def save(self, verbosity=1, *args, **kwargs):
        connection = connections[get_tenant_database_alias()]
        is_new = self.pk is None
        has_schema = hasattr(connection, 'schema_name')
        if has_schema and is_new and connection.schema_name != get_public_schema_name():
            raise Exception("Can't create tenant outside the public schema. "
                            "Current schema is %s." % connection.schema_name)
        elif has_schema and not is_new and connection.schema_name not in (self.schema_name, get_public_schema_name()):
            raise Exception("Can't update tenant outside it's own schema or "
                            "the public schema. Current schema is %s."
                            % connection.schema_name)

        super().save(*args, **kwargs)

        if has_schema and is_new and self.auto_create_schema:
            try:
                self.create_schema(check_if_exists=True, verbosity=verbosity)
                post_schema_sync.send(sender=TenantMixin, tenant=self.serializable_fields())
            except Exception:
                # We failed creating the tenant, delete what we created and
                # re-raise the exception
                self.delete(force_drop=True)
                raise
        elif is_new:
            # although we are not using the schema functions directly, the signal might be registered by a listener
            schema_needs_to_be_sync.send(sender=TenantMixin, tenant=self.serializable_fields())
        elif not is_new and self.auto_create_schema and not schema_exists(self.schema_name):
            # Create schemas for existing models, deleting only the schema on failure
            try:
                self.create_schema(check_if_exists=True, verbosity=verbosity)
                post_schema_sync.send(sender=TenantMixin, tenant=self.serializable_fields())
            except Exception:
                # We failed creating the schema, delete what we created and
                # re-raise the exception
                self._drop_schema()
                raise

    def serializable_fields(self):
        """ in certain cases the user model isn't serializable so you may want to only send the id """
        return self

    def _drop_schema(self, force_drop=False):
        """ Drops the schema"""
        connection = connections[get_tenant_database_alias()]
        has_schema = hasattr(connection, 'schema_name')
        if has_schema and connection.schema_name not in (self.schema_name, get_public_schema_name()):
            raise Exception("Can't delete tenant outside it's own schema or "
                            "the public schema. Current schema is %s."
                            % connection.schema_name)

        if has_schema and schema_exists(self.schema_name) and (self.auto_drop_schema or force_drop):
            self.pre_drop()
            cursor = connection.cursor()
            cursor.execute('DROP SCHEMA %s CASCADE' % self.schema_name)

    def pre_drop(self):
        """
        This is a routine which you could override to backup the tenant schema before dropping.
        :return:
        """

    def delete(self, force_drop=False, *args, **kwargs):
        """
        Deletes this row. Drops the tenant's schema if the attribute
        auto_drop_schema set to True.
        """
        self._drop_schema(force_drop)
        super().delete(*args, **kwargs)

    def create_schema(self, check_if_exists=False, sync_schema=True,
                      verbosity=1):
        """
        Creates the schema 'schema_name' for this tenant. Optionally checks if
        the schema already exists before creating it. Returns true if the
        schema was created, false otherwise.
        """

        # safety check
        connection = connections[get_tenant_database_alias()]
        _check_schema_name(self.schema_name)
        cursor = connection.cursor()

        if check_if_exists and schema_exists(self.schema_name):
            return False

        fake_migrations = get_creation_fakes_migrations()

        if sync_schema:
            if fake_migrations:
                # copy tables and data from provided model schema
                base_schema = get_tenant_base_schema()
                clone_schema = CloneSchema()
                clone_schema.clone_schema(base_schema, self.schema_name)

                call_command('migrate_schemas',
                             tenant=True,
                             fake=True,
                             schema_name=self.schema_name,
                             interactive=False,
                             verbosity=verbosity)
            else:
                # create the schema
                cursor.execute('CREATE SCHEMA %s' % self.schema_name)
                call_command('migrate_schemas',
                             tenant=True,
                             schema_name=self.schema_name,
                             interactive=False,
                             verbosity=verbosity)

        connection.set_schema_to_public()

    def get_primary_domain(self):
        """
        Returns the primary domain of the tenant
        """
        try:
            domain = self.domains.get(is_primary=True)
            return domain
        except get_tenant_domain_model().DoesNotExist:
            return None

    def reverse(self, request, view_name):
        """
        Returns the URL of this tenant.
        """
        http_type = 'https://' if request.is_secure() else 'http://'

        domain = get_current_site(request).domain

        url = ''.join((http_type, self.schema_name, '.', domain, reverse(view_name)))

        return url

class DomainMixin(models.Model):
    """
    All models that store the domains must inherit this class
    """
    domain = models.CharField(max_length=253, unique=True, db_index=True)
    tenant = models.ForeignKey(settings.TENANT_MODEL, db_index=True, related_name='domains',
                               on_delete=models.CASCADE)

    # Set this to true if this is the primary domain
    is_primary = models.BooleanField(default=True)

    @transaction.atomic
    def save(self, *args, **kwargs):
        # Get all other primary domains with the same tenant
        domain_list = self.__class__.objects.filter(tenant=self.tenant, is_primary=True).exclude(pk=self.pk)
        # If we have no primary domain yet, set as primary domain by default
        self.is_primary = self.is_primary or (not domain_list.exists())
        if self.is_primary:
            # Remove primary status of existing domains for tenant
            domain_list.update(is_primary=False)
        super().save(*args, **kwargs)

    class Meta:
        abstract = True
<|endoftext|>


<commit_before>from django.db import models
from django.db.models.signals import post_save, pre_save
from django.contrib.auth.models import User
from exceptions import *
import tagging
from tagging.fields import TagField

import datetime
import difflib

class Page(models.Model):
    """
    Wiki pages are unique by title and slug.

    Pages are placeholder's for Revisions.  Each page can have many revisions.
    Upon creation a default hello world page is created and published.

    A Page can only have one unpublished Reversion.  A blank unpublished
    Revision is created when a Page is checked out.

    A Page cannot be checked in while an unblished Revision for it exists.
    """
    title = models.CharField(max_length=255, unique=True)
    slug = models.SlugField(unique=True)
    tags = TagField()
    creator = models.ForeignKey(User)
    is_editable = models.BooleanField(default=True)
    is_checked_out = models.BooleanField(default=False)
    created_on = models.DateTimeField(auto_now_add=True, editable=False)

    def check_out(self, user):
        """
        Before checking a page out, check if page is already checked out.  If
        the page is already checked out, raise PageAlreadyCheckedOut exception.

        If the page isn't checked out, create a skeleton revision, check out page.
        """
        if self.is_checked_out:
            raise PageAlreadyCheckedOut
        revision = Revision.objects.create(
            page=self,
            author=user,
            body=self.latest_revision().body
        )
        self.is_checked_out = True
        self.save()
        return revision

    def check_in(self):
        """
        When model is saved a pre_save signal will throw a UnpublishedRevisionExists
        exception if the unpublished revision isn't published or abandoned first.
        """
        self.is_checked_out = False
        self.save()

    def latest_revision(self):
        try:
            return Revision.objects.filter(page=self,is_published=True).latest('published_on')
        except Revision.DoesNotExist:
            return None

    def history(self):
        return Revision.objects.filter(page=self, is_published=True).order_by('-published_on')

    def unpublished_revision(self):
        try:
            return Revision.objects.filter(page=self, is_published=False).latest('created_on')
        except Revision.DoesNotExist:
            return None

    def count_revisions(self):
        return Revision.objects.filter(page=self, is_published=True).count()

    def who_checked_out(self):
        try:
            return Revision.objects.filter(page=self, is_published=False).latest('created_on').author
        except Revision.DoesNotExist:
            return None

    def compare(self, rev1, rev2):
        if rev1 == rev2:
            raise ComparingSameRevision
        revisions = Revision.objects.filter(page=self, pk__in=sorted([rev1, rev2]))
        if revisions.count() != 2:
            raise RevisionDoesNotExist

        comparison, created = Comparison.objects.get_or_create(
            page=self,
            rev1=revisions[0],
            rev2=revisions[1],
        )
        return comparison

    @models.permalink
    def get_absolute_url(self):
        return ('page_detail', (), { 'slug': self.slug })

    def __unicode__(self):
        return self.title

    class Meta:
        ordering = ['title']

class Revision(models.Model):
    """
    All Revisions belong to a Page.

    A Revision is created only when a Page is checked out.

    A Revision can only be deleted before it is published, once published it
    cannot be changed.
    """
    page = models.ForeignKey(Page)
    author = models.ForeignKey(User)
    number = models.IntegerField(default=0, editable=False)
    body = models.TextField()
    is_published = models.BooleanField(default=False)
    published_on = models.DateTimeField(blank=True, null=True, editable=False)
    created_on = models.DateTimeField(auto_now_add=True, editable=False)
    edited_on = models.DateTimeField(auto_now=True, editable=False)

    def publish(self, check_in_page=True):
        self.is_published = True
        self.save()

        if check_in_page:
            self.page.check_in()

    def __unicode__(self):
        return '%s #%s' % (self.page.title, self.number)

    class Meta:
        ordering = ['number']
        unique_together = ("page", "number")

def create_first_revision(sender, instance, created, **kwargs):
    """Creates a "blank" revision for a new page."""
    if created:
        Revision.objects.create(
            page=instance,
            author=instance.creator,
            body="hello world!",
            is_published=True,
        )

def check_page_already_checked_out(page_new, page_old):
    """Checks to see if page is checked out, can't be checked out again."""
    if page_old is not None and page_new.is_checked_out and page_old.is_checked_out is True:
        raise PageAlreadyCheckedOut

def check_page_unpublished_revisions(page_new):
    """Before a page is checked in there can be no unpublished revisions"""
    if not page_new.is_checked_out and Revision.objects.filter(page=page_new, is_published=False).count() > 0:
        raise UnpublishedRevisionExists

def check_revision_already_published(sender, instance, **kwargs):
    """Published revisions can not be unpublished"""
    if instance.pk != None:
        if instance.is_published:
            try:
                Revision.objects.get(pk=instance.pk, is_published=True)
            except Revision.DoesNotExist:
                return
            raise AlreadyPublishedRevision

def update_published_on(sender, instance, **kwargs):
    """If page goes from not published to published, update published_on date."""
    published_on = datetime.datetime.now()
    latest = instance.page.latest_revision()

    if latest is None:
        number = 1
    else:
        number = latest.number + 1

    try:
        original = Revision.objects.get(pk=instance.pk)
        if not original.is_published and instance.is_published:
            instance.published_on = published_on
            instance.number = number
    except Revision.DoesNotExist:
        if instance.is_published:
            instance.published_on = published_on
            instance.number = number

def page_pre_save_maintenance(sender, instance, **kwargs):
    if instance.pk:
        check_page_already_checked_out(instance, Page.objects.get(pk=instance.pk))
        check_page_unpublished_revisions(instance)

post_save.connect(create_first_revision, sender=Page)
pre_save.connect(page_pre_save_maintenance, sender=Page)
pre_save.connect(check_revision_already_published, sender=Revision)
pre_save.connect(update_published_on, sender=Revision)

class Comparison(models.Model):
    page = models.ForeignKey(Page)
    rev1 = models.ForeignKey(Revision, related_name='rev1')
    rev2 = models.ForeignKey(Revision, related_name='rev2')
    diff_text = models.TextField(default='blank')

    def __unicode__(self):
        return u'%s: %s vs %s' % (self.page.title, self.rev1.pk, self.rev2.pk)

def comparison_calculate_diff(sender, instance, created, **kwargs):
    from utils.diff_match_patch import diff_match_patch
    diff = diff_match_patch()
    diff_array = diff.diff_main(instance.rev1.body, instance.rev2.body)
    diff.diff_cleanupSemantic(diff_array)
    instance.diff_text = diff.diff_prettyHtml(diff_array)
    instance.save()

post_save.connect(comparison_calculate_diff, sender=Comparison)
<commit_msg>fixed inifite loop in comparison post_save signal<commit_after>from django.db import models
from django.db.models.signals import post_save, pre_save
from django.contrib.auth.models import User
from exceptions import *
import tagging
from tagging.fields import TagField

import datetime
import difflib

class Page(models.Model):
    """
    Wiki pages are unique by title and slug.

    Pages are placeholder's for Revisions.  Each page can have many revisions.
    Upon creation a default hello world page is created and published.

    A Page can only have one unpublished Reversion.  A blank unpublished
    Revision is created when a Page is checked out.

    A Page cannot be checked in while an unblished Revision for it exists.
    """
    title = models.CharField(max_length=255, unique=True)
    slug = models.SlugField(unique=True)
    tags = TagField()
    creator = models.ForeignKey(User)
    is_editable = models.BooleanField(default=True)
    is_checked_out = models.BooleanField(default=False)
    created_on = models.DateTimeField(auto_now_add=True, editable=False)

    def check_out(self, user):
        """
        Before checking a page out, check if page is already checked out.  If
        the page is already checked out, raise PageAlreadyCheckedOut exception.

        If the page isn't checked out, create a skeleton revision, check out page.
        """
        if self.is_checked_out:
            raise PageAlreadyCheckedOut
        revision = Revision.objects.create(
            page=self,
            author=user,
            body=self.latest_revision().body
        )
        self.is_checked_out = True
        self.save()
        return revision

    def check_in(self):
        """
        When model is saved a pre_save signal will throw a UnpublishedRevisionExists
        exception if the unpublished revision isn't published or abandoned first.
        """
        self.is_checked_out = False
        self.save()

    def latest_revision(self):
        try:
            return Revision.objects.filter(page=self,is_published=True).latest('published_on')
        except Revision.DoesNotExist:
            return None

    def history(self):
        return Revision.objects.filter(page=self, is_published=True).order_by('-published_on')

    def unpublished_revision(self):
        try:
            return Revision.objects.filter(page=self, is_published=False).latest('created_on')
        except Revision.DoesNotExist:
            return None

    def count_revisions(self):
        return Revision.objects.filter(page=self, is_published=True).count()

    def who_checked_out(self):
        try:
            return Revision.objects.filter(page=self, is_published=False).latest('created_on').author
        except Revision.DoesNotExist:
            return None

    def compare(self, rev1, rev2):
        if rev1 == rev2:
            raise ComparingSameRevision
        revisions = Revision.objects.filter(page=self, pk__in=sorted([rev1, rev2]))
        if revisions.count() != 2:
            raise RevisionDoesNotExist

        comparison, created = Comparison.objects.get_or_create(
            page=self,
            rev1=revisions[0],
            rev2=revisions[1],
        )
        return comparison

    @models.permalink
    def get_absolute_url(self):
        return ('page_detail', (), { 'slug': self.slug })

    def __unicode__(self):
        return self.title

    class Meta:
        ordering = ['title']

class Revision(models.Model):
    """
    All Revisions belong to a Page.

    A Revision is created only when a Page is checked out.

    A Revision can only be deleted before it is published, once published it
    cannot be changed.
    """
    page = models.ForeignKey(Page)
    author = models.ForeignKey(User)
    number = models.IntegerField(default=0, editable=False)
    body = models.TextField()
    is_published = models.BooleanField(default=False)
    published_on = models.DateTimeField(blank=True, null=True, editable=False)
    created_on = models.DateTimeField(auto_now_add=True, editable=False)
    edited_on = models.DateTimeField(auto_now=True, editable=False)

    def publish(self, check_in_page=True):
        self.is_published = True
        self.save()

        if check_in_page:
            self.page.check_in()

    def __unicode__(self):
        return '%s #%s' % (self.page.title, self.number)

    class Meta:
        ordering = ['number']
        unique_together = ("page", "number")

def create_first_revision(sender, instance, created, **kwargs):
    """Creates a "blank" revision for a new page."""
    if created:
        Revision.objects.create(
            page=instance,
            author=instance.creator,
            body="hello world!",
            is_published=True,
        )

def check_page_already_checked_out(page_new, page_old):
    """Checks to see if page is checked out, can't be checked out again."""
    if page_old is not None and page_new.is_checked_out and page_old.is_checked_out is True:
        raise PageAlreadyCheckedOut

def check_page_unpublished_revisions(page_new):
    """Before a page is checked in there can be no unpublished revisions"""
    if not page_new.is_checked_out and Revision.objects.filter(page=page_new, is_published=False).count() > 0:
        raise UnpublishedRevisionExists

def check_revision_already_published(sender, instance, **kwargs):
    """Published revisions can not be unpublished"""
    if instance.pk != None:
        if instance.is_published:
            try:
                Revision.objects.get(pk=instance.pk, is_published=True)
            except Revision.DoesNotExist:
                return
            raise AlreadyPublishedRevision

def update_published_on(sender, instance, **kwargs):
    """If page goes from not published to published, update published_on date."""
    published_on = datetime.datetime.now()
    latest = instance.page.latest_revision()

    if latest is None:
        number = 1
    else:
        number = latest.number + 1

    try:
        original = Revision.objects.get(pk=instance.pk)
        if not original.is_published and instance.is_published:
            instance.published_on = published_on
            instance.number = number
    except Revision.DoesNotExist:
        if instance.is_published:
            instance.published_on = published_on
            instance.number = number

def page_pre_save_maintenance(sender, instance, **kwargs):
    if instance.pk:
        check_page_already_checked_out(instance, Page.objects.get(pk=instance.pk))
        check_page_unpublished_revisions(instance)

post_save.connect(create_first_revision, sender=Page)
pre_save.connect(page_pre_save_maintenance, sender=Page)
pre_save.connect(check_revision_already_published, sender=Revision)
pre_save.connect(update_published_on, sender=Revision)

class Comparison(models.Model):
    page = models.ForeignKey(Page)
    rev1 = models.ForeignKey(Revision, related_name='rev1')
    rev2 = models.ForeignKey(Revision, related_name='rev2')
    diff_text = models.TextField(default='blank')

    def __unicode__(self):
        return u'%s: %s vs %s' % (self.page.title, self.rev1.pk, self.rev2.pk)

def comparison_calculate_diff(sender, instance, created, **kwargs):
    if created:
        from utils.diff_match_patch import diff_match_patch
        diff = diff_match_patch()
        diff_array = diff.diff_main(instance.rev1.body, instance.rev2.body)
        diff.diff_cleanupSemantic(diff_array)
        instance.diff_text = diff.diff_prettyHtml(diff_array)
        instance.save()

post_save.connect(comparison_calculate_diff, sender=Comparison)
<|endoftext|>


<commit_before>"""
Actual build definitions.

This is kinda complex, so here's a high-level overview of exactly how the build
process works:

The main point is to assume as little as possible about the build slave (and
thus transfer as much smartsfrom the master to the slave as possible). The slave
just needs to assure that Python is installed, a database is accessible (according
to the slave config), and whatever headers needed to build database modules exist.

The steps, then, are:

    * Make the SVN checkout of Django.

    * Transfer virtualenv.py from the master to the slave.

    * Create a virtualenv sandbox and install any extra prereqs (database
      modules, really).

    * Generate a Django settings file from the slave config.

    * Run Django's test suite using that settings file.

This sandbox is shared for each (python, database) combination; this prevents
needing to build the database wrappers each time.

However, this *does* mean that we can't test installing Django (via setup.py
install or friends) and that the tests pass against the *installed* files (which
hasn't always been true in the past). If we did, and if a buildslave wanted
to run multuple builds in parallel, we'd get conflicts. Also, if we install
the recommended way (setup.py install), then we can't uninstall easily.

So that's a big FIXME for later. Perhaps there's a clever way to have a new
virtualenv for each build but still avoid re-building database bindings...
"""

import itertools
from buildbot.config import BuilderConfig
from buildbot.process.factory import BuildFactory
from . import buildsteps
from .utils import parse_version_spec

def get_builders(branches, slaves):
    """
    Gets a list of builders for entry in BuildmasterConfig['builders']

    Creates a builder for each (branch, python, database) combination.
    """
    builders = []

    # Figure out the superset of pythons and databases to test against. Since DB
    # entries are as specific as possible ('postgresql8.3.1') there's some munging
    # that needs to happen get the exact correct subset.

    all_dbs = set()
    all_pythons = set()
    for slave in slaves:
        all_dbs.update(parse_version_spec(db) for db in slave.databases)
        all_pythons.update(k for k in slave.pythons if slave.pythons[k])

    # Now create a builder for each (branch, python, database) combo.
    combos = itertools.product(branches, all_pythons, all_dbs)
    for (branch, python, database) in combos:
        # Figure out which slaves can run this combo by asking the slave.
        builder_slaves = [slave for slave in slaves if slave.can_build(python, database)]

        # If none can we have to skip this combo.
        if not builder_slaves:
            continue

        # Make a builder config for this combo.
        builders.append(BuilderConfig(
            name = '%s-%s-%s%s' % (branch, python, database.name, database.version),
            factory = make_factory(branch, python, database),
            slavenames = [s.slavename for s in builder_slaves],
        ))

    return builders

def make_factory(branch, python, database):
    """
    Generates the BuildFactory (e.g. set of build steps) for this (branch,
    python, database) combo. The series of steps is described in the module
    docstring, above.
    """
    f = BuildFactory()
    f.addSteps([
        buildsteps.DjangoSVN(branch=branch),
        buildsteps.DownloadVirtualenv(),
        buildsteps.UpdateVirtualenv(python=python, db=database),
        buildsteps.GenerateSettings(python=python, db=database),
        buildsteps.TestDjango(python=python, db=database),
    ])
    return f
<commit_msg>Fixed environment naming a bit.<commit_after>"""
Actual build definitions.

This is kinda complex, so here's a high-level overview of exactly how the build
process works:

The main point is to assume as little as possible about the build slave (and
thus transfer as much smartsfrom the master to the slave as possible). The slave
just needs to assure that Python is installed, a database is accessible (according
to the slave config), and whatever headers needed to build database modules exist.

The steps, then, are:

    * Make the SVN checkout of Django.

    * Transfer virtualenv.py from the master to the slave.

    * Create a virtualenv sandbox and install any extra prereqs (database
      modules, really).

    * Generate a Django settings file from the slave config.

    * Run Django's test suite using that settings file.

This sandbox is shared for each (python, database) combination; this prevents
needing to build the database wrappers each time.

However, this *does* mean that we can't test installing Django (via setup.py
install or friends) and that the tests pass against the *installed* files (which
hasn't always been true in the past). If we did, and if a buildslave wanted
to run multuple builds in parallel, we'd get conflicts. Also, if we install
the recommended way (setup.py install), then we can't uninstall easily.

So that's a big FIXME for later. Perhaps there's a clever way to have a new
virtualenv for each build but still avoid re-building database bindings...
"""

import itertools
from buildbot.config import BuilderConfig
from buildbot.process.factory import BuildFactory
from . import buildsteps
from .utils import parse_version_spec

def get_builders(branches, slaves):
    """
    Gets a list of builders for entry in BuildmasterConfig['builders']

    Creates a builder for each (branch, python, database) combination.
    """
    builders = []

    # Figure out the superset of pythons and databases to test against. Since DB
    # entries are as specific as possible ('postgresql8.3.1') there's some munging
    # that needs to happen get the exact correct subset.

    all_dbs = set()
    all_pythons = set()
    for slave in slaves:
        all_dbs.update(parse_version_spec(db) for db in slave.databases)
        all_pythons.update(k for k in slave.pythons if slave.pythons[k])

    # Now create a builder for each (branch, python, database) combo.
    combos = itertools.product(branches, all_pythons, all_dbs)
    for (branch, python, database) in combos:
        # Figure out which slaves can run this combo by asking the slave.
        builder_slaves = [slave for slave in slaves if slave.can_build(python, database)]

        # If none can we have to skip this combo.
        if not builder_slaves:
            continue

        # Make a builder config for this combo.
        builders.append(BuilderConfig(
            name = '%s-python%s-%s%s' % (branch, python, database.name, database.version),
            factory = make_factory(branch, python, database),
            slavenames = [s.slavename for s in builder_slaves],
        ))

    return builders

def make_factory(branch, python, database):
    """
    Generates the BuildFactory (e.g. set of build steps) for this (branch,
    python, database) combo. The series of steps is described in the module
    docstring, above.
    """
    f = BuildFactory()
    f.addSteps([
        buildsteps.DjangoSVN(branch=branch),
        buildsteps.DownloadVirtualenv(),
        buildsteps.UpdateVirtualenv(python=python, db=database),
        buildsteps.GenerateSettings(python=python, db=database),
        buildsteps.TestDjango(python=python, db=database),
    ])
    return f
<|endoftext|>


<commit_before>__version__ = "0.1.0a1"
<commit_msg>Bumped version to 0.1.0.<commit_after>__version__ = "0.1.0"
<|endoftext|>


<commit_before>"""
ElementTree interface to an XRD document.
"""

import sys
import random
import functools

from datetime import datetime
from time import strptime

from openid import xri
from openid.oidutil import importElementTree
ElementTree = importElementTree()

# the different elementtree modules don't have a common exception
# model. We just want to be able to catch the exceptions that signify
# malformed XML data and wrap them, so that the other library code
# doesn't have to know which XML library we're using.
try:
    # Make the parser raise an exception so we can sniff out the type
    # of exceptions
    ElementTree.XML('> purposely malformed XML <')
except (SystemExit, MemoryError, AssertionError, ImportError):
    raise
except:
    XMLError = sys.exc_info()[0]

class XRDSError(Exception):
    '''
    General error with the XRDS document.
    '''
    pass

class XRDSFraud(XRDSError):
    """Raised when there's an assertion in the XRDS that it does not have
    the authority to make.
    """

def parseXRDS(text):
    """Parse the given text as an XRDS document.

    @return: ElementTree containing an XRDS document

    @raises XRDSError: When there is a parse error or the document does
        not contain an XRDS.
    """
    try:
        element = ElementTree.XML(text)
    except XMLError:
        raise XRDSError('Error parsing document as XML', text)
    else:
        tree = ElementTree.ElementTree(element)
        if not isXRDS(tree):
            raise XRDSError('Not an XRDS document', text)

        return tree

XRD_NS_2_0 = 'xri://$xrd*($v*2.0)'
XRDS_NS = 'xri://$xrds'

def nsTag(ns, t):
    return '{%s}%s' % (ns, t)

def mkXRDTag(t):
    """basestring -> basestring

    Create a tag name in the XRD 2.0 XML namespace suitable for using
    with ElementTree
    """
    return nsTag(XRD_NS_2_0, t)

def mkXRDSTag(t):
    """basestring -> basestring

    Create a tag name in the XRDS XML namespace suitable for using
    with ElementTree
    """
    return nsTag(XRDS_NS, t)

# Tags that are used in Yadis documents
root_tag = mkXRDSTag('XRDS')
service_tag = mkXRDTag('Service')
xrd_tag = mkXRDTag('XRD')
type_tag = mkXRDTag('Type')
uri_tag = mkXRDTag('URI')
expires_tag = mkXRDTag('Expires')

# Other XRD tags
canonicalID_tag = mkXRDTag('CanonicalID')

def isXRDS(xrd_tree):
    """Is this document an XRDS document?"""
    root = xrd_tree.getroot()
    return root.tag == root_tag

def getYadisXRD(xrd_tree):
    """Return the XRD element that should contain the Yadis services"""
    xrd = None

    # for the side-effect of assigning the last one in the list to the
    # xrd variable
    for xrd in xrd_tree.findall(xrd_tag):
        pass

    # There were no elements found, or else xrd would be set to the
    # last one
    if xrd is None:
        raise XRDSError('No XRD present in tree')

    return xrd

def getCanonicalID(iname, xrd_tree):
    """Return the CanonicalID from this XRDS document.

    @param iname: the XRI being resolved.
    @type iname: unicode

    @param xrd_tree: The XRDS output from the resolver.
    @type xrd_tree: ElementTree

    @returns: The XRI CanonicalID or None.
    @returntype: unicode or None
    """
    xrd_list = xrd_tree.findall(xrd_tag)
    xrd_list.reverse()

    try:
        canonicalID = xri.XRI(xrd_list[0].findall(canonicalID_tag)[0].text)
    except IndexError:
        return None

    childID = canonicalID.lower()

    for xrd in xrd_list[1:]:
        # XXX: can't use rsplit until we require python >= 2.4.
        parent_sought = childID[:childID.rindex('!')]
        parent = xri.XRI(xrd.findtext(canonicalID_tag))
        if parent_sought != parent.lower():
            raise XRDSFraud("%r can not come from %s" % (childID, parent))

        childID = parent_sought

    root = xri.rootAuthority(iname)
    if not xri.providerIsAuthoritative(root, childID):
        raise XRDSFraud("%r can not come from root %r" % (childID, root))

    return canonicalID

@functools.total_ordering
class _Max(object):
    """
    Value that compares greater than any other value.

    Should only be used as a singleton. Implemented for use as a
    priority value for when a priority is not specified.
    """
    def __lt__(self, other):
        return isinstance(other, self.__class__)

    def __eq__(self, other):
        return isinstance(other, self.__class__)

Max = _Max()

def getPriorityStrict(element):
    """Get the priority of this element.

    Raises ValueError if the value of the priority is invalid. If no
    priority is specified, it returns a value that compares greater
    than any other value.
    """
    prio_str = element.get('priority')
    if prio_str is not None:
        prio_val = int(prio_str)
        if prio_val >= 0:
            return prio_val
        else:
            raise ValueError('Priority values must be non-negative integers')

    # Any errors in parsing the priority fall through to here
    return Max

def getPriority(element):
    """Get the priority of this element

    Returns Max if no priority is specified or the priority value is invalid.
    """
    try:
        return getPriorityStrict(element)
    except ValueError:
        return Max

def prioSort(elements):
    """Sort a list of elements that have priority attributes"""
    # Randomize the services before sorting so that equal priority
    # elements are load-balanced.
    random.shuffle(elements)

    sorted_elems = sorted(elements, key=getPriority)
    return sorted_elems

def iterServices(xrd_tree):
    """Return an iterable over the Service elements in the Yadis XRD

    sorted by priority"""
    xrd = getYadisXRD(xrd_tree)
    return prioSort(xrd.findall(service_tag))

def getURI(service_element):
    """Given a Service element, return content of its URI tag or
    None if absent
    """
    uri_element = service_element.find(uri_tag)
    return uri_element.text if uri_element is not None else None

def getTypeURIs(service_element):
    """Given a Service element, return a list of the contents of all
    Type tags"""
    return [type_element.text for type_element
            in service_element.findall(type_tag)]

def matches_types(element, types):
    '''
    Checks if the service element supports any of the types.
    '''
    return not types or set(types).intersection(set(getTypeURIs(element)))

def get_elements(data, types):
    '''
    Parses an XRDS document and returns a list of service elements matching
    types.
    '''
    elements = iterServices(parseXRDS(data))
    return [e for e in elements if matches_types(e, types) and getURI(e) is not None]
<commit_msg>Inline isXRDS.<commit_after>"""
ElementTree interface to an XRD document.
"""

import sys
import random
import functools

from datetime import datetime
from time import strptime

from openid import xri
from openid.oidutil import importElementTree
ElementTree = importElementTree()

# the different elementtree modules don't have a common exception
# model. We just want to be able to catch the exceptions that signify
# malformed XML data and wrap them, so that the other library code
# doesn't have to know which XML library we're using.
try:
    # Make the parser raise an exception so we can sniff out the type
    # of exceptions
    ElementTree.XML('> purposely malformed XML <')
except (SystemExit, MemoryError, AssertionError, ImportError):
    raise
except:
    XMLError = sys.exc_info()[0]

class XRDSError(Exception):
    '''
    General error with the XRDS document.
    '''
    pass

class XRDSFraud(XRDSError):
    """Raised when there's an assertion in the XRDS that it does not have
    the authority to make.
    """

def parseXRDS(text):
    """Parse the given text as an XRDS document.

    @return: ElementTree containing an XRDS document

    @raises XRDSError: When there is a parse error or the document does
        not contain an XRDS.
    """
    try:
        element = ElementTree.XML(text)
    except XMLError:
        raise XRDSError('Error parsing document as XML', text)
    else:
        tree = ElementTree.ElementTree(element)
        if tree.getroot().tag != root_tag:
            raise XRDSError('Not an XRDS document', text)

        return tree

XRD_NS_2_0 = 'xri://$xrd*($v*2.0)'
XRDS_NS = 'xri://$xrds'

def nsTag(ns, t):
    return '{%s}%s' % (ns, t)

def mkXRDTag(t):
    """basestring -> basestring

    Create a tag name in the XRD 2.0 XML namespace suitable for using
    with ElementTree
    """
    return nsTag(XRD_NS_2_0, t)

def mkXRDSTag(t):
    """basestring -> basestring

    Create a tag name in the XRDS XML namespace suitable for using
    with ElementTree
    """
    return nsTag(XRDS_NS, t)

# Tags that are used in Yadis documents
root_tag = mkXRDSTag('XRDS')
service_tag = mkXRDTag('Service')
xrd_tag = mkXRDTag('XRD')
type_tag = mkXRDTag('Type')
uri_tag = mkXRDTag('URI')
expires_tag = mkXRDTag('Expires')

# Other XRD tags
canonicalID_tag = mkXRDTag('CanonicalID')

def getYadisXRD(xrd_tree):
    """Return the XRD element that should contain the Yadis services"""
    xrd = None

    # for the side-effect of assigning the last one in the list to the
    # xrd variable
    for xrd in xrd_tree.findall(xrd_tag):
        pass

    # There were no elements found, or else xrd would be set to the
    # last one
    if xrd is None:
        raise XRDSError('No XRD present in tree')

    return xrd

def getCanonicalID(iname, xrd_tree):
    """Return the CanonicalID from this XRDS document.

    @param iname: the XRI being resolved.
    @type iname: unicode

    @param xrd_tree: The XRDS output from the resolver.
    @type xrd_tree: ElementTree

    @returns: The XRI CanonicalID or None.
    @returntype: unicode or None
    """
    xrd_list = xrd_tree.findall(xrd_tag)
    xrd_list.reverse()

    try:
        canonicalID = xri.XRI(xrd_list[0].findall(canonicalID_tag)[0].text)
    except IndexError:
        return None

    childID = canonicalID.lower()

    for xrd in xrd_list[1:]:
        # XXX: can't use rsplit until we require python >= 2.4.
        parent_sought = childID[:childID.rindex('!')]
        parent = xri.XRI(xrd.findtext(canonicalID_tag))
        if parent_sought != parent.lower():
            raise XRDSFraud("%r can not come from %s" % (childID, parent))

        childID = parent_sought

    root = xri.rootAuthority(iname)
    if not xri.providerIsAuthoritative(root, childID):
        raise XRDSFraud("%r can not come from root %r" % (childID, root))

    return canonicalID

@functools.total_ordering
class _Max(object):
    """
    Value that compares greater than any other value.

    Should only be used as a singleton. Implemented for use as a
    priority value for when a priority is not specified.
    """
    def __lt__(self, other):
        return isinstance(other, self.__class__)

    def __eq__(self, other):
        return isinstance(other, self.__class__)

Max = _Max()

def getPriorityStrict(element):
    """Get the priority of this element.

    Raises ValueError if the value of the priority is invalid. If no
    priority is specified, it returns a value that compares greater
    than any other value.
    """
    prio_str = element.get('priority')
    if prio_str is not None:
        prio_val = int(prio_str)
        if prio_val >= 0:
            return prio_val
        else:
            raise ValueError('Priority values must be non-negative integers')

    # Any errors in parsing the priority fall through to here
    return Max

def getPriority(element):
    """Get the priority of this element

    Returns Max if no priority is specified or the priority value is invalid.
    """
    try:
        return getPriorityStrict(element)
    except ValueError:
        return Max

def prioSort(elements):
    """Sort a list of elements that have priority attributes"""
    # Randomize the services before sorting so that equal priority
    # elements are load-balanced.
    random.shuffle(elements)

    sorted_elems = sorted(elements, key=getPriority)
    return sorted_elems

def iterServices(xrd_tree):
    """Return an iterable over the Service elements in the Yadis XRD

    sorted by priority"""
    xrd = getYadisXRD(xrd_tree)
    return prioSort(xrd.findall(service_tag))

def getURI(service_element):
    """Given a Service element, return content of its URI tag or
    None if absent
    """
    uri_element = service_element.find(uri_tag)
    return uri_element.text if uri_element is not None else None

def getTypeURIs(service_element):
    """Given a Service element, return a list of the contents of all
    Type tags"""
    return [type_element.text for type_element
            in service_element.findall(type_tag)]

def matches_types(element, types):
    '''
    Checks if the service element supports any of the types.
    '''
    return not types or set(types).intersection(set(getTypeURIs(element)))

def get_elements(data, types):
    '''
    Parses an XRDS document and returns a list of service elements matching
    types.
    '''
    elements = iterServices(parseXRDS(data))
    return [e for e in elements if matches_types(e, types) and getURI(e) is not None]
<|endoftext|>


<commit_before>from django.contrib import admin
from order.models import *
from forms import *
from django.utils.translation import ugettext_lazy as _
import copy
from django.core.mail import send_mail
from django.core.mail import EmailMultiAlternatives

# Register your models here.

class SuperOrder(admin.ModelAdmin):
    # Shows the requests according to the user permission
    def get_queryset(self, request):
        qs = super(SuperOrder, self).get_queryset(request)
        # If super-user, show all
        if request.user.is_superuser:
            return qs
        return qs.filter(requester=request.user)

    # If not superuser, do not show the status field
    def get_readonly_fields(self, request, obj=None):
        ro_fields = super(SuperOrder, self).get_readonly_fields(request, obj)
        if not request.user.is_superuser:
            ro_fields = list(ro_fields) + ['status']
        return ro_fields

    # If not superuser, do not show the requester field
    def get_fieldsets(self, request, obj=None):
        fieldsets = copy.deepcopy(super(SuperOrder, self).get_fieldsets(request, obj))
        if request.user.is_superuser:
            fieldsets[0][1]['fields'].append('requester')
        return fieldsets

    # If not superuser, set the requester as the current user and status as Open
    def save_model(self, request, obj, form, change):
        if not request.user.is_superuser:
            if not change:
                obj.requester = Investigator.objects.get(user=request.user)
                obj.status = 'o'
        obj.save()

        if not change:
            subject, from_email, to = 'NIRA - Novo pedido', 'neuromatematica@gmail.com', 'admin-nira@numec.prp.usp.br'
            text_content = 'Um novo pedido foi gerado no sistema NIRA.'
            html_content = '
Um novo pedido foi gerado no sistema NIRA. ' \
                           'Ver pedidos.
'
            msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
            msg.attach_alternative(html_content, "text/html")
            msg.send()

class OrderAdmin(SuperOrder):
    list_display = ('id_order', 'date_modified', 'order_date', 'status', 'type_of_order', 'requester')
    list_per_page = 15
    list_filter = ('status', 'type_of_order', 'requester',)

admin.site.register(Order, OrderAdmin)

class EventAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Event Info'), {
            'fields': ('name', 'url', 'value', 'start_date', 'end_date', 'invitation')
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'name', 'order_date')

    list_display_links = ('order_number', 'status', 'name', 'order_date')

admin.site.register(Event, EventAdmin)

class HardwareSoftwareAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Hardware or Software Info'), {
            'fields': ('type', 'quantity')
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'type', 'order_date')

    list_display_links = ('order_number', 'status', 'type', 'order_date')

admin.site.register(HardwareSoftware, HardwareSoftwareAdmin)

class ServiceAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Service Info'), {
            'fields': ('type',)
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'type', 'order_date')

    list_display_links = ('order_number', 'status', 'type', 'order_date')

admin.site.register(Service, ServiceAdmin)

class TicketAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Ticket Info'), {
            'fields': ('type_transportation', 'type', 'origin', 'destination', 'outbound_date',
                       'outbound_date_preference', 'inbound_date', 'inbound_date_preference', 'note')
        }),
        (_('Purpose of the trip'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'origin', 'destination', 'outbound_date', 'inbound_date',
                    'order_date')

    list_display_links = ('order_number', 'status', 'origin', 'destination', 'outbound_date', 'inbound_date',
                          'order_date')

    form = TicketAdminForm

admin.site.register(Ticket, TicketAdmin)

class DailyStipendAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Daily stipend Info'), {
            'fields': ('origin', 'destination', 'departure', 'arrival')
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'origin', 'destination', 'order_date')

    list_display_links = ('order_number', 'status', 'origin', 'destination', 'order_date')

admin.site.register(DailyStipend, DailyStipendAdmin)

class ReimbursementAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Reimbursement Info'), {
            'fields': ('why',)
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'why', 'order_date')

    list_display_links = ('order_number', 'status', 'why', 'order_date')

admin.site.register(Reimbursement, ReimbursementAdmin)<commit_msg>Send email after the creation of a new order<commit_after>from django.contrib import admin
from order.models import *
from forms import *
from django.utils.translation import ugettext_lazy as _
import copy
from django.core.mail import send_mail
from django.core.mail import EmailMultiAlternatives

# Register your models here.

class SuperOrder(admin.ModelAdmin):
    # Shows the requests according to the user permission
    def get_queryset(self, request):
        qs = super(SuperOrder, self).get_queryset(request)
        # If super-user, show all
        if request.user.is_superuser:
            return qs
        return qs.filter(requester=request.user)

    # If not superuser, do not show the status field
    def get_readonly_fields(self, request, obj=None):
        ro_fields = super(SuperOrder, self).get_readonly_fields(request, obj)
        if not request.user.is_superuser:
            ro_fields = list(ro_fields) + ['status']
        return ro_fields

    # If not superuser, do not show the requester field
    def get_fieldsets(self, request, obj=None):
        fieldsets = copy.deepcopy(super(SuperOrder, self).get_fieldsets(request, obj))
        if request.user.is_superuser:
            fieldsets[0][1]['fields'].append('requester')
        return fieldsets

    # If not superuser, set the requester as the current user and status as Open
    def save_model(self, request, obj, form, change):
        if not request.user.is_superuser:
            if not change:
                obj.requester = Investigator.objects.get(user=request.user)
                obj.status = 'o'
        obj.save()

        if not change:
            subject, from_email, to = 'NIRA - Novo pedido', 'neuromatematica@gmail.com', 'admin-nira@numec.prp.usp.br'
            text_content = 'Um novo pedido foi gerado no sistema NIRA.'
            html_content = '
Um novo pedido foi gerado no sistema NIRA. ' \
                           'Ver pedidos.
'
            msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
            msg.attach_alternative(html_content, "text/html")
            msg.send()

class OrderAdmin(SuperOrder):
    list_display = ('id_order', 'date_modified', 'order_date', 'status', 'type_of_order', 'requester')
    list_per_page = 15
    list_filter = ('status', 'type_of_order', 'requester',)

admin.site.register(Order, OrderAdmin)

class EventAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Event Info'), {
            'fields': ('name', 'url', 'value', 'start_date', 'end_date', 'invitation')
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'name', 'order_date')

    list_display_links = ('order_number', 'status', 'name', 'order_date')

admin.site.register(Event, EventAdmin)

class HardwareSoftwareAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Hardware or Software Info'), {
            'fields': ('type', 'quantity')
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'type', 'order_date')

    list_display_links = ('order_number', 'status', 'type', 'order_date')

admin.site.register(HardwareSoftware, HardwareSoftwareAdmin)

class ServiceAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Service Info'), {
            'fields': ('type',)
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'type', 'order_date')

    list_display_links = ('order_number', 'status', 'type', 'order_date')

admin.site.register(Service, ServiceAdmin)

class TicketAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Ticket Info'), {
            'fields': ('type_transportation', 'type', 'origin', 'destination', 'outbound_date',
                       'outbound_date_preference', 'inbound_date', 'inbound_date_preference', 'note')
        }),
        (_('Purpose of the trip'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'origin', 'destination', 'outbound_date', 'inbound_date',
                    'order_date')

    list_display_links = ('order_number', 'status', 'origin', 'destination', 'outbound_date', 'inbound_date',
                          'order_date')

    form = TicketAdminForm

admin.site.register(Ticket, TicketAdmin)

class DailyStipendAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Daily stipend Info'), {
            'fields': ('origin', 'destination', 'departure', 'arrival')
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'origin', 'destination', 'order_date')

    list_display_links = ('order_number', 'status', 'origin', 'destination', 'order_date')

admin.site.register(DailyStipend, DailyStipendAdmin)

class ReimbursementAdmin(SuperOrder):

    fieldsets = (
        (None, {
            'fields': ['status']
        }),
        (_('Reimbursement Info'), {
            'fields': ('why',)
        }),
        (_('Purpose'), {
            'fields': ('justification',)
        }),
    )

    list_display = ('order_number', 'status', 'requester', 'why', 'order_date')

    list_display_links = ('order_number', 'status', 'why', 'order_date')

admin.site.register(Reimbursement, ReimbursementAdmin)<|endoftext|>


<commit_before># System
"""Originality Checking."""
import logging
import functools
from threading import Lock

# Third Party
from scipy.stats.stats import pearsonr
import numpy as np
import pandas as pd

# First Party
import common

lock = Lock()

@functools.lru_cache(maxsize=2048)
def get_submission(db_manager, filemanager, submission_id):
    """Gets the submission file from S3

    Parameters:
    -----------
    db_manager: DatabaseManager
        DB data access object that has read and write functions to NoSQL DB

    filemanager: FileManager
        S3 Bucket data access object for querying competition datasets

    submission_id : string
        The ID of the submission

    Returns:
    --------
    submission : ndarray
        2d array of the submission probabilities. First column is sorted by ID
        and second column is sorted by probability.
    """
    if not submission_id:
        return None

    s3_filename, _ = common.get_filename(db_manager.postgres_db, submission_id)
    try:

        local_files = filemanager.download([s3_filename])
        if len(local_files) != 1:
            logging.getLogger().info("Error looking for submission {}, found files".format(submission_id, local_files))
            return None

        local_file = local_files[0]
    except Exception as e:
        logging.getLogger().info("Could not get submission {} at S3 path {}".format(submission_id, s3_filename))
        return None

    df = pd.read_csv(local_file)

    df.sort_values("id", inplace=True)
    df = df["probability"]
    a = df.as_matrix()
    a_sorted = np.sort(a)

    # make a two-column numpy array: first column is sorted by id; second
    # column is sorted by probability
    a = a.reshape(-1, 1)
    a_sorted = a_sorted.reshape(-1, 1)
    a = np.hstack((a, a_sorted))

    return a

def original(submission1, submission2, threshold=0.05):
    """Determines if two submissions are original

    Paramters:
    ----------
    submission1, submission2 : 1-D ndarrays
        Submission arrays that will be used in the Kolmogorov-Smirnov statistic
    threshold : float, optional, default: 0.05
        threshold in which the originality_score must be greater than to be "original"

    Returns:
    --------
    original : bool
        boolean value that indicates if a submission is original
    """
    score = originality_score(submission1, submission2)
    return score > threshold

# this function is taken from scipy (ks_2samp) and modified and so falls
# under their BSD license
def originality_score(data1, data2):
    """
    Computes the Kolmogorov-Smirnov statistic on 2 samples.

    This is a two-sided test for the null hypothesis that 2 independent samples
    are drawn from the same continuous distribution.

    Warning: data1 and data2 are assumed sorted in ascending order.

    Parameters
    ----------
    data1, data2 : ndarray
        Two arrays of sample observations assumed to be drawn from a
        continuous distribution. Arrays must be of the same size. data1 and
        data2 are assumed sorted in ascending order.

    Returns
    -------
    statistic : float
        KS statistic
    """

    # data1 and date2 are assumed sorted in ascending order
    n1 = data1.shape[0]
    n2 = data2.shape[0]
    if n1 != n2:
        raise ValueError("`data1` and `data2` must have the same length")

    # the following commented out line is slower than the two after it
    # cdf1 = np.searchsorted(data1, data_all, side='right') / (1.0*n1)
    cdf1 = np.searchsorted(data1, data2, side='right')
    cdf1 = np.concatenate((np.arange(n1) + 1, cdf1)) / (1.0*n1)

    # the following commented out line is slower than the two after it
    # cdf2 = np.searchsorted(data2, data_all, side='right') / (1.0*n2)
    cdf2 = np.searchsorted(data2, data1, side='right')
    cdf2 = np.concatenate((cdf2, np.arange(n1) + 1)) / (1.0*n2)

    d = np.max(np.absolute(cdf1 - cdf2))

    return d

def is_almost_unique(submission_data, submission, db_manager, filemanager, is_exact_dupe_thresh, is_similar_thresh, max_similar_models):
    """Determines how similar/exact a submission is to all other submission for the competition round

    Paramters:
    ----------
    submission_data : dictionary
        Submission metadata containing the submission_id and the user associated to the submission

    submission : ndarray
        Submission data that contains the probabilities for the competition
        data. The array is 2d. First column is sorted by ID and second column
        is sorted by probability.

    db_manager : DatabaseManager
        DB data access object that has read and write functions to NoSQL DB

    filemanager : FileManager
        S3 Bucket data access object for querying competition datasets

    is_exact_dupe_thresh :
        Threshold for determining if a submission is and exact duplicate to another submission

    is_similar_thresh :
        Similarity threshold that determines if a submission is too similar and counts against the submissions originality

    max_similar_models :
        The max number of models that a submission is allow to be similar to

    Returns:
    --------
    bool
        Whether the submission data is considered to be original or not
    """
    num_similar_models = 0
    is_original = True
    similar_models = []
    is_not_a_constant = np.std(submission[:, 0]) > 0

    date_created = db_manager.get_date_created(submission_data['submission_id'])

    get_others = db_manager.get_everyone_elses_recent_submssions

    # first test correlations
    for user_sub in get_others(submission_data['competition_id'],
                               submission_data['user'], date_created):

        with lock:
            other_submission = get_submission(db_manager, filemanager,
                                              user_sub["submission_id"])
        if other_submission is None:
            continue

        if is_not_a_constant and np.std(other_submission[:, 0]) > 0:
            correlation = pearsonr(submission[:, 0], other_submission[:, 0])[0]
            if np.abs(correlation) > 0.95:
                msg = "Found a highly correlated submission {} with score {}"
                msg = msg.format(user_sub["submission_id"])
                logging.getLogger().info(msg, correlation)
                is_original = False
                break

    # only run KS test if correlation test passes
    if is_original:
        for user_sub in get_others(submission_data['competition_id'],
                                   submission_data['user'], date_created):

            with lock:
                other_submission = get_submission(db_manager, filemanager,
                                                  user_sub["submission_id"])
            if other_submission is None:
                continue

            score = originality_score(submission[:, 1], other_submission[:, 1])
            if score < is_exact_dupe_thresh:
                logging.getLogger().info("Found a duplicate submission {} with score {}".format(user_sub["submission_id"], score))
                is_original = False
                break
            if score <= is_similar_thresh:
                num_similar_models += 1
                similar_models.append(user_sub["submission_id"])
                if num_similar_models >= max_similar_models:
                    logging.getLogger().info("Found too many similar models. Similar models were {}".format(similar_models))
                    is_original = False
                    break

    return is_original

def submission_originality(submission_data, db_manager, filemanager):
    """Pulls submission data from DB and determines the originality score and will update the submissions originality score

    This checks a few things
        1. If the current submission is similar to the previous submission, we give it the same originality score
        2. Otherwise, we check that it is sufficently unique. To check this we see if it is A. Almost identitical to
        any other submission or B. Very similar to a handful of other models.

    Parameters:
    -----------
    submission_data : dictionary
        Metadata about the submission pulled from the queue

    db_manager : DatabaseManager
        DB data access object that has read and write functions to DB

    filemanager : FileManager
        S3 Bucket data access object for querying competition datasets
    """
    query = "SELECT round_id, user_id FROM submissions WHERE id='{}'".format(submission_data["submission_id"])
    cursor = db_manager.postgres_db.cursor()
    cursor.execute(query)
    results = cursor.fetchone()
    cursor.close()
    submission_data["round_id"] = results[0]
    submission_data["user_id"] = results[1]
    logging.getLogger().info("Scoring user_id {} submission_id {}".format(submission_data["user_id"], submission_data['submission_id']))

    with lock:
        submission = get_submission(db_manager, filemanager, submission_data['submission_id'])

    if submission is None:
        logging.getLogger().info("Couldn't find submission {}".format(submission_data['submission_id']))
        return

    is_exact_dupe_thresh = 0.005
    is_similar_thresh = 0.03
    max_similar_models = 1

    is_original = is_almost_unique(submission_data, submission, db_manager, filemanager, is_exact_dupe_thresh, is_similar_thresh, max_similar_models)
    db_manager.write_originality(submission_data['submission_id'], is_original)
<commit_msg>Add assertions on columns since they are referenced<commit_after># System
"""Originality Checking."""
import logging
import functools
from threading import Lock

# Third Party
from scipy.stats.stats import pearsonr
import numpy as np
import pandas as pd

# First Party
import common

lock = Lock()

@functools.lru_cache(maxsize=2048)
def get_submission(db_manager, filemanager, submission_id):
    """Gets the submission file from S3

    Parameters:
    -----------
    db_manager: DatabaseManager
        DB data access object that has read and write functions to NoSQL DB

    filemanager: FileManager
        S3 Bucket data access object for querying competition datasets

    submission_id : string
        The ID of the submission

    Returns:
    --------
    submission : ndarray
        2d array of the submission probabilities. First column is sorted by ID
        and second column is sorted by probability.
    """
    if not submission_id:
        return None

    s3_filename, _ = common.get_filename(db_manager.postgres_db, submission_id)
    try:

        local_files = filemanager.download([s3_filename])
        if len(local_files) != 1:
            logging.getLogger().info("Error looking for submission {}, found files".format(submission_id, local_files))
            return None

        local_file = local_files[0]
    except Exception as e:
        logging.getLogger().info("Could not get submission {} at S3 path {}".format(submission_id, s3_filename))
        return None

    df = pd.read_csv(local_file)
    assert "id" in df.columns, "No id column in submission {}".format(s3_filename)
    assert "probability" in df.column, "No probability column in submission {}".format(s3_filename)

    df.sort_values("id", inplace=True)
    df = df["probability"]
    a = df.as_matrix()
    a_sorted = np.sort(a)

    # make a two-column numpy array: first column is sorted by id; second
    # column is sorted by probability
    a = a.reshape(-1, 1)
    a_sorted = a_sorted.reshape(-1, 1)
    a = np.hstack((a, a_sorted))

    return a

def original(submission1, submission2, threshold=0.05):
    """Determines if two submissions are original

    Paramters:
    ----------
    submission1, submission2 : 1-D ndarrays
        Submission arrays that will be used in the Kolmogorov-Smirnov statistic
    threshold : float, optional, default: 0.05
        threshold in which the originality_score must be greater than to be "original"

    Returns:
    --------
    original : bool
        boolean value that indicates if a submission is original
    """
    score = originality_score(submission1, submission2)
    return score > threshold

# this function is taken from scipy (ks_2samp) and modified and so falls
# under their BSD license
def originality_score(data1, data2):
    """
    Computes the Kolmogorov-Smirnov statistic on 2 samples.

    This is a two-sided test for the null hypothesis that 2 independent samples
    are drawn from the same continuous distribution.

    Warning: data1 and data2 are assumed sorted in ascending order.

    Parameters
    ----------
    data1, data2 : ndarray
        Two arrays of sample observations assumed to be drawn from a
        continuous distribution. Arrays must be of the same size. data1 and
        data2 are assumed sorted in ascending order.

    Returns
    -------
    statistic : float
        KS statistic
    """

    # data1 and date2 are assumed sorted in ascending order
    n1 = data1.shape[0]
    n2 = data2.shape[0]
    if n1 != n2:
        raise ValueError("`data1` and `data2` must have the same length")

    # the following commented out line is slower than the two after it
    # cdf1 = np.searchsorted(data1, data_all, side='right') / (1.0*n1)
    cdf1 = np.searchsorted(data1, data2, side='right')
    cdf1 = np.concatenate((np.arange(n1) + 1, cdf1)) / (1.0*n1)

    # the following commented out line is slower than the two after it
    # cdf2 = np.searchsorted(data2, data_all, side='right') / (1.0*n2)
    cdf2 = np.searchsorted(data2, data1, side='right')
    cdf2 = np.concatenate((cdf2, np.arange(n1) + 1)) / (1.0*n2)

    d = np.max(np.absolute(cdf1 - cdf2))

    return d

def is_almost_unique(submission_data, submission, db_manager, filemanager, is_exact_dupe_thresh, is_similar_thresh, max_similar_models):
    """Determines how similar/exact a submission is to all other submission for the competition round

    Paramters:
    ----------
    submission_data : dictionary
        Submission metadata containing the submission_id and the user associated to the submission

    submission : ndarray
        Submission data that contains the probabilities for the competition
        data. The array is 2d. First column is sorted by ID and second column
        is sorted by probability.

    db_manager : DatabaseManager
        DB data access object that has read and write functions to NoSQL DB

    filemanager : FileManager
        S3 Bucket data access object for querying competition datasets

    is_exact_dupe_thresh :
        Threshold for determining if a submission is and exact duplicate to another submission

    is_similar_thresh :
        Similarity threshold that determines if a submission is too similar and counts against the submissions originality

    max_similar_models :
        The max number of models that a submission is allow to be similar to

    Returns:
    --------
    bool
        Whether the submission data is considered to be original or not
    """
    num_similar_models = 0
    is_original = True
    similar_models = []
    is_not_a_constant = np.std(submission[:, 0]) > 0

    date_created = db_manager.get_date_created(submission_data['submission_id'])

    get_others = db_manager.get_everyone_elses_recent_submssions

    # first test correlations
    for user_sub in get_others(submission_data['competition_id'],
                               submission_data['user'], date_created):

        with lock:
            other_submission = get_submission(db_manager, filemanager,
                                              user_sub["submission_id"])
        if other_submission is None:
            continue

        if is_not_a_constant and np.std(other_submission[:, 0]) > 0:
            correlation = pearsonr(submission[:, 0], other_submission[:, 0])[0]
            if np.abs(correlation) > 0.95:
                msg = "Found a highly correlated submission {} with score {}"
                msg = msg.format(user_sub["submission_id"])
                logging.getLogger().info(msg, correlation)
                is_original = False
                break

    # only run KS test if correlation test passes
    if is_original:
        for user_sub in get_others(submission_data['competition_id'],
                                   submission_data['user'], date_created):

            with lock:
                other_submission = get_submission(db_manager, filemanager,
                                                  user_sub["submission_id"])
            if other_submission is None:
                continue

            score = originality_score(submission[:, 1], other_submission[:, 1])
            if score < is_exact_dupe_thresh:
                logging.getLogger().info("Found a duplicate submission {} with score {}".format(user_sub["submission_id"], score))
                is_original = False
                break
            if score <= is_similar_thresh:
                num_similar_models += 1
                similar_models.append(user_sub["submission_id"])
                if num_similar_models >= max_similar_models:
                    logging.getLogger().info("Found too many similar models. Similar models were {}".format(similar_models))
                    is_original = False
                    break

    return is_original

def submission_originality(submission_data, db_manager, filemanager):
    """Pulls submission data from DB and determines the originality score and will update the submissions originality score

    This checks a few things
        1. If the current submission is similar to the previous submission, we give it the same originality score
        2. Otherwise, we check that it is sufficently unique. To check this we see if it is A. Almost identitical to
        any other submission or B. Very similar to a handful of other models.

    Parameters:
    -----------
    submission_data : dictionary
        Metadata about the submission pulled from the queue

    db_manager : DatabaseManager
        DB data access object that has read and write functions to DB

    filemanager : FileManager
        S3 Bucket data access object for querying competition datasets
    """
    query = "SELECT round_id, user_id FROM submissions WHERE id='{}'".format(submission_data["submission_id"])
    cursor = db_manager.postgres_db.cursor()
    cursor.execute(query)
    results = cursor.fetchone()
    cursor.close()
    submission_data["round_id"] = results[0]
    submission_data["user_id"] = results[1]
    logging.getLogger().info("Scoring user_id {} submission_id {}".format(submission_data["user_id"], submission_data['submission_id']))

    with lock:
        submission = get_submission(db_manager, filemanager, submission_data['submission_id'])

    if submission is None:
        logging.getLogger().info("Couldn't find submission {}".format(submission_data['submission_id']))
        return

    is_exact_dupe_thresh = 0.005
    is_similar_thresh = 0.03
    max_similar_models = 1

    is_original = is_almost_unique(submission_data, submission, db_manager, filemanager, is_exact_dupe_thresh, is_similar_thresh, max_similar_models)
    db_manager.write_originality(submission_data['submission_id'], is_original)
<|endoftext|>