def build_cycle(workflow, cycle=None, current_user=None): """Build a cycle with it's child objects""" if not workflow.tasks: logger.error("Starting a cycle has failed on Workflow with " "slug == '%s' and id == '%s'", workflow.slug, workflow.id) pusher.update_or_create_notifications(workflow, date.today(), "cycle_start_failed") return # Determine the relevant Workflow cycle = cycle or models.Cycle() # Use Admin role when this is called via the cron job. if not current_user: current_user = workflow.get_persons_for_rolename("Admin")[0] # Populate the top-level Cycle object cycle.workflow = workflow cycle.is_current = True cycle.context = workflow.context cycle.title = workflow.title cycle.description = workflow.description cycle.is_verification_needed = workflow.is_verification_needed cycle.status = models.Cycle.ASSIGNED # Populate CycleTaskGroups based on Workflow's TaskGroups for task_group in workflow.task_groups: cycle_task_group = models.CycleTaskGroup( context=cycle.context, cycle=cycle, task_group=task_group, title=task_group.title, description=task_group.description, end_date=cycle.end_date, modified_by=current_user, contact=task_group.contact, status=models.CycleTaskGroup.ASSIGNED, sort_index=task_group.sort_index, ) # preserve the old cycle creation for old workflows, so each object # gets its own cycle task if workflow.is_old_workflow: create_old_style_cycle(cycle, task_group, cycle_task_group, current_user) else: for task_group_task in task_group.task_group_tasks: cycle_task_group_object_task = _create_cycle_task( task_group_task, cycle, cycle_task_group, current_user) for task_group_object in task_group.task_group_objects: object_ = task_group_object.object Relationship(source=cycle_task_group_object_task, destination=object_) update_cycle_dates(cycle) workflow.repeat_multiplier += 1 workflow.next_cycle_start_date = workflow.calc_next_adjusted_date( workflow.min_task_start_date) return cycle
def test_generates_correct_url_for_inactive_cycle(self, *mocks): """The method should return correct URL for inactive Cycles.""" workflow = models.Workflow(id=111) cycle = models.Cycle(id=22, workflow=workflow) expected_url = (u"http://www.foo.com/" u"workflows/111#history" u"/cycle/22") self.assertEqual(expected_url, cycle.cycle_inactive_url)
def test_url_for_inactive_cycle(self, *_): """The method should return correct URL for inactive Cycles.""" workflow = models.Workflow(id=111) cycle = models.Cycle(slug="CYCLE-22", workflow=workflow) expected_url = (u"http://www.foo.com/" u"workflows/111#history" u"&query=%22cycle%20slug%22%3D%22CYCLE-22%22") self.assertEqual(expected_url, cycle.cycle_inactive_url)
def test_generates_correct_url_for_active_cycle(self, *mocks): """The method should return correct URL for active Cycles.""" workflow = models.Workflow(id=111) cycle = models.Cycle(id=22, workflow=workflow) # by default, a cycle is considered active expected_url = (u"http://www.foo.com/" u"workflows/111#current" u"/cycle/22") self.assertEqual(expected_url, cycle.cycle_url)
def test_url_for_active_cycle(self, *_): """The method should return correct URL for active Cycles.""" workflow = models.Workflow(id=111) cycle = models.Cycle(slug="CYCLE-22", workflow=workflow) # by default, a cycle is considered active expected_url = (u"http://www.foo.com/" u"workflows/111#current" u"&query=%22cycle%20slug%22%3D%22CYCLE-22%22") self.assertEqual(expected_url, cycle.cycle_url)
def update_workflow_state(workflow): today = date.today() calculator = workflow_cycle_calculator.get_cycle_calculator(workflow) # Start the first cycle if min_start_date < today < max_end_date if workflow.status == "Active" and workflow.recurrences and calculator.tasks: start_date, end_date = calculator.workflow_date_range() # Only create the cycle if we're mid-cycle if (start_date <= today <= end_date) \ and not workflow.cycles: cycle = models.Cycle() cycle.workflow = workflow cycle.calculator = calculator # Other cycle attributes will be set in build_cycle. build_cycle( cycle, None, base_date=workflow.non_adjusted_next_cycle_start_date) notification.handle_cycle_created(None, obj=cycle) adjust_next_cycle_start_date(calculator, workflow) db.session.add(workflow) db.session.flush() return if not calculator.tasks: workflow.next_cycle_start_date = None workflow.non_adjusted_next_cycle_start_date = None return for cycle in workflow.cycles: if cycle.is_current: return if workflow.status == 'Draft': return if workflow.status == "Inactive": if workflow.cycles: workflow.status = "Active" db.session.add(workflow) db.session.flush() return # Active workflow with no recurrences and no active cycles, workflow is # now Inactive workflow.status = 'Inactive' db.session.add(workflow) db.session.flush()
def start_recurring_cycles(): # Get all workflows that should start a new cycle today # The next_cycle_start_date is precomputed and stored when a cycle is created today = date.today() workflows = db.session.query(models.Workflow)\ .filter( models.Workflow.next_cycle_start_date == today, models.Workflow.recurrences == True # noqa ).all() # For each workflow, start and save a new cycle. for workflow in workflows: cycle = models.Cycle() cycle.workflow = workflow cycle.calculator = workflow_cycle_calculator.get_cycle_calculator( workflow) cycle.context = workflow.context # We can do this because we selected only workflows with # next_cycle_start_date = today cycle.start_date = date.today() # Flag the cycle to be saved db.session.add(cycle) if workflow.non_adjusted_next_cycle_start_date: base_date = workflow.non_adjusted_next_cycle_start_date else: base_date = date.today() # Create the cycle (including all child objects) build_cycle(cycle, base_date=base_date) # Update the workflow next_cycle_start_date to push it ahead based on the # frequency. adjust_next_cycle_start_date(cycle.calculator, workflow, move_forward=True) db.session.add(workflow) notification.handle_workflow_modify(None, workflow) notification.handle_cycle_created(None, obj=cycle) db.session.commit() db.session.flush()
def build_cycle(workflow, cycle=None, current_user=None): """Build a cycle with it's child objects""" build_failed = False if not workflow.tasks: logger.error("Starting a cycle has failed on Workflow with " "slug == '%s' and id == '%s', due to empty setup", workflow.slug, workflow.id) build_failed = True # Use Admin role when this is called via the cron job. if not current_user: admins = workflow.get_persons_for_rolename("Admin") if admins: current_user = admins[0] else: logger.error("Cannot start cycle on Workflow with slug == '%s' and " "id == '%s', cause it doesn't have Admins", workflow.slug, workflow.id) build_failed = True if build_failed: pusher.update_or_create_notifications(workflow, date.today(), "cycle_start_failed") return # Determine the relevant Workflow cycle = cycle or models.Cycle() # Populate the top-level Cycle object cycle.workflow = workflow cycle.is_current = True cycle.context = workflow.context cycle.title = workflow.title cycle.description = workflow.description cycle.is_verification_needed = workflow.is_verification_needed cycle.status = models.Cycle.ASSIGNED # Populate CycleTaskGroups based on Workflow's TaskGroups for task_group in workflow.task_groups: cycle_task_group = models.CycleTaskGroup( context=cycle.context, cycle=cycle, task_group=task_group, title=task_group.title, description=task_group.description, end_date=cycle.end_date, modified_by=current_user, contact=task_group.contact, status=models.CycleTaskGroup.ASSIGNED, ) # preserve the old cycle creation for old workflows, so each object # gets its own cycle task if workflow.is_old_workflow: create_old_style_cycle(cycle, task_group, cycle_task_group, current_user) else: for task_group_task in task_group.task_group_tasks: cycle_task_group_object_task = _create_cycle_task( task_group_task, cycle, cycle_task_group, current_user) related_objs = [obj for obj in task_group.related_objects() if not isinstance(obj, ( all_models.TaskGroupTask, all_models.Workflow ))] for obj in related_objs: Relationship(source=cycle_task_group_object_task, destination=obj) update_cycle_dates(cycle) workflow.repeat_multiplier += 1 workflow.next_cycle_start_date = workflow.calc_next_adjusted_date( workflow.min_task_start_date) return cycle