class Page(node): FORM_CHOICES = ( ("ContactForm", "Contact Form"), ("FosteringForm", "Fostering Form"), ) body = RichTextField(_("Body")) show_map = models.BooleanField(_("Show map"), default=False) form = models.CharField(max_length=100, blank=True, default="", choices=FORM_CHOICES) success_message = RichTextField(_("Success Message"), blank=True, default="") def getFormClass(self): # Locals from . import forms return getattr(forms, self.form) @property def success_url(self): return reverse("pages:page_success", kwargs={"slug": self.slug}) class Meta: verbose_name = _("Page") verbose_name_plural = _("Pages")
class Garden(statusMixin, models.Model): title = models.CharField(max_length=150) sub_title = models.CharField(max_length=250, blank=True) work_type = models.ManyToManyField(WorkType, blank=True) short_description = models.CharField(_("Short Description"), max_length=150, blank=True) description = RichTextField(_("Body")) slug = fields.AutoSlugField(populate_from="title") created = fields.CreationDateTimeField() modified = fields.ModificationDateTimeField() position = models.PositiveIntegerField(default=0) def __str__(self): return self.title class Meta: ordering = ("position",) verbose_name = _("Garden") verbose_name_plural = _("Gardens") @property def url(self): return reverse_lazy("gardens:GardenDetail", kwargs={"slug": self.slug}) @property def gardenphoto_hero_set(self): return self.gardenphoto_set.filter(is_hero=True)
class MaintenanceItem(statusMixin, models.Model): title = models.CharField(max_length=150) short_description = models.CharField(_("Short Description"), max_length=150, blank=True) description = RichTextField(_("Description")) slug = fields.AutoSlugField(populate_from="title") created = fields.CreationDateTimeField() modified = fields.ModificationDateTimeField() position = models.PositiveIntegerField(default=0) def __str__(self): return self.title class Meta: ordering = ("position",) verbose_name = _("Maintenance Item") verbose_name_plural = _("Maintenance Items") @property def url(self): if self.slug == "": self._meta.get_field("slug").create_slug(self, True) self.save() return reverse_lazy("gardens:MaintenanceItemDetailView", kwargs={"slug": self.slug}) @property def list_description(self): if self.short_description != "": return f"<p>{self.short_description}</p>" else: return self.description
class concept(_concept): """ This is an abstract class that all items that should behave like a 11179 Concept **must inherit from**. This model includes the definitions for many long and optional text fields and the self-referential ``superseded_by`` field. It is not possible to include this model in a ``ForeignKey`` or ``ManyToManyField``. """ short_name = models.CharField(max_length=100, blank=True) version = models.CharField(max_length=20, blank=True) synonyms = models.CharField(max_length=200, blank=True) references = RichTextField(blank=True) origin_URI = models.URLField( blank=True, help_text="If imported, the original location of the item" ) comments = RichTextField( help_text="Descriptive comments about the metadata item.", blank=True ) submitting_organisation = models.CharField(max_length=256, blank=True) responsible_organisation = models.CharField(max_length=256, blank=True) superseded_by = models.ForeignKey( 'self', related_name='supersedes', blank=True, null=True ) objects = ConceptManager() class Meta: abstract = True @property def help_name(self): return self._meta.model_name @property def item(self): """ Return self, because we already have the correct item. """ return self
class ClassificationScheme(concept): """ 3.3.9 (edition 2) Classification Scheme The descriptive information for an arrangement or division of objects into groups based on characteristics, which the objects have in common """ classificationStructure = RichTextField(blank=True)
class WorkType(statusMixin, models.Model): title = models.CharField(max_length=150) slug = fields.AutoSlugField(populate_from="title") description = RichTextField(_("Description")) position = models.PositiveIntegerField(default=0) class Meta: ordering = ("position",) verbose_name = _("Work Type") verbose_name_plural = _("Work Types") def __str__(self): return self.title
class ModuleList(node): module = models.CharField( _("Module"), max_length=200, ) body = RichTextField(_("Body"), null=True, blank=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._meta.get_field("module").choices = lazy( get_registered_list_views, list)() class Meta: verbose_name = _("Module List") verbose_name_plural = _("Module Lists") def get_view_object(self): module_name, class_name = self.module.rsplit(".", 1) view_class = getattr(importlib.import_module(module_name), class_name) return view_class()
class baseAristotleObject(TimeStampedModel): uuid = models.UUIDField(help_text=_( "Universally-unique Identifier. Uses UUID1 as this improves uniqueness and tracking between registries" ), unique=True, default=uuid.uuid1, editable=False, null=False) name = ShortTextField(help_text=_( "The primary name used for human identification purposes.")) definition = RichTextField( _('definition'), blank=True, default='', help_text=_( "Representation of a concept by a descriptive statement " "which serves to differentiate it from related concepts. (3.2.39)") ) objects = MetadataItemManager() class Meta: # So the url_name works for items we can't determine verbose_name = "item" # Can't be abstract as we need unique app wide IDs. abstract = True @property def aristotle_id(self): """ Id that can be displayed through infobox and graphql This is a property to allow for easy splitting of display id and internal id """ return self.pk def was_modified_very_recently(self): return self.modified >= ( timezone.now() - datetime.timedelta(seconds=VERY_RECENTLY_SECONDS)) def was_modified_recently(self): return self.modified >= timezone.now() - datetime.timedelta(days=1) was_modified_recently.admin_order_field = 'modified' # type: ignore was_modified_recently.boolean = True # type: ignore was_modified_recently.short_description = 'Modified recently?' # type: ignore def description_stub(self): from django.utils.html import strip_tags d = strip_tags(self.definition) if len(d) > 150: d = d[0:150] + "..." return d def __str__(self): return "{name}".format(name=self.name) # Defined so we can access it during templates. @classmethod def get_verbose_name(cls): return cls._meta.verbose_name.title() @classmethod def get_verbose_name_plural(cls): return cls._meta.verbose_name_plural.title() def can_edit(self, user): # This should always be overridden raise NotImplementedError # pragma: no cover def can_view(self, user): # This should always be overridden raise NotImplementedError # pragma: no cover @classmethod def meta(cls): """ The purpose of this function is to use the meta attribute in templates. example: "item.meta" :return: _meta attribute of class. """ return cls._meta
class _concept(baseAristotleObject): """ 9.1.2.1 - Concept class Concept is a class each instance of which models a concept (3.2.18), a unit of knowledge created by a unique combination of characteristics (3.2.14). A concept is independent of representation. This is the base concrete class that ``Status`` items attach to, and to which collection objects refer to. It is not marked abstract in the Django Meta class, and **must not be inherited from**. It has relatively few fields and is a convenience class to link with in relationships. """ objects = ConceptManager() template = "aristotle_mdr/concepts/managedContent.html" list_details_template = "aristotle_mdr/helpers/concept_list_details.html" workgroup = models.ForeignKey(Workgroup, related_name="items", null=True, blank=True) submitter = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="created_items", null=True, blank=True, help_text=_('This is the person who first created an item. Users can always see items they made.')) # We will query on these, so want them cached with the items themselves # To be usable these must be updated when statuses are changed _is_public = models.BooleanField(default=False) _is_locked = models.BooleanField(default=False) short_name = models.CharField(max_length=100, blank=True) version = models.CharField(max_length=20, blank=True) references = RichTextField(blank=True) origin_URI = models.URLField( blank=True, help_text="If imported, the original location of the item" ) comments = RichTextField( help_text=_("Descriptive comments about the metadata item (8.1.2.2.3.4)"), blank=True ) submitting_organisation = models.CharField(max_length=256, blank=True) responsible_organisation = models.CharField(max_length=256, blank=True) superseded_by = ConceptForeignKey( 'self', related_name='supersedes', blank=True, null=True ) tracker = FieldTracker() comparator = comparators.Comparator edit_page_excludes = None admin_page_excludes = None registerable = True class Meta: # So the url_name works for items we can't determine. verbose_name = "item" @property def non_cached_fields_changed(self): changed = self.tracker.changed() changed.pop('_is_public', False) changed.pop('_is_locked', False) return len(changed.keys()) > 0 @property def changed_fields(self): changed = self.tracker.changed() changed.pop('_is_public', False) changed.pop('_is_locked', False) return changed.keys() def can_edit(self, user): return _concept.objects.filter(pk=self.pk).editable(user).exists() def can_view(self, user): return _concept.objects.filter(pk=self.pk).visible(user).exists() @property def item(self): """ Performs a lookup using ``model_utils.managers.InheritanceManager`` to find the subclassed item. """ return _concept.objects.get_subclass(pk=self.pk) @property def concept(self): """ Returns the parent _concept that an item is built on. If the item type is _concept, return itself. """ return getattr(self, '_concept_ptr', self) @classmethod def get_autocomplete_name(self): return 'Autocomplete' + "".join( self._meta.verbose_name.title().split() ) @staticmethod def autocomplete_search_fields(self): return ("name__icontains",) def get_absolute_url(self): return url_slugify_concept(self) @property def registry_cascade_items(self): """ This returns the items that can be registered along with the this item. If a subclass of _concept defines this method, then when an instance of that class is registered using a cascading method then that instance, all instances returned by this method will all recieve the same registration status. Reimplementations of this MUST return iterables. """ return [] @property def is_registered(self): return self.statuses.count() > 0 @property def is_superseded(self): return all( STATES.superseded == status.state for status in self.statuses.all() ) and self.superseded_by @property def is_retired(self): return all( STATES.retired == status.state for status in self.statuses.all() ) and self.statuses.count() > 0 def check_is_public(self, when=timezone.now()): """ A concept is public if any registration authority has advanced it to a public state in that RA. """ statuses = self.statuses.all() statuses = self.current_statuses(qs=statuses, when=when) pub_state = True in [ s.state >= s.registrationAuthority.public_state for s in statuses ] q = Q() extra = False extra_q = fetch_aristotle_settings().get('EXTRA_CONCEPT_QUERYSETS', {}).get('public', None) if extra_q: for func in extra_q: q |= import_string(func)() extra = self.__class__.objects.filter(pk=self.pk).filter(q).exists() return pub_state or extra def is_public(self): return self._is_public is_public.boolean = True is_public.short_description = 'Public' def check_is_locked(self, when=timezone.now()): """ A concept is locked if any registration authority has advanced it to a locked state in that RA. """ statuses = self.statuses.all() statuses = self.current_statuses(qs=statuses, when=when) return True in [ s.state >= s.registrationAuthority.locked_state for s in statuses ] def is_locked(self): return self._is_locked is_locked.boolean = True is_locked.short_description = 'Locked' def recache_states(self): self._is_public = self.check_is_public() self._is_locked = self.check_is_locked() self.save() concept_visibility_updated.send(sender=self.__class__, concept=self) def current_statuses(self, qs=None, when=timezone.now()): if qs is None: qs = self.statuses.all() if hasattr(when, 'date'): when = when.date() states = status_filter(qs, when) states = states.order_by("registrationAuthority", "-registrationDate", "-created") from django.db import connection if connection.vendor == 'postgresql': states = states.distinct('registrationAuthority') else: current_ids = [] seen_ras = [] for s in states: ra = s.registrationAuthority if ra not in seen_ras: current_ids.append(s.pk) seen_ras.append(ra) # We hit again so we can return this as a queryset states = states.filter(pk__in=current_ids) return states def get_download_items(self): """ When downloading a concept, extra items can be included for download by overriding the ``get_download_items`` method on your item. By default this returns an empty list, but can be modified to include any number of items that inherit from ``_concept``. When overriding, each entry in the list must be a two item tuple, with the first entry being the python class of the item or items being included, and the second being the queryset of items to include. """ return []
class baseAristotleObject(TimeStampedModel): name = models.TextField(help_text=_( "The primary name used for human identification purposes.")) definition = RichTextField( _('definition'), help_text=_( "Representation of a concept by a descriptive statement " "which serves to differentiate it from related concepts. (3.2.39)") ) objects = InheritanceManager() class Meta: # So the url_name works for items we can't determine verbose_name = "item" # Can't be abstract as we need unique app wide IDs. abstract = True def was_modified_very_recently(self): return self.modified >= ( timezone.now() - datetime.timedelta(seconds=VERY_RECENTLY_SECONDS)) def was_modified_recently(self): return self.modified >= timezone.now() - datetime.timedelta(days=1) was_modified_recently.admin_order_field = 'modified' was_modified_recently.boolean = True was_modified_recently.short_description = 'Modified recently?' def description_stub(self): from django.utils.html import strip_tags d = strip_tags(self.definition) if len(d) > 150: d = d[0:150] + "..." return d def __str__(self): return "{name}".format(name=self.name).encode('utf-8') def __unicode__(self): return "{name}".format(name=self.name) # Defined so we can access it during templates. @classmethod def get_verbose_name(cls): return cls._meta.verbose_name.title() @classmethod def get_verbose_name_plural(cls): return cls._meta.verbose_name_plural.title() def can_edit(self, user): # This should always be overridden raise NotImplementedError # pragma: no cover def can_view(self, user): # This should always be overridden raise NotImplementedError # pragma: no cover @classmethod def meta(self): # I know what I'm doing, get out the way. return self._meta