def resolve(self, user, project, resolution=COMPLETED): "Resolve the task." status = self.statuses.get(project=project) status.status = RESOLVED status.resolution = resolution status.save() flag = RESOLVED + resolution status_changed.send(sender=status, user=user, flag=flag)
def activate(self, user): "Activate the tracker across all related projects." self.activate_children(user) for status in self.statuses.all(): status.status = ACTIVE status.save() status_changed.send(sender=status, user=user, flag=ACTIVATED)
def resolve(self, user, resolution=COMPLETED, bubble_up=True): """Resolve the step. Resolve the current step and, if `bubble_up` is True, resolve the parent or the parents. Arguments: user -- the user that is resolving the step; used for the signal resolution -- an integer specifying the resolution type (see todo.workflow) bubble_up -- a boolean which if True will make the method resolve the parent(s) of the current step as well. More on the `bubble_up` behavior: When `bubble_up` is True, and if the step was resolved successfully and it is the last one among its siblings, its parent will be resolved successfully too and if in turn the parent is the last one among *its* siblings, the parent's parent will be resolved etc. The resolution *bubbles up*. On the other hand, if the step has failed (note that it must be a review step to be able to fail in the first place), the immediate parent of the current step is resolved as failed as well and a fresh clone is made so that the required actions can take place again. This time, however, the parent's parent is not touched. The bubbling stops after the first parent. """ self.status = RESOLVED self.resolution = resolution self.save() flag = RESOLVED + resolution status_changed.send(sender=self, user=user, flag=flag) if not bubble_up: # don't do anything more return if ((self.is_last() and self.is_only_active()) or self.is_last_open()): if self.parent: # resolve the parent Step(s) if self.resolution == FAILED: # if the step has failed, resolve only the immediate parent # and make a fresh copy of it. The parent needs to be # a Step, not the Task (can't clone a Task), hence check # for self.parent. bubble_up = False # `clone` will call the prototype's `spawn` method which by # default activates the created todos self.parent.clone(user) self.parent.resolve(user, self.resolution, bubble_up) else: # no parent means this is a top-level step and the task is # possibly ready to be resolved. This left for the user to # decide however. pass elif self.next_step() and self.next_step().status_is('new'): self.next_step().activate(user)
def activate(self, user): if self.has_children: self.activate_children(user) # it's `active`, because one of the children is `next` self.status = ACTIVE else: # no children, `next` it self.status = NEXT self.save() status_changed.send(sender=self, user=user, flag=self.status)
def activate(self, user): if self.has_children is True: self.activate_children(user) # it's `active`, because one of the children is `next` self.status = 2 else: # no children, `next` it self.status = 3 self.save() status_changed.send(sender=self, user=user, action=self.status)
def spawn(self, user, cloning_allowed=True, **custom_fields): """Create an instance of the model related to the proto, with children. This method creates an instance of the model related to the current prototype, e.g. in case of a ProtoTracker, it will create a Tracker. It also creates all the children of the newly created Tracker, Task or Step (the 'todo' objects). The todo objects will be created with properties as set on the prototypes and nestings. You can override them by passing custom values in the keyword arguments. A `project` keyword argument is required when spawning trackers or tasks. It must be an instance of todo.models.Project. The created todo objects will be related to the project passed. A special iterable `locales` keyword argument can be passed to create multiple trees of todo objects, one per locale in `locales`. Only todo objects with clone_per_locale set to True will be cloned in this way. The method always returns just one, top-level todo object, even if more were created as children. """ if self.type in (1, 2) and 'project' not in custom_fields: raise TypeError("Pass a project to spawn trackers and tasks.") todo = self._spawn_instance(**custom_fields) todo.save() status_changed.send(sender=todo, user=user, action=todo.status) if self.type in (1, 3): # trackers and steps to_be_removed = self.inheritable custom_fields.update(parent=todo) else: # tasks # Steps are related to Tasks via the `task` property which is set # above, and the top-level steps have the `parent` property set # to None. Here, we're removing `parent` to make sure the child # steps are `parent`-less. (`parent` might have been used before # to create relationships between Trackers or Trackers/Tasks). to_be_removed = self.inheritable + ('parent',) custom_fields.update(task=todo) for prop in to_be_removed: # these properties are inheritable by the current todo object, # but not by its children. they should not propagate further down. if prop in custom_fields: del custom_fields[prop] self._spawn_children(user, cloning_allowed, **custom_fields) return todo
def create(request, obj): if obj == 'tasks': form_class = AddTasksForm else: form_class = AddTrackersForm if request.method == 'POST': form = form_class(request.POST) parent_form = ChooseParentForm(request.POST) if form.is_valid() and parent_form.is_valid(): fields = form.cleaned_data parent_clean = parent_form.cleaned_data parent = parent_clean['tracker'] if (parent is None and parent_clean['parent_summary'] and parent_clean['parent_project']): # user wants to create a new tracker which will be the parent parent = Tracker(summary=parent_clean['parent_summary'], project=parent_clean['parent_project'], locale=parent_clean['parent_locale'], suffix=parent_clean['parent_suffix']) parent.save() # send the 'created' signal status_changed.send(sender=parent, user=request.user, action=parent.status) parent.activate(request.user) if parent is not None: # For consistency's sake, if a parent is specified, try to # use its values for `project` and `locale` to create the # task, ignoring values provided by the user in the form. fields['project'] = parent.project if parent.locale: fields['locales'] = [parent.locale] fields['parent'] = parent prototype = form.cleaned_data.pop('prototype') if prototype.clone_per_locale is True: for todo in prototype.spawn_per_locale(request.user, **fields): todo.activate(request.user) else: todo = prototype.spawn(request.user, **fields) todo.activate(request.user) return HttpResponseRedirect(reverse('todo.views.demo.%s' % obj[:-1], args=[todo.pk])) else: form = form_class() parent_form = ChooseParentForm() return render_to_response('todo/new_%s.html' % obj[:-1], {'form': form, 'parent_form': parent_form,})
def _spawn_instance(self, user, activate, **custom_fields): "Create an instance of the model related to the proto." related_model = self.get_related_model() # fields that are accepted by the spawned object accepted_fields = [f.name for f in related_model._meta.fields] # models can define additional accepted fields (e.g. 'suffix') accepted_fields += related_model.extra_fields # remove projects from custom_fields since related_model.__init__ # (regardless of which model it stands for) does not accept them as # argument; store the value for later use, though. projects = custom_fields.pop('projects', None) # remove empty/unknown values from custom_fields and overwrite other # attributes custom_fields = [(k, v) for k, v in custom_fields.items() if v and k in accepted_fields] # fields (with values) which will be inherited by the spawned object fields = dict([(f, getattr(self, f)) for f in self.inheritable]) # custom fields override fields from the proto fields.update(custom_fields) todo = related_model(prototype=self, **fields) # store the string representation of the todo as its property before # it is saved, in order to avoid a query made by todo.get_repr todo.repr = todo.format_repr(**fields) if self.type == STEP_TYPE: if activate and todo.should_be_activated(): # a Step can only be related to a single Project (or, more # often, to no projects at all), so its status is stored # directly as a property. todo.status = ACTIVE todo.save() else: # save it so that it has an ID todo.save() # in order to create relations between Trackers/Tasks and Projects, # create required {Tracker,Task}InProject objects handling the # many-to-many relation; set the status to 'active' is requested. todo.assign_to_projects(projects, status=ACTIVE if activate else None) status_changed.send(sender=todo, user=user, flag=CREATED) return todo
def reset_time(self, user): if self.status == NEXT: # the step must be a 'next' step. Resetting the timer is simply # sending the signal about the step being 'nexted' again. status_changed.send(sender=self, user=user, flag=NEXTED)
def resolve(self, user, resolution=1): self.status = 5 self.resolution = resolution self.save() status_changed.send(sender=self, user=user, action=self.status)
def activate(self, user): self.activate_children(user) self.status = 2 self.save() status_changed.send(sender=self, user=user, action=self.status)