Languages

Previous versions

1.2
1.1

Source code for openPLM.plmapp.models.link

from django.utils import timezone
import kjbuckets


from django.core.exceptions import ValidationError
from django.db import models, IntegrityError
from django.db.models.query import QuerySet
from django.contrib.auth.models import User

from openPLM.plmapp.utils.units import UNITS, DEFAULT_UNIT
from openPLM.plmapp.utils import level_to_sign_str

from .lifecycle import State
from .plmobject import PLMObject
from .part import Part
from .document import Document

[docs]class LinkQuerySet(QuerySet): """ QuerySet with utility methods to filter links alive at a given time."""
[docs] def now(self): """ Filters links: keeps only alive links (end_time is null). """ return self.filter(end_time__isnull=True)
[docs] def at(self, time): """ Filters links: keeps alive links at time *time*. :param time: a :class:`~datetime.datetime` or None """ if time is None: return self.now() return self.filter(ctime__lte=time).exclude(end_time__isnull=False, end_time__lt=time)
[docs] def end(self): """ Ends all alive links: sets theur :attr:`end_time` to the current time and saves them if there :attr:`end_time` are not already set. """ return self.now().update(end_time=timezone.now())
[docs]class LinkManager(models.Manager): """Links manager, returns a :class:`LinkQuerySet`.""" use_for_related_fields = True def get_query_set(self): return LinkQuerySet(self.model)
[docs] def now(self): """ Shorcut for ``self.get_query_set().now()``. See :meth:`LinkQuerySet.now`. """ return self.get_query_set().now()
[docs] def at(self, time): """ Shorcut for ``self.get_query_set().at(time)``. See :meth:`LinkQuerySet.at`. """ return self.get_query_set().at(time)
[docs] def end(self): """ Shorcut for ``self.get_query_set().end()``. See :meth:`LinkQuerySet.end`. """ return self.get_query_set().end()
[docs]class CurrentLinkManager(LinkManager): """ Manager which returns alive links. """ def get_query_set(self): return LinkQuerySet(self.model).now()
class ChildQuerySet(QuerySet): def iterator(self): for obj in super(ChildQuerySet, self).iterator(): yield obj.get_child_object() class ChildManager(models.Manager): def get_query_set(self): return ChildQuerySet(self.model) class ParentModel(models.Model): _child_name = models.CharField(max_length=100, editable=False) class Meta: abstract = True def save(self, *args, **kwargs): self._child_name = self.get_child_name() super(ParentModel, self).save(*args, **kwargs) def get_child_name(self): if type(self) is self.get_parent_model(): return self._child_name return self.get_parent_link().related_query_name() def get_child_object(self): return getattr(self, self.get_child_name()) def get_parent_link(self): return self._meta.parents[self.get_parent_model()] def get_parent_model(self): raise NotImplementedError def get_parent_object(self): return getattr(self, self.get_parent_link().name) registered_PCLEs = []
[docs]class ParentChildLinkExtension(ParentModel): """ Extension of a :class:`.ParentChildLink` used to store additional data. This class is abstract, subclass must define the :meth:`.clone` method, add at least one field (or it would be useless) and may override :meth:`.get_visible_fields` or :meth:`.get_editable_fields`. .. seealso:: :ref:`bom_extensions` explains how to subclass this class. """ class Meta: app_label = "plmapp" #! link bound to the PCLE link = models.ForeignKey(ParentChildLink, related_name="%(class)s_link") objects = models.Manager() children = ChildManager() @classmethod
[docs] def get_visible_fields(cls): """ Returns the list of visible fieldnames. By default, returns an empty list. """ return []
@classmethod
[docs] def get_editable_fields(cls): """ Returns the list of editable fields. By default, returns :meth:`.get_visible_fields`. """ return list(cls.get_visible_fields())
@classmethod @classmethod
[docs] def apply_to(cls, parent): """ Returns True if this extension applies to *parent*. :param parent: part which will have a new child :type parent: :class:`.Part` (its most specific subclass). Returns True by default. """ return True
[docs] def clone(self, link, save=False, **data): """ Clone this extension. **Subclass must define its implementation.** and respect the following specification: :param link: the new cloned link, the cloned extension must be bound to it :type link: :class:`.ParentChildLink` :param save: True if the cloned extension must be saved, False (the default) if it must not be saved. :type save: boolean :param data: additional data that override the original values :return: the cloned extension """ raise NotImplementedError
def get_parent_model(self): return ParentChildLinkExtension
[docs] def to_dict(self): """ Returns a dictionary fieldnames -> value that can be safely passed as a kwargument to :meth:`.clone` and that is used to compare two extensions. """ d = {} for field in self._meta.get_all_field_names(): if field not in ("id", "link", "_child_name", 'parentchildlinkextension_ptr'): d[field] = getattr(self, field) return d
[docs]def register_PCLE(PCLE): """ Register *PCLE* so that openPLM can show its visible fields. :param PCLE: the registered PCLE :type PCLE: a subclass of :class:`.ParentChildLinkExtension`. """ registered_PCLEs.append(PCLE)
[docs]def get_PCLEs(parent): """ Returns the list of registered :class:`.ParentChildLinkExtension` that applied to *parent*. """ return [PCLE for PCLE in registered_PCLEs if PCLE.apply_to(parent)]
ROLE_NOTIFIED = "notified" ROLE_SIGN = "sign_" ROLE_OWNER = "owner" ROLE_SPONSOR = "sponsor" ROLES = [ROLE_OWNER, ROLE_NOTIFIED, ROLE_SPONSOR] for i in range(10): level = level_to_sign_str(i) ROLES.append(level) ROLE_READER = "reader" ROLES.append(ROLE_READER)
[docs]class PromotionApproval(Link): """ .. versionadded:: 1.2 Model to track a promotion approval :model attributes: .. attribute:: plmobject approved :class:`.PLMObject` .. attribute:: user :class:`.User` who approved the promotion .. attribute:: current_state current :class:`.State` of :attr:`plmobject` .. attribute:: next_state next :class:`.State` of :attr:`plmobject` when if will be promoted """ plmobject = models.ForeignKey(PLMObject, related_name="approvals") user = models.ForeignKey(User, related_name="approvals") current_state = models.ForeignKey(State, related_name="+") next_state = models.ForeignKey(State, related_name="+") class Meta: app_label = "plmapp" unique_together = ("plmobject", "user", "current_state", "next_state", "end_time")
class PartSet(Link): parts = models.ManyToManyField(Part, related_name="%(class)ss") class Meta: app_label = "plmapp" abstract = True def add_part(self, part): new_partset = self.__class__.objects.create() new_partset.parts.add(part, *self.parts.all()) self.end() return new_partset def remove_part(self, part): if self.parts.all().count() == 2: self.end() return None else: new_partset = self.__class__.objects.create() new_partset.parts.add(*[p for p in self.parts.all() if p.id != part.id]) self.end() return new_partset @classmethod def join(cls, part, part_or_set): if isinstance(part_or_set, cls): return part_or_set.add_part(part) partset = cls.get_partset(part_or_set) if partset is None: partset = cls.get_partset(part) if partset is not None: return partset.add_part(part_or_set) new_partset = cls.objects.create() new_partset.parts.add(part, part_or_set) return new_partset else: return partset.add_part(part) @classmethod def get_partset(cls, part): try: partset = getattr(part, "%ss" % cls.__name__.lower()).now().get() return partset except cls.DoesNotExist: return None @classmethod def get_related_parts(cls, parts): if not parts: return [] ps = cls.objects.now().filter(parts__in=parts).distinct() query = {"%ss__in" % cls.__name__.lower() : ps} return list(set(Part.objects.filter(**query).values_list("id", flat=True)))
[docs]class AlternatePartSet(PartSet): class Meta: app_label = "plmapp"