projectal.entities.task API documentation

# projectal.entities.task

View Source

    import datetime

    import projectal
    from projectal.entity import Entity
    from projectal.enums import DateLimit
    from projectal.linkers import *

    class Task(Entity, FileLinker, RebateLinker, ResourceLinker, SkillLinker,
               StaffLinker, StageLinker):
        """
        Implementation of the [Task](https://projectal.com/docs/latest/#tag/Task) API.
        """
        _path = 'task'
        _name = 'TASK'

        @classmethod
        def create(cls, holder, entities):
            """Create a Task

            `holder`: An instance or the `uuId` of the owner

            `entities`: `dict` containing the fields of the entity to be created,
            or a list of such `dict`s to create in bulk.
            """
            holder = holder['uuId'] if isinstance(holder, dict) else holder
            params = "?holder=" + holder
            return super().create(entities, params)

        def update_order(self, order_at_uuId, order_as=True):
            url = "/api/task/update?order-at={}&order-as={}".format(
                order_at_uuId, 'true' if order_as else 'false')
            return api.put(url, [{'uuId': self['uuId']}])

        def link_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'add')

        def relink_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'update')

        def unlink_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'delete')

        @classmethod
        def __plan(cls, from_task, to_task, operation):
            url = '/api/task/plan/task/{}'.format(operation)
            payload = {
                'uuId': from_task['uuId'],
                'taskList': [to_task]
            }
            api.post(url, payload=payload)
            return True

        def parents(self):
            """
            Return an ordered list of `uuId`s of this task's parents, up to
            (but not including) the root of the project.
            """
            payload = {
                "name": "Task Parents",
                "type": "msql",
                "start": 0,
                "limit": -1,
                "holder": "{}".format(self['uuId']),
                "select": [
                    [
                        "TASK(one).PARENT_ALL_TASK.name"
                    ],
                    [
                        "TASK(one).PARENT_ALL_TASK.uuId"
                    ]
                ],
            }
            list = api.query(payload)
            # Results come back in reverse order. Flip them around
            list.reverse()
            return list

        def project_uuId(self):
            """Return the `uuId` of the Project that holds this Task."""
            payload = {
                "name": "Project that holds this task",
                "type": "msql", "start": 0, "limit": 1,
                "holder": "{}".format(self['uuId']),
                "select": [
                    ["TASK.PROJECT.uuId"]
                ],
            }
            projects = api.query(payload)
            for t in projects:
                return t[0]
            return None

        @classmethod
        def add_task_template(cls, project, template):
            """Insert TaskTemplate `template` into Project `project`"""
            url = '/api/task/task_template/add?override=false&group=false'
            payload = {
                'uuId': project['uuId'],
                'templateList': [template]
            }
            api.post(url, payload)

        def reset_duration(self, calendars=None):
            """Set this task's duration based on its start and end dates while
            taking into account the calendar for weekends and scheduled time off.

            calendars is expected to be the list of calendar objects for the
            location of the project that holds this task. You may provide this
            list yourself for efficiency (recommended) - if not provided, it
            will be fetched for you by issuing requests to the server.
            """
            if not calendars:
                if 'projectRef' not in self:
                    task = projectal.Task.get(self)
                    project_ref = task['projectRef']
                else:
                    project_ref = self['projectRef']
                project = projectal.Project.get(project_ref, links='LOCATION')
                for location in project.get('locationList', []):
                    calendars = location.calendar()
                    break

            start = self.get('startTime')
            end = self.get('closeTime')
            if not start or start == DateLimit.Min:
                return 0
            if not end or end == DateLimit.Max:
                return 0

            # Build a list of weekday names that are non-working
            base_non_working = set()
            location_non_working = set()
            for calendar in calendars:
                if calendar['name'] == "base_calendar":
                    for item in calendar['calendarList']:
                        if not item['isWorking']:
                            base_non_working.add(item['type'])

                if calendar['name'] == "location":
                    for item in calendar['calendarList']:
                        if not item['isWorking']:
                            # TODO: handle multi-day exceptions and times
                            location_non_working.add(item['startDate'])

            start = datetime.datetime.fromtimestamp(start / 1000)
            end = datetime.datetime.fromtimestamp(end / 1000)
            hours = 0
            current = start
            while current <= end:
                if current.strftime('%A') in base_non_working:
                    current += datetime.timedelta(days=1)
                    continue
                if current.strftime('%Y-%m-%d') in location_non_working:
                    current += datetime.timedelta(days=1)
                    continue
                hours += 8
                current += datetime.timedelta(days=1)

            self['duration'] = hours * 60

