source: main/branches/3D/openPLM/plmapp/controllers/part.py @ 662

Revision 662, 13.8 KB checked in by pcosquer, 9 years ago (diff)

3D branch: merge changes from trunk (rev [661])

Line 
1############################################################################
2# openPLM - open source PLM
3# Copyright 2010 Philippe Joulaud, Pierre Cosquer
4#
5# This file is part of openPLM.
6#
7#    openPLM is free software: you can redistribute it and/or modify
8#    it under the terms of the GNU General Public License as published by
9#    the Free Software Foundation, either version 3 of the License, or
10#    (at your option) any later version.
11#
12#    openPLM is distributed in the hope that it will be useful,
13#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15#    GNU General Public License for more details.
16#
17#    You should have received a copy of the GNU General Public License
18#    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
19#
20# Contact :
21#    Philippe Joulaud : ninoo.fr@gmail.com
22#    Pierre Cosquer : pierre.cosquer@insa-rennes.fr
23################################################################################
24
25"""
26"""
27
28import datetime
29from collections import namedtuple
30
31
32import openPLM.plmapp.models as models
33from openPLM.plmapp.units import DEFAULT_UNIT
34from openPLM.plmapp.controllers.plmobject import PLMObjectController
35from openPLM.plmapp.controllers.base import get_controller
36
37Child = namedtuple("Child", "level link")
38Parent = namedtuple("Parent", "level link")
39
40class PartController(PLMObjectController):
41    u"""
42    Controller for :class:`.Part`.
43
44    This controller adds methods to manage Parent-Child links between two
45    Parts.
46    """
47
48    def check_add_child(self, child):
49        """
50        Checks if *child"* can be added to *self*.
51        If *child* can not be added, an exception is raised.
52       
53        :param child: child to be added
54        :type child: :class:`.Part`
55       
56        :raises: :exc:`ValueError` if *child* is already a child or a parent.
57        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
58            :attr:`object`.   
59        """
60        self.check_permission("owner")
61        self.check_editable()
62        if not child.is_part:
63            raise TypeError("Can not add child: not a Part")
64        # check if child is not a parent
65        if child.id == self.object.id:
66            raise ValueError("Can not add child: child is current object")
67        get_controller(child.type)(child, self._user).check_readable()
68        parents = (p.link.parent.pk for p in self.get_parents(-1))
69        if child.pk in parents:
70            raise ValueError("Can not add child %s to %s, it is a parent" %
71                                (child, self.object))
72        link = self.parentchildlink_parent.filter(child=child, end_time=None)
73        if link:
74            raise ValueError("Can not add child, %s is already a child of %s" %
75                                (child, self.object))
76
77    def can_add_child(self, child):
78        """
79        Returns True if *child* can be added to *self*.
80        """
81
82        can_add = False
83        try:
84            self.check_add_child(child)
85            can_add = True
86        except StandardError:
87            pass
88        return can_add
89
90    def add_child(self, child, quantity, order, unit=DEFAULT_UNIT):
91        """
92        Adds *child* to *self*.
93
94        :param child: added child
95        :type child: :class:`.Part`
96        :param quantity: amount of *child*
97        :type quantity: positive float
98        :param order: order
99        :type order: positive int
100        :param unit: a valid unit
101       
102        :raises: :exc:`ValueError` if *child* is already a child or a parent.
103        :raises: :exc:`ValueError` if *quantity* or *order* are negative.
104        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
105            :attr:`object`.
106        :raises: :exc:`.PermissionError` if :attr:`object` is not editable.
107        """
108
109        if isinstance(child, PLMObjectController):
110            child = child.object
111        self.check_add_child(child)
112        # check if child is not already a direct child
113        if child.pk in (c.link.child.pk for c in self.get_children(1)):
114            raise ValueError("%s is already a child of %s" % (child, self.object))
115        if order < 0 or quantity < 0:
116            raise ValueError("Quantity or order is negative")
117        # data are valid : create the link
118        link = models.ParentChildLink()
119        link.parent = self.object
120        link.child = child
121        link.quantity = quantity
122        link.order = order
123        link.unit = unit
124        link.save()
125        # records creation in history
126        self._save_histo(link.ACTION_NAME,
127                         "parent : %s\nchild : %s" % (self.object, child))
128
129    def delete_child(self, child):
130        u"""
131        Deletes *child* from current children and records this action in the
132        history.
133
134        .. note::
135            The link is not destroyed: its end_time is set to now.
136       
137        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
138            :attr:`object`.
139        :raises: :exc:`.PermissionError` if :attr:`object` is not editable.
140        """
141
142        self.check_permission("owner")
143        self.check_editable()
144        if isinstance(child, PLMObjectController):
145            child = child.object
146        link = self.parentchildlink_parent.get(child=child, end_time=None)
147        link.end_time = datetime.datetime.today()
148        link.save()
149        self._save_histo("Delete - %s" % link.ACTION_NAME, "child : %s" % child)
150
151    def modify_child(self, child, new_quantity, new_order, new_unit):
152        """
153        Modifies information about *child*.
154
155        :param child: added child
156        :type child: :class:`.Part`
157        :param new_quantity: amount of *child*
158        :type new_quantity: positive float
159        :param new_order: order
160        :type new_order: positive int
161       
162        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
163            :attr:`object`.
164        :raises: :exc:`.PermissionError` if :attr:`object` is not editable.
165        """
166       
167        self.check_permission("owner")
168        self.check_editable()
169        if isinstance(child, PLMObjectController):
170            child = child.object
171        if new_order < 0 or new_quantity < 0:
172            raise ValueError("Quantity or order is negative")
173        link = models.ParentChildLink.objects.get(parent=self.object,
174                                                  child=child, end_time=None)
175        if (link.quantity == new_quantity and link.order == new_order and
176            link.unit == new_unit):
177            # do not make an update if it is useless
178            return
179        link.end_time = datetime.datetime.today()
180        link.save()
181        # make a new link
182        link2 = models.ParentChildLink(parent=self.object, child=child,
183                                       quantity=new_quantity, order=new_order,
184                                       unit=new_unit)
185        details = ""
186        if link.quantity != new_quantity:
187            details += "quantity changes from %f to %f\n" % (link.quantity, new_quantity)
188        if link.order != new_order:
189            details += "order changes from %d to %d" % (link.order, new_order)
190        if link.unit != new_unit:
191            details += "unit changes from %s to %s" % (link.unit, new_unit)
192        self._save_histo("Modify - %s" % link.ACTION_NAME, details)
193        link2.save(force_insert=True)
194
195    def get_children(self, max_level=1, current_level=1, date=None):
196        """
197        Returns a list of all children at time *date*.
198       
199        :rtype: list of :class:`Child`
200        """
201
202        if max_level != -1 and current_level > max_level:
203            return []
204        if not date:
205            links = self.parentchildlink_parent.filter(end_time__exact=None)
206        else:
207            links = self.parentchildlink_parent.filter(ctime__lt=date).exclude(end_time__lt=date)
208        res = []
209        for link in links.order_by("order", "child__reference"):
210            res.append(Child(current_level, link))
211            pc = PartController(link.child, self._user)
212            res.extend(pc.get_children(max_level, current_level + 1, date))
213        return res
214   
215    def get_parents(self, max_level=1, current_level=1, date=None):
216        """
217        Returns a list of all parents at time *date*.
218       
219        :rtype: list of :class:`Parent`
220        """
221
222        if max_level != -1 and current_level > max_level:
223            return []
224        if not date:
225            links = self.parentchildlink_child.filter(end_time__exact=None)
226        else:
227            links = self.parentchildlink_child.filter(ctime__lt=date).exclude(end_time__lt=date)
228        res = []
229        for link in links:
230            res.append(Parent(current_level, link))
231            pc = PartController(link.parent, self._user)
232            res.extend(pc.get_parents(max_level, current_level + 1, date))
233        return res
234
235    def update_children(self, formset):
236        u"""
237        Updates children informations with data from *formset*
238       
239        :param formset:
240        :type formset: a modelfactory_formset of
241                        :class:`~plmapp.forms.ModifyChildForm`
242       
243        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
244            :attr:`object`.
245        :raises: :exc:`.PermissionError` if :attr:`object` is not editable.
246        """
247
248        self.check_permission("owner")
249        self.check_editable()
250        if formset.is_valid():
251            for form in formset.forms:
252                parent = form.cleaned_data["parent"]
253                if parent.pk != self.object.pk:
254                    raise ValueError("Bad parent %s (%s expected)" % (parent, self.object))
255                delete = form.cleaned_data["delete"]
256                child = form.cleaned_data["child"]
257                if delete:
258                    self.delete_child(child)
259                else:
260                    quantity = form.cleaned_data["quantity"]
261                    order = form.cleaned_data["order"]
262                    unit = form.cleaned_data["unit"]
263                    self.modify_child(child, quantity, order, unit)
264
265    def revise(self, new_revision):
266        # same as PLMObjectController + add children
267        new_controller = super(PartController, self).revise(new_revision)
268        for level, link in self.get_children(1):
269            new_controller.add_child(link.child, link.quantity, link.order,
270                    link.unit)
271        return new_controller
272
273    def attach_to_document(self, document):
274        """
275        Links *document* (a :class:`.Document`) with
276        :attr:`~PLMObjectController.object`.
277       
278        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
279            :attr:`object`.
280        """
281       
282        self.check_attach_document(document)
283        if isinstance(document, PLMObjectController):
284            document = document.object
285        self.documentpartlink_part.create(document=document)
286        self._save_histo(models.DocumentPartLink.ACTION_NAME,
287                         "Part : %s - Document : %s" % (self.object, document))
288
289    def detach_document(self, document):
290        """
291        Delete link between *document* (a :class:`.Document`)
292        and :attr:`~PLMObjectController.object`.
293       
294        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
295            :attr:`object`.
296        """
297
298        self.check_permission("owner")
299        if isinstance(document, PLMObjectController):
300            document = document.object
301        link = self.documentpartlink_part.get(document=document)
302        link.delete()
303        self._save_histo(models.DocumentPartLink.ACTION_NAME + " - delete",
304                         "Part : %s - Document : %s" % (self.object, document))
305
306    def get_attached_documents(self):
307        """
308        Returns all :class:`.Document` attached to
309        :attr:`~PLMObjectController.object`.
310        """
311        return self.documentpartlink_part.all()
312     
313    def is_document_attached(self, document):
314        """
315        Returns True if *document* is attached to the current part.
316        """
317
318        if isinstance(document, PLMObjectController):
319            document = document.object
320        return bool(self.documentpartlink_part.filter(document=document))
321   
322    def check_attach_document(self, document):
323        self.check_permission("owner")
324        if not hasattr(document, "is_document") or not document.is_document:
325            raise TypeError("%s is not a document" % document)
326
327        if isinstance(document, PLMObjectController):
328            document.check_readable()
329            document = document.object
330        else:
331            get_controller(document.type)(document, self._user).check_readable()
332        if self.is_document_attached(document):
333            raise ValueError("Document is already attached.")
334
335    def can_attach_document(self, document):
336        """
337        Returns True if *document* can be attached to the current part.
338        """
339        can_attach = False
340        try:
341            self.check_attach_document(document)
342            can_attach = True
343        except StandardError:
344            pass
345        return can_attach
346
347    def update_doc_cad(self, formset):
348        u"""
349        Updates doc_cad informations with data from *formset*
350       
351        :param formset:
352        :type formset: a modelfactory_formset of
353                        :class:`~plmapp.forms.ModifyChildForm`
354       
355        :raises: :exc:`.PermissionError` if :attr:`_user` is not the owner of
356            :attr:`object`.
357        """
358       
359        self.check_permission("owner")
360        if formset.is_valid():
361            for form in formset.forms:
362                part = form.cleaned_data["part"]
363                if part.pk != self.object.pk:
364                    raise ValueError("Bad part %s (%s expected)" % (part, self.object))
365                delete = form.cleaned_data["delete"]
366                document = form.cleaned_data["document"]
367                if delete:
368                    self.detach_document(document)
369
Note: See TracBrowser for help on using the repository browser.