models.py 5.81 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
"""
Model to store a microsite in the database.

The object is stored as a json representation of the python dict
that would have been used in the settings.

"""
import collections

from django.db import models
from django.dispatch import receiver
from django.db.models.signals import pre_save, pre_delete
from django.db.models.base import ObjectDoesNotExist
from django.contrib.sites.models import Site

from jsonfield.fields import JSONField
from model_utils.models import TimeStampedModel
from simple_history.models import HistoricalRecords


class Microsite(models.Model):
    """
    This is where the information about the microsite gets stored to the db.
    To achieve the maximum flexibility, most of the fields are stored inside
    a json field.

    Notes:
        - The key field was required for the dict definition at the settings, and it
        is used in some of the microsite_configuration methods.
        - The site field is django site.
        - The values field must be validated on save to prevent the platform from crashing
        badly in the case the string is not able to be loaded as json.
    """
    site = models.OneToOneField(Site, related_name='microsite')
    key = models.CharField(max_length=63, db_index=True, unique=True)
    values = JSONField(null=False, blank=True, load_kwargs={'object_pairs_hook': collections.OrderedDict})

    def __unicode__(self):
        return self.key

    def get_organizations(self):
        """
        Helper method to return a list of organizations associated with our particular Microsite
        """
        return MicrositeOrganizationMapping.get_organizations_for_microsite_by_pk(self.id)  # pylint: disable=no-member

    @classmethod
    def get_microsite_for_domain(cls, domain):
        """
        Returns the microsite associated with this domain. Note that we always convert to lowercase, or
        None if no match
        """

        # remove any port number from the hostname
        domain = domain.split(':')[0]
        microsites = cls.objects.filter(site__domain__iexact=domain)

        return microsites[0] if microsites else None


class MicrositeHistory(TimeStampedModel):
    """
    This is an archive table for Microsites model, so that we can maintain a history of changes. Note that the
    key field is no longer unique
    """
66 67
    site = models.ForeignKey(Site, related_name='microsite_history')
    key = models.CharField(max_length=63, db_index=True)
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    values = JSONField(null=False, blank=True, load_kwargs={'object_pairs_hook': collections.OrderedDict})

    def __unicode__(self):
        return self.key

    class Meta(object):
        """ Meta class for this Django model """
        verbose_name_plural = "Microsite histories"


def _make_archive_copy(instance):
    """
    Helper method to make a copy of a Microsite into the history table
    """
    archive_object = MicrositeHistory(
        key=instance.key,
        site=instance.site,
        values=instance.values,
    )
    archive_object.save()


@receiver(pre_delete, sender=Microsite)
def on_microsite_deleted(sender, instance, **kwargs):  # pylint: disable=unused-argument
    """
    Archive the exam attempt when the item is about to be deleted
    Make a clone and populate in the History table
    """
    _make_archive_copy(instance)


@receiver(pre_save, sender=Microsite)
def on_microsite_updated(sender, instance, **kwargs):  # pylint: disable=unused-argument
    """
    Archive the microsite on an update operation
    """

    if instance.id:
        # on an update case, get the original and archive it
        original = Microsite.objects.get(id=instance.id)
        _make_archive_copy(original)


class MicrositeOrganizationMapping(models.Model):
    """
    Mapping of Organization to which Microsite it belongs
    """

    organization = models.CharField(max_length=63, db_index=True, unique=True)
    microsite = models.ForeignKey(Microsite, db_index=True)

    # for archiving
    history = HistoricalRecords()

    def __unicode__(self):
        """String conversion"""
        return u'{microsite_key}: {organization}'.format(
            microsite_key=self.microsite.key,
            organization=self.organization
        )

    @classmethod
    def get_organizations_for_microsite_by_pk(cls, microsite_pk):
        """
        Returns a list of organizations associated with the microsite key, returned as a set
        """
        return cls.objects.filter(microsite_id=microsite_pk).values_list('organization', flat=True)

    @classmethod
    def get_microsite_for_organization(cls, org):
        """
        Returns the microsite object for a given organization based on the table mapping, None if
        no mapping exists
        """

        try:
            item = cls.objects.select_related('microsite').get(organization=org)
            return item.microsite
        except ObjectDoesNotExist:
            return None


class MicrositeTemplate(models.Model):
    """
    A HTML template that a microsite can use
    """

    microsite = models.ForeignKey(Microsite, db_index=True)
    template_uri = models.CharField(max_length=255, db_index=True)
    template = models.TextField()

    # for archiving
    history = HistoricalRecords()

    def __unicode__(self):
        """String conversion"""
        return u'{microsite_key}: {template_uri}'.format(
            microsite_key=self.microsite.key,
            template_uri=self.template_uri
        )

    class Meta(object):
        """ Meta class for this Django model """
        unique_together = (('microsite', 'template_uri'),)

    @classmethod
    def get_template_for_microsite(cls, domain, template_uri):
        """
        Returns the template object for the microsite, None if not found
        """
        try:
            return cls.objects.get(microsite__site__domain=domain, template_uri=template_uri)
        except ObjectDoesNotExist:
            return None