Adding the declaration of the settings object to openedx.conf to be able to import it from a nicer location Resolving quality violations Merging dicts with the settings definition when they exist in the microsite configuration Using a cache to improve the perfomance of quering any dictionary in the microsite definition Ignoring the invalid-name pylint warning since the names must be kept thsi way to stay the same as the ones in django. Removing the default dict argument as per https://docs.python.org/2/tutorial/controlflow.html#default-argument-values Extracting the implementation of the microsite to a selectable backend. Leaving the function startup.enable_microsites for backwards compatibilityy Adding a database backend Using a cache to improve the perfomance of quering any dictionary in the microsite definition. Changed the database backend so that it extends the settings file backend and removed all the unnecessary methods. Using the backend provider for the get_dict function some tweeks and some initial unit tests Using getattr as a function insteal of calling the underlying __getattr__ directly Adding an ModelAdmin object for the microsite model in the django-admin panel refactor enable_microsites() consolidate/refactor some shared code add config to aws.py and add migration files fix tests Changes to get the backends to run after the refactor add archiving capabilities to microsites. Also make a few notes about performance improvements to make fix tests Making the query to find if microsites exist in the database faster add ORG to microsite mapping tables and some performance improvements allow for Mako templates to be pulled from the database fix tests For the database template backend the uri of the template does not use the filesystem relative path Fixing pylint violations Added caching of the templates stored in the database Fixing pylint errors fix pylint Clearing the cache on model save Fixing pylint errors rebased and added test coverage rebased cdodge/microsite-improvements branch with master and added test coverage added missing migration fix quality violations add more test coverage mattdrayer: Add microsite_configuration to cms.INSTALLED_APPS added microsite settings to cms/envs/test.py run session cookie tests only in LMS fixed broken tests putting middleware changes back Preventing the template_backend to be called on requests which have no microsite changes to address feedback from mjfrey changed BaseMicrositeBackend to AbstractBaseMicrositeBackend changes after feedback from mattdrayer fixed broken tests and quality violations Allowing the backend to handle the enable_pre_startup routine Typos and docstrings Adressing feedback Fixing python tests add comment to explain why we need enable_microsites_pre_startup()
182 lines
5.8 KiB
Python
182 lines
5.8 KiB
Python
"""
|
|
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
|
|
"""
|
|
site = models.OneToOneField(Site, related_name='microsite_history')
|
|
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
|
|
|
|
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
|