class Task(projectal.entity.Entity, projectal.linkers.FileLinker, projectal.linkers.RebateLinker, projectal.linkers.ResourceLinker, projectal.linkers.SkillLinker, projectal.linkers.StaffLinker, projectal.linkers.StageLinker):
View Source

    class Task(Entity, FileLinker, RebateLinker, ResourceLinker, SkillLinker,
               StaffLinker, StageLinker):
        """
        Implementation of the [Task](https://projectal.com/docs/latest/#tag/Task) API.
        """
        _path = 'task'
        _name = 'TASK'

        @classmethod
        def create(cls, holder, entities):
            """Create a Task

            `holder`: An instance or the `uuId` of the owner

            `entities`: `dict` containing the fields of the entity to be created,
            or a list of such `dict`s to create in bulk.
            """
            holder = holder['uuId'] if isinstance(holder, dict) else holder
            params = "?holder=" + holder
            return super().create(entities, params)

        def update_order(self, order_at_uuId, order_as=True):
            url = "/api/task/update?order-at={}&order-as={}".format(
                order_at_uuId, 'true' if order_as else 'false')
            return api.put(url, [{'uuId': self['uuId']}])

        def link_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'add')

        def relink_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'update')

        def unlink_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'delete')

        @classmethod
        def __plan(cls, from_task, to_task, operation):
            url = '/api/task/plan/task/{}'.format(operation)
            payload = {
                'uuId': from_task['uuId'],
                'taskList': [to_task]
            }
            api.post(url, payload=payload)
            return True

        def parents(self):
            """
            Return an ordered list of `uuId`s of this task's parents, up to
            (but not including) the root of the project.
            """
            payload = {
                "name": "Task Parents",
                "type": "msql",
                "start": 0,
                "limit": -1,
                "holder": "{}".format(self['uuId']),
                "select": [
                    [
                        "TASK(one).PARENT_ALL_TASK.name"
                    ],
                    [
                        "TASK(one).PARENT_ALL_TASK.uuId"
                    ]
                ],
            }
            list = api.query(payload)
            # Results come back in reverse order. Flip them around
            list.reverse()
            return list

        def project_uuId(self):
            """Return the `uuId` of the Project that holds this Task."""
            payload = {
                "name": "Project that holds this task",
                "type": "msql", "start": 0, "limit": 1,
                "holder": "{}".format(self['uuId']),
                "select": [
                    ["TASK.PROJECT.uuId"]
                ],
            }
            projects = api.query(payload)
            for t in projects:
                return t[0]
            return None

        @classmethod
        def add_task_template(cls, project, template):
            """Insert TaskTemplate `template` into Project `project`"""
            url = '/api/task/task_template/add?override=false&group=false'
            payload = {
                'uuId': project['uuId'],
                'templateList': [template]
            }
            api.post(url, payload)

        def reset_duration(self, calendars=None):
            """Set this task's duration based on its start and end dates while
            taking into account the calendar for weekends and scheduled time off.

            calendars is expected to be the list of calendar objects for the
            location of the project that holds this task. You may provide this
            list yourself for efficiency (recommended) - if not provided, it
            will be fetched for you by issuing requests to the server.
            """
            if not calendars:
                if 'projectRef' not in self:
                    task = projectal.Task.get(self)
                    project_ref = task['projectRef']
                else:
                    project_ref = self['projectRef']
                project = projectal.Project.get(project_ref, links='LOCATION')
                for location in project.get('locationList', []):
                    calendars = location.calendar()
                    break

            start = self.get('startTime')
            end = self.get('closeTime')
            if not start or start == DateLimit.Min:
                return 0
            if not end or end == DateLimit.Max:
                return 0

            # Build a list of weekday names that are non-working
            base_non_working = set()
            location_non_working = set()
            for calendar in calendars:
                if calendar['name'] == "base_calendar":
                    for item in calendar['calendarList']:
                        if not item['isWorking']:
                            base_non_working.add(item['type'])

                if calendar['name'] == "location":
                    for item in calendar['calendarList']:
                        if not item['isWorking']:
                            # TODO: handle multi-day exceptions and times
                            location_non_working.add(item['startDate'])

            start = datetime.datetime.fromtimestamp(start / 1000)
            end = datetime.datetime.fromtimestamp(end / 1000)
            hours = 0
            current = start
            while current <= end:
                if current.strftime('%A') in base_non_working:
                    current += datetime.timedelta(days=1)
                    continue
                if current.strftime('%Y-%m-%d') in location_non_working:
                    current += datetime.timedelta(days=1)
                    continue
                hours += 8
                current += datetime.timedelta(days=1)

            self['duration'] = hours * 60

Implementation of the Task API.

@classmethod
def create(cls, holder, entities):
View Source

        @classmethod
        def create(cls, holder, entities):
            """Create a Task

            `holder`: An instance or the `uuId` of the owner

            `entities`: `dict` containing the fields of the entity to be created,
            or a list of such `dict`s to create in bulk.
            """
            holder = holder['uuId'] if isinstance(holder, dict) else holder
            params = "?holder=" + holder
            return super().create(entities, params)

Create a Task

`holder`: An instance or the `uuId` of the owner

`entities`: `dict` containing the fields of the entity to be created, or a list of such `dict`s to create in bulk.

def update_order(self, order_at_uuId, order_as=True):
View Source

        def update_order(self, order_at_uuId, order_as=True):
            url = "/api/task/update?order-at={}&order-as={}".format(
                order_at_uuId, 'true' if order_as else 'false')
            return api.put(url, [{'uuId': self['uuId']}])

def link_predecessor_task(self, predecessor_task):
View Source

        def link_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'add')

def relink_predecessor_task(self, predecessor_task):
View Source

        def relink_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'update')

def unlink_predecessor_task(self, predecessor_task):
View Source

        def unlink_predecessor_task(self, predecessor_task):
            return self.__plan(self, predecessor_task, 'delete')

def parents(self):
View Source

        def parents(self):
            """
            Return an ordered list of `uuId`s of this task's parents, up to
            (but not including) the root of the project.
            """
            payload = {
                "name": "Task Parents",
                "type": "msql",
                "start": 0,
                "limit": -1,
                "holder": "{}".format(self['uuId']),
                "select": [
                    [
                        "TASK(one).PARENT_ALL_TASK.name"
                    ],
                    [
                        "TASK(one).PARENT_ALL_TASK.uuId"
                    ]
                ],
            }
            list = api.query(payload)
            # Results come back in reverse order. Flip them around
            list.reverse()
            return list

Return an ordered list of `uuId`s of this task's parents, up to (but not including) the root of the project.

def project_uuId(self):
View Source

        def project_uuId(self):
            """Return the `uuId` of the Project that holds this Task."""
            payload = {
                "name": "Project that holds this task",
                "type": "msql", "start": 0, "limit": 1,
                "holder": "{}".format(self['uuId']),
                "select": [
                    ["TASK.PROJECT.uuId"]
                ],
            }
            projects = api.query(payload)
            for t in projects:
                return t[0]
            return None

Return the `uuId` of the Project that holds this Task.

@classmethod
def add_task_template(cls, project, template):
View Source

        @classmethod
        def add_task_template(cls, project, template):
            """Insert TaskTemplate `template` into Project `project`"""
            url = '/api/task/task_template/add?override=false&group=false'
            payload = {
                'uuId': project['uuId'],
                'templateList': [template]
            }
            api.post(url, payload)

Insert TaskTemplate `template` into Project `project`

def reset_duration(self, calendars=None):
View Source

        def reset_duration(self, calendars=None):
            """Set this task's duration based on its start and end dates while
            taking into account the calendar for weekends and scheduled time off.

            calendars is expected to be the list of calendar objects for the
            location of the project that holds this task. You may provide this
            list yourself for efficiency (recommended) - if not provided, it
            will be fetched for you by issuing requests to the server.
            """
            if not calendars:
                if 'projectRef' not in self:
                    task = projectal.Task.get(self)
                    project_ref = task['projectRef']
                else:
                    project_ref = self['projectRef']
                project = projectal.Project.get(project_ref, links='LOCATION')
                for location in project.get('locationList', []):
                    calendars = location.calendar()
                    break

            start = self.get('startTime')
            end = self.get('closeTime')
            if not start or start == DateLimit.Min:
                return 0
            if not end or end == DateLimit.Max:
                return 0

            # Build a list of weekday names that are non-working
            base_non_working = set()
            location_non_working = set()
            for calendar in calendars:
                if calendar['name'] == "base_calendar":
                    for item in calendar['calendarList']:
                        if not item['isWorking']:
                            base_non_working.add(item['type'])

                if calendar['name'] == "location":
                    for item in calendar['calendarList']:
                        if not item['isWorking']:
                            # TODO: handle multi-day exceptions and times
                            location_non_working.add(item['startDate'])

            start = datetime.datetime.fromtimestamp(start / 1000)
            end = datetime.datetime.fromtimestamp(end / 1000)
            hours = 0
            current = start
            while current <= end:
                if current.strftime('%A') in base_non_working:
                    current += datetime.timedelta(days=1)
                    continue
                if current.strftime('%Y-%m-%d') in location_non_working:
                    current += datetime.timedelta(days=1)
                    continue
                hours += 8
                current += datetime.timedelta(days=1)

            self['duration'] = hours * 60

Set this task's duration based on its start and end dates while taking into account the calendar for weekends and scheduled time off.

calendars is expected to be the list of calendar objects for the location of the project that holds this task. You may provide this list yourself for efficiency (recommended) - if not provided, it will be fetched for you by issuing requests to the server.

##### Inherited Members

projectal.entity.Entity
  Entity
  get
  update
  delete
  save
  clone
  history
  list
  match
  match_startswith
  match_endswith
  search
  query
  profile_get
  profile_set
  changes
  set_readonly
projectal.linkers.FileLinker
  link_file
  unlink_file
projectal.linkers.RebateLinker
  link_rebate
  unlink_rebate
projectal.linkers.ResourceLinker
  link_resource
  relink_resource
  unlink_resource
projectal.linkers.SkillLinker
  link_skill
  relink_skill
  unlink_skill
projectal.linkers.StaffLinker
  link_staff
  relink_staff
  unlink_staff
projectal.linkers.StageLinker
  link_stage
  unlink_stage
builtins.dict
  setdefault
  pop
  popitem
  keys
  items
  values
  fromkeys
  clear
  copy