def upgrade():
  # New instances don't need this migration so we can skip this.
  # All product instances already had this migration applied and therefore
  # don't need this.
  # In case this migration IS needed - FIRST upgrade to grapes release, THEN
  # upgrade to plum and beyond...

  return

  today = date.today()

  # Get all active workflows with no next_cycle_start_date
  workflows = db.session.query(models.Workflow) \
    .filter(
    models.Workflow.next_cycle_start_date == None,
    models.Workflow.recurrences == True,
    models.Workflow.status == 'Active'
  ).all()

  from ggrc_workflows.services.workflow_cycle_calculator import \
    get_cycle_calculator
  # Update all workflows.
  for workflow in workflows:
    base_date = date.today()
    calculator = get_cycle_calculator(workflow)
    workflow.next_cycle_start_date = \
      calculator.nearest_start_date_after_basedate(base_date)
    db.session.add(workflow)

  # Save
  db.session.commit()
def upgrade():
    # New instances don't need this migration so we can skip this.
    # All product instances already had this migration applied and therefore
    # don't need this.
    # In case this migration IS needed - FIRST upgrade to grapes release, THEN
    # upgrade to plum and beyond...

    return

    today = date.today()

    # Get all active workflows with no next_cycle_start_date
    workflows = db.session.query(models.Workflow) \
      .filter(
      models.Workflow.next_cycle_start_date == None,
      models.Workflow.recurrences == True,
      models.Workflow.status == 'Active'
    ).all()

    from ggrc_workflows.services.workflow_cycle_calculator import \
      get_cycle_calculator
    # Update all workflows.
    for workflow in workflows:
        base_date = date.today()
        calculator = get_cycle_calculator(workflow)
        workflow.next_cycle_start_date = \
          calculator.nearest_start_date_after_basedate(base_date)
        db.session.add(workflow)

    # Save
    db.session.commit()
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
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()
Exemplo n.º 5
0
def handle_cycle_post(sender, obj=None, src=None, service=None):  # noqa pylint: disable=unused-argument
  if src.get('autogenerate', False):
    # When called via a REST POST, use current user.
    current_user = get_current_user()
    workflow = obj.workflow
    obj.calculator = workflow_cycle_calculator.get_cycle_calculator(workflow)

    if workflow.non_adjusted_next_cycle_start_date:
      base_date = workflow.non_adjusted_next_cycle_start_date
    else:
      base_date = date.today()
    build_cycle(obj, current_user=current_user, base_date=base_date)

    adjust_next_cycle_start_date(obj.calculator, workflow, move_forward=True)
    update_workflow_state(workflow)
    db.session.add(workflow)
Exemplo n.º 6
0
def handle_cycle_post(sender, obj=None, src=None, service=None):  # noqa pylint: disable=unused-argument
  if src.get('autogenerate', False):
    # When called via a REST POST, use current user.
    current_user = get_current_user()
    workflow = obj.workflow
    obj.calculator = workflow_cycle_calculator.get_cycle_calculator(workflow)

    if workflow.non_adjusted_next_cycle_start_date:
      base_date = workflow.non_adjusted_next_cycle_start_date
    else:
      base_date = date.today()
    build_cycle(obj, current_user=current_user, base_date=base_date)

    adjust_next_cycle_start_date(obj.calculator, workflow, move_forward=True)
    update_workflow_state(workflow)
    db.session.add(workflow)
Exemplo n.º 7
0
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()
Exemplo n.º 8
0
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)

  log_event(db.session)
  db.session.commit()
Exemplo n.º 9
0
def upgrade():
    op.add_column(
        'workflows',
        sa.Column('non_adjusted_next_cycle_start_date',
                  sa.Date(),
                  nullable=True))

    # If somebody deleted all the tasks we must clear the next cycle start
    # date
    workflows = db.session.query(models.Workflow) \
        .filter(
        models.Workflow.next_cycle_start_date != None,
        models.Workflow.recurrences == True,
        models.Workflow.status == 'Active',
        models.Workflow.next_cycle_start_date < date.today()
    ).all()

    for workflow in workflows:
        tasks_start_days = [
            task.relative_start_day for tg in workflow.task_groups
            for task in tg.task_group_tasks
        ]

        tasks_end_days = [
            task.relative_end_day for tg in workflow.task_groups
            for task in tg.task_group_tasks
        ]

        if ((not all(tasks_start_days) and not all(tasks_end_days))
                or (not tasks_start_days and not tasks_end_days)):
            app.logger.warning(
                "Removing NCSD from expired WF {} because no tasks are "
                "set up. Current NCSD: {}".format(
                    workflow.id, workflow.next_cycle_start_date))
            workflow.next_cycle_start_date = None
            db.session.add(workflow)

    workflows = db.session.query(models.Workflow) \
        .filter(
        models.Workflow.next_cycle_start_date != None,
        models.Workflow.non_adjusted_next_cycle_start_date == None,
        models.Workflow.recurrences == True,
        models.Workflow.status == 'Active',
        models.Workflow.next_cycle_start_date >= date.today()
    ).all()

    for workflow in workflows:
        tasks_start_days = [
            task.relative_start_day for tg in workflow.task_groups
            for task in tg.task_group_tasks
        ]

        tasks_end_days = [
            task.relative_end_day for tg in workflow.task_groups
            for task in tg.task_group_tasks
        ]

        # We must skip tasks that don't have start days and end days defined
        if ((not all(tasks_start_days) and not all(tasks_end_days))
                or (not tasks_start_days and not tasks_end_days)):
            append_msg = ""
            if workflow.next_cycle_start_date:
                workflow.next_cycle_start_date = None
                append_msg += (" Removing existing next cycle start date "
                               "because none are configured.")
                db.session.add(workflow)

            app.logger.warning("Skipping active WF {0} because no tasks "
                               "are set up.{1}".format(workflow.id,
                                                       append_msg))
            continue

        pre_compute_ncsd = workflow.next_cycle_start_date
        last_cycle_start_date = None
        if workflow.cycles:
            last_cycle_start_date = max(
                [c.start_date for c in workflow.cycles])

        if last_cycle_start_date:
            base_date = last_cycle_start_date
        else:
            base_date = base_date.today()

        base_date = max(base_date, workflow.next_cycle_start_date)
        calculator = get_cycle_calculator(workflow, base_date=base_date)

        if workflow.frequency in {"weekly", "monthly"}:
            nancsd_day = min(v['relative_start']
                             for v in calculator.reified_tasks.values())
            nancsd_month = None
        else:
            nancsd_month, nancsd_day = min(
                v['relative_start'] for v in calculator.reified_tasks.values())

        nancsd_date = calculator.relative_day_to_date(
            relative_day=nancsd_day,
            relative_month=nancsd_month,
            base_date=base_date)

        if last_cycle_start_date:
            while calculator.adjust_date(nancsd_date) <= last_cycle_start_date:
                base_date = base_date + calculator.time_delta
                nancsd_date = calculator.relative_day_to_date(
                    relative_day=nancsd_day,
                    relative_month=nancsd_month,
                    base_date=base_date)
        else:
            base_date = base_date - calculator.time_delta
            while calculator.adjust_date(nancsd_date) <= pre_compute_ncsd:
                base_date = base_date + calculator.time_delta
                nancsd_date = calculator.relative_day_to_date(
                    relative_day=nancsd_day,
                    relative_month=nancsd_month,
                    base_date=base_date)

        workflow.non_adjusted_next_cycle_start_date = nancsd_date
        workflow.next_cycle_start_date = calculator.adjust_date(nancsd_date)
        post_compute_ncsd = workflow.next_cycle_start_date

        start_dates = [
            "{}/{}".format(task.relative_start_month, task.relative_start_day)
            for tg in workflow.task_groups for task in tg.task_group_tasks
        ]
        end_dates = [
            "{}/{}".format(task.relative_end_month, task.relative_end_day)
            for tg in workflow.task_groups for task in tg.task_group_tasks
        ]

        if pre_compute_ncsd != post_compute_ncsd:
            app.logger.warning(
                "Adjusted NCSD for workflow {}. "
                "Freq: {}, PRE: {}, Last cycle: {}, POST: {}, NON: {},"
                "tasks start: {}, tasks end: {},".format(
                    workflow.id, workflow.frequency[:2], pre_compute_ncsd,
                    last_cycle_start_date, post_compute_ncsd,
                    workflow.non_adjusted_next_cycle_start_date, start_dates,
                    end_dates))
        db.session.add(workflow)

    # Save
    db.session.commit()
Exemplo n.º 10
0
    def test_task_order(self):
        annually_wf = {
            "title":
            "annually thingy",
            "description":
            "start this many a time",
            "frequency":
            "annually",
            "task_groups": [
                {
                    "title":
                    "task group",
                    "task_group_tasks": [
                        {
                            'title': 'annual task 1',
                            "relative_start_day": 21,  # 6/21/2015
                            "relative_start_month": 6,
                            "relative_end_day": 25,  # 6/25/2015 Thu
                            "relative_end_month": 6,
                        },
                        {
                            'title': 'annual task 2',
                            "relative_start_day": 11,  # 6/11/2015 Thu
                            "relative_start_month": 6,
                            "relative_end_day": 16,  # 6/16/2015 Tue
                            "relative_end_month": 6,
                        },
                        {
                            'title': 'annual task 6',
                            "relative_start_day": 2,  # 7/2/2015 Thu
                            "relative_start_month": 7,
                            "relative_end_day": 15,  # 7/15/2015 Wed
                            "relative_end_month": 7,
                        },
                        {
                            'title': 'annual task 3',
                            "relative_start_day": 3,  # 6/3/2015 Wed
                            "relative_start_month": 6,
                            "relative_end_day": 15,  # 6/15/2015 Mon
                            "relative_end_month": 6,
                        },
                        {
                            'title': 'annual task 4',
                            "relative_start_day": 8,  # 6/8/2015 Mon
                            "relative_start_month": 6,
                            "relative_end_day": 15,  # 6/15/2015 Mon
                            "relative_end_month": 6,
                        },
                        {
                            'title': 'annual task 5',
                            "relative_start_day": 2,  # 7/2/2015 Thu
                            "relative_start_month": 6,
                            "relative_end_day": 15,  # 6/15/2015 Mon
                            "relative_end_month": 6,
                        }
                    ],
                    "task_group_objects":
                    self.random_objects
                },
            ]
        }
        with freezegun.freeze_time("2015-06-01 13:00"):
            _, wf = self.generator.generate_workflow(annually_wf)

            active_wf = db.session.query(
                models.Workflow).filter(models.Workflow.id == wf.id).one()

            calculator = get_cycle_calculator(active_wf)
            self.assertEqual(
                [2, 3, 8, 11, 21, 2],
                [task.relative_start_day for task in calculator.tasks])