projectal.entities.staff
1from datetime import datetime 2 3import projectal 4from projectal.entity import Entity 5from projectal.linkers import * 6from projectal.errors import UsageException 7from projectal.enums import DateLimit 8 9 10class Staff( 11 Entity, 12 LocationLinker, 13 ResourceLinker, 14 SkillLinker, 15 FileLinker, 16 CompanyLinker, 17 DepartmentLinker, 18 TaskLinker, 19 TaskTemplateLinker, 20 NoteLinker, 21 CalendarLinker, 22 TagLinker, 23 ContractLinker, 24): 25 """ 26 Implementation of the [Staff](https://projectal.com/docs/latest/#tag/Staff) API. 27 """ 28 29 _path = "staff" 30 _name = "staff" 31 _links = [ 32 LocationLinker, 33 ResourceLinker, 34 SkillLinker, 35 FileLinker, 36 NoteLinker, 37 CalendarLinker, 38 TagLinker, 39 ContractLinker, 40 ] 41 _links_reverse = [CompanyLinker, DepartmentLinker, TaskLinker, TaskTemplateLinker] 42 43 @classmethod 44 def calendar(cls, uuId, begin=None, until=None): 45 """ 46 Returns the calendar of the staff with `uuId`. 47 48 `begin`: Start date in `yyyy-MM-dd`. 49 50 `until`: End date in `yyyy-MM-dd`. 51 52 53 Optionally specify a date range. If no range specified, the 54 minimum and maximum dates are used (see projectal.enums.DateLimit). 55 """ 56 if begin: 57 begin = datetime.strptime(begin, "%Y-%m-%d").date() 58 if until: 59 until = datetime.strptime(until, "%Y-%m-%d").date() 60 61 url = "/api/staff/{}/calendar?".format(uuId) 62 params = [] 63 params.append("begin={}".format(begin)) if begin else None 64 params.append("until={}".format(until)) if until else None 65 url += "&".join(params) 66 67 cals = api.get(url) 68 cals = [projectal.Calendar(c) for c in cals] 69 return cals 70 71 @classmethod 72 def calendar_availability(cls, uuId, begin=None, until=None): 73 """ 74 Returns the availability (in hours) of the staff in `uuId` 75 for each day within the specified date range. 76 77 `begin`: Start date in `yyyy-MM-dd`. 78 79 `until`: End date in `yyyy-MM-dd`. 80 81 If no range specified, the minimum and maximum dates are 82 used (see projectal.enums.DateLimit). 83 """ 84 if begin: 85 begin = datetime.strptime(begin, "%Y-%m-%d").date() 86 if until: 87 until = datetime.strptime(until, "%Y-%m-%d").date() 88 89 url = "/api/staff/{}/calendar/availability?".format(uuId) 90 params = [] 91 params.append("begin={}".format(begin)) if begin else None 92 params.append("until={}".format(until)) if until else None 93 url += "&".join(params) 94 95 return api.get(url) 96 97 @classmethod 98 def usage( 99 cls, 100 begin, 101 until, 102 holder=None, 103 start=None, 104 limit=None, 105 span=None, 106 ksort=None, 107 order=None, 108 staff=None, 109 ): 110 """ 111 Returns the staff-to-task allocations for all staff within the `holder`. 112 113 See [Usage API](https://projectal.com/docs/latest/#tag/Staff/paths/~1api~1staff~1usage/post) 114 for full details. 115 """ 116 url = "/api/staff/usage?begin={}&until={}".format(begin, until) 117 params = [] 118 params.append("holder={}".format(holder)) if holder else None 119 params.append("start={}".format(start)) if start else None 120 params.append("limit={}".format(limit)) if limit else None 121 params.append("span={}".format(span)) if span else None 122 params.append("ksort={}".format(ksort)) if ksort else None 123 params.append("order={}".format(order)) if order else None 124 if len(params) > 0: 125 url += "&" + "&".join(params) 126 payload = staff if staff and not holder else None 127 response = api.post(url, payload) 128 # Do some extra checks for empty list case 129 if "status" in response: 130 # We didn't have a 'jobCase' key and returned the outer dict. 131 return [] 132 return response 133 134 @classmethod 135 def auto_assign( 136 cls, 137 type="Recommend", 138 over_allocate_staff=False, 139 include_assigned_task=False, 140 include_started_task=False, 141 skills=None, 142 tasks=None, 143 staffs=None, 144 ): 145 """ 146 Automatically assign a set of staff (real or generic) to a set of tasks 147 using various skill and allocation criteria. 148 149 See [Staff Assign API](https://projectal.com/docs/latest/#tag/Staff-Assign/paths/~1api~1allocation~1staff/post) 150 for full details. 151 """ 152 url = "/api/allocation/staff" 153 payload = { 154 "type": type, 155 "overAllocateStaff": over_allocate_staff, 156 "includeAssignedTask": include_assigned_task, 157 "includeStartedTask": include_started_task, 158 "skillMatchList": skills if skills else [], 159 "staffList": staffs if staffs else [], 160 "taskList": tasks if tasks else [], 161 } 162 return api.post(url, payload) 163 164 @classmethod 165 def create_contract( 166 cls, 167 UUID, 168 payload={}, 169 end_current_contract=False, 170 start_new_contract=False, 171 ): 172 """ 173 Creates a new Contract for a staff, with updated fields from the payload 174 175 end_current: Sets the end date of the current contract to today's date 176 start_new_date: Sets the start date of the new contract to today's date 177 178 See [Staff Clone API](https://projectal.com/docs/latest#tag/Staff/paths/~1api~1staff~1clone/post) 179 for full details. 180 """ 181 182 url = "/api/staff/clone?reference={}&as_contract=true".format(UUID) 183 date_today = datetime.today().strftime("%Y-%m-%d") 184 185 current_staff = None 186 if end_current_contract: 187 # Check if setting end date to today is 188 # invalid for current Staff Contract 189 current_staff = cls.get(UUID) 190 if projectal.timestamp_from_date( 191 current_staff.get("startDate") 192 ) > projectal.timestamp_from_date(date_today): 193 raise UsageException( 194 f"Cannot set endDate before startDate for current contract: {date_today}" 195 ) 196 197 if start_new_contract: 198 payload["startDate"] = date_today 199 payload["endDate"] = DateLimit.Max 200 201 response = api.post(url, payload) 202 203 if end_current_contract and current_staff: 204 current_staff["endDate"] = date_today 205 current_staff.save() 206 207 return response["jobClue"]["uuId"]
11class Staff( 12 Entity, 13 LocationLinker, 14 ResourceLinker, 15 SkillLinker, 16 FileLinker, 17 CompanyLinker, 18 DepartmentLinker, 19 TaskLinker, 20 TaskTemplateLinker, 21 NoteLinker, 22 CalendarLinker, 23 TagLinker, 24 ContractLinker, 25): 26 """ 27 Implementation of the [Staff](https://projectal.com/docs/latest/#tag/Staff) API. 28 """ 29 30 _path = "staff" 31 _name = "staff" 32 _links = [ 33 LocationLinker, 34 ResourceLinker, 35 SkillLinker, 36 FileLinker, 37 NoteLinker, 38 CalendarLinker, 39 TagLinker, 40 ContractLinker, 41 ] 42 _links_reverse = [CompanyLinker, DepartmentLinker, TaskLinker, TaskTemplateLinker] 43 44 @classmethod 45 def calendar(cls, uuId, begin=None, until=None): 46 """ 47 Returns the calendar of the staff with `uuId`. 48 49 `begin`: Start date in `yyyy-MM-dd`. 50 51 `until`: End date in `yyyy-MM-dd`. 52 53 54 Optionally specify a date range. If no range specified, the 55 minimum and maximum dates are used (see projectal.enums.DateLimit). 56 """ 57 if begin: 58 begin = datetime.strptime(begin, "%Y-%m-%d").date() 59 if until: 60 until = datetime.strptime(until, "%Y-%m-%d").date() 61 62 url = "/api/staff/{}/calendar?".format(uuId) 63 params = [] 64 params.append("begin={}".format(begin)) if begin else None 65 params.append("until={}".format(until)) if until else None 66 url += "&".join(params) 67 68 cals = api.get(url) 69 cals = [projectal.Calendar(c) for c in cals] 70 return cals 71 72 @classmethod 73 def calendar_availability(cls, uuId, begin=None, until=None): 74 """ 75 Returns the availability (in hours) of the staff in `uuId` 76 for each day within the specified date range. 77 78 `begin`: Start date in `yyyy-MM-dd`. 79 80 `until`: End date in `yyyy-MM-dd`. 81 82 If no range specified, the minimum and maximum dates are 83 used (see projectal.enums.DateLimit). 84 """ 85 if begin: 86 begin = datetime.strptime(begin, "%Y-%m-%d").date() 87 if until: 88 until = datetime.strptime(until, "%Y-%m-%d").date() 89 90 url = "/api/staff/{}/calendar/availability?".format(uuId) 91 params = [] 92 params.append("begin={}".format(begin)) if begin else None 93 params.append("until={}".format(until)) if until else None 94 url += "&".join(params) 95 96 return api.get(url) 97 98 @classmethod 99 def usage( 100 cls, 101 begin, 102 until, 103 holder=None, 104 start=None, 105 limit=None, 106 span=None, 107 ksort=None, 108 order=None, 109 staff=None, 110 ): 111 """ 112 Returns the staff-to-task allocations for all staff within the `holder`. 113 114 See [Usage API](https://projectal.com/docs/latest/#tag/Staff/paths/~1api~1staff~1usage/post) 115 for full details. 116 """ 117 url = "/api/staff/usage?begin={}&until={}".format(begin, until) 118 params = [] 119 params.append("holder={}".format(holder)) if holder else None 120 params.append("start={}".format(start)) if start else None 121 params.append("limit={}".format(limit)) if limit else None 122 params.append("span={}".format(span)) if span else None 123 params.append("ksort={}".format(ksort)) if ksort else None 124 params.append("order={}".format(order)) if order else None 125 if len(params) > 0: 126 url += "&" + "&".join(params) 127 payload = staff if staff and not holder else None 128 response = api.post(url, payload) 129 # Do some extra checks for empty list case 130 if "status" in response: 131 # We didn't have a 'jobCase' key and returned the outer dict. 132 return [] 133 return response 134 135 @classmethod 136 def auto_assign( 137 cls, 138 type="Recommend", 139 over_allocate_staff=False, 140 include_assigned_task=False, 141 include_started_task=False, 142 skills=None, 143 tasks=None, 144 staffs=None, 145 ): 146 """ 147 Automatically assign a set of staff (real or generic) to a set of tasks 148 using various skill and allocation criteria. 149 150 See [Staff Assign API](https://projectal.com/docs/latest/#tag/Staff-Assign/paths/~1api~1allocation~1staff/post) 151 for full details. 152 """ 153 url = "/api/allocation/staff" 154 payload = { 155 "type": type, 156 "overAllocateStaff": over_allocate_staff, 157 "includeAssignedTask": include_assigned_task, 158 "includeStartedTask": include_started_task, 159 "skillMatchList": skills if skills else [], 160 "staffList": staffs if staffs else [], 161 "taskList": tasks if tasks else [], 162 } 163 return api.post(url, payload) 164 165 @classmethod 166 def create_contract( 167 cls, 168 UUID, 169 payload={}, 170 end_current_contract=False, 171 start_new_contract=False, 172 ): 173 """ 174 Creates a new Contract for a staff, with updated fields from the payload 175 176 end_current: Sets the end date of the current contract to today's date 177 start_new_date: Sets the start date of the new contract to today's date 178 179 See [Staff Clone API](https://projectal.com/docs/latest#tag/Staff/paths/~1api~1staff~1clone/post) 180 for full details. 181 """ 182 183 url = "/api/staff/clone?reference={}&as_contract=true".format(UUID) 184 date_today = datetime.today().strftime("%Y-%m-%d") 185 186 current_staff = None 187 if end_current_contract: 188 # Check if setting end date to today is 189 # invalid for current Staff Contract 190 current_staff = cls.get(UUID) 191 if projectal.timestamp_from_date( 192 current_staff.get("startDate") 193 ) > projectal.timestamp_from_date(date_today): 194 raise UsageException( 195 f"Cannot set endDate before startDate for current contract: {date_today}" 196 ) 197 198 if start_new_contract: 199 payload["startDate"] = date_today 200 payload["endDate"] = DateLimit.Max 201 202 response = api.post(url, payload) 203 204 if end_current_contract and current_staff: 205 current_staff["endDate"] = date_today 206 current_staff.save() 207 208 return response["jobClue"]["uuId"]
Implementation of the Staff API.
44 @classmethod 45 def calendar(cls, uuId, begin=None, until=None): 46 """ 47 Returns the calendar of the staff with `uuId`. 48 49 `begin`: Start date in `yyyy-MM-dd`. 50 51 `until`: End date in `yyyy-MM-dd`. 52 53 54 Optionally specify a date range. If no range specified, the 55 minimum and maximum dates are used (see projectal.enums.DateLimit). 56 """ 57 if begin: 58 begin = datetime.strptime(begin, "%Y-%m-%d").date() 59 if until: 60 until = datetime.strptime(until, "%Y-%m-%d").date() 61 62 url = "/api/staff/{}/calendar?".format(uuId) 63 params = [] 64 params.append("begin={}".format(begin)) if begin else None 65 params.append("until={}".format(until)) if until else None 66 url += "&".join(params) 67 68 cals = api.get(url) 69 cals = [projectal.Calendar(c) for c in cals] 70 return cals
Returns the calendar of the staff with uuId
.
begin
: Start date in yyyy-MM-dd
.
until
: End date in yyyy-MM-dd
.
Optionally specify a date range. If no range specified, the minimum and maximum dates are used (see projectal.enums.DateLimit).
72 @classmethod 73 def calendar_availability(cls, uuId, begin=None, until=None): 74 """ 75 Returns the availability (in hours) of the staff in `uuId` 76 for each day within the specified date range. 77 78 `begin`: Start date in `yyyy-MM-dd`. 79 80 `until`: End date in `yyyy-MM-dd`. 81 82 If no range specified, the minimum and maximum dates are 83 used (see projectal.enums.DateLimit). 84 """ 85 if begin: 86 begin = datetime.strptime(begin, "%Y-%m-%d").date() 87 if until: 88 until = datetime.strptime(until, "%Y-%m-%d").date() 89 90 url = "/api/staff/{}/calendar/availability?".format(uuId) 91 params = [] 92 params.append("begin={}".format(begin)) if begin else None 93 params.append("until={}".format(until)) if until else None 94 url += "&".join(params) 95 96 return api.get(url)
Returns the availability (in hours) of the staff in uuId
for each day within the specified date range.
begin
: Start date in yyyy-MM-dd
.
until
: End date in yyyy-MM-dd
.
If no range specified, the minimum and maximum dates are used (see projectal.enums.DateLimit).
98 @classmethod 99 def usage( 100 cls, 101 begin, 102 until, 103 holder=None, 104 start=None, 105 limit=None, 106 span=None, 107 ksort=None, 108 order=None, 109 staff=None, 110 ): 111 """ 112 Returns the staff-to-task allocations for all staff within the `holder`. 113 114 See [Usage API](https://projectal.com/docs/latest/#tag/Staff/paths/~1api~1staff~1usage/post) 115 for full details. 116 """ 117 url = "/api/staff/usage?begin={}&until={}".format(begin, until) 118 params = [] 119 params.append("holder={}".format(holder)) if holder else None 120 params.append("start={}".format(start)) if start else None 121 params.append("limit={}".format(limit)) if limit else None 122 params.append("span={}".format(span)) if span else None 123 params.append("ksort={}".format(ksort)) if ksort else None 124 params.append("order={}".format(order)) if order else None 125 if len(params) > 0: 126 url += "&" + "&".join(params) 127 payload = staff if staff and not holder else None 128 response = api.post(url, payload) 129 # Do some extra checks for empty list case 130 if "status" in response: 131 # We didn't have a 'jobCase' key and returned the outer dict. 132 return [] 133 return response
Returns the staff-to-task allocations for all staff within the holder
.
See Usage API for full details.
135 @classmethod 136 def auto_assign( 137 cls, 138 type="Recommend", 139 over_allocate_staff=False, 140 include_assigned_task=False, 141 include_started_task=False, 142 skills=None, 143 tasks=None, 144 staffs=None, 145 ): 146 """ 147 Automatically assign a set of staff (real or generic) to a set of tasks 148 using various skill and allocation criteria. 149 150 See [Staff Assign API](https://projectal.com/docs/latest/#tag/Staff-Assign/paths/~1api~1allocation~1staff/post) 151 for full details. 152 """ 153 url = "/api/allocation/staff" 154 payload = { 155 "type": type, 156 "overAllocateStaff": over_allocate_staff, 157 "includeAssignedTask": include_assigned_task, 158 "includeStartedTask": include_started_task, 159 "skillMatchList": skills if skills else [], 160 "staffList": staffs if staffs else [], 161 "taskList": tasks if tasks else [], 162 } 163 return api.post(url, payload)
Automatically assign a set of staff (real or generic) to a set of tasks using various skill and allocation criteria.
See Staff Assign API for full details.
165 @classmethod 166 def create_contract( 167 cls, 168 UUID, 169 payload={}, 170 end_current_contract=False, 171 start_new_contract=False, 172 ): 173 """ 174 Creates a new Contract for a staff, with updated fields from the payload 175 176 end_current: Sets the end date of the current contract to today's date 177 start_new_date: Sets the start date of the new contract to today's date 178 179 See [Staff Clone API](https://projectal.com/docs/latest#tag/Staff/paths/~1api~1staff~1clone/post) 180 for full details. 181 """ 182 183 url = "/api/staff/clone?reference={}&as_contract=true".format(UUID) 184 date_today = datetime.today().strftime("%Y-%m-%d") 185 186 current_staff = None 187 if end_current_contract: 188 # Check if setting end date to today is 189 # invalid for current Staff Contract 190 current_staff = cls.get(UUID) 191 if projectal.timestamp_from_date( 192 current_staff.get("startDate") 193 ) > projectal.timestamp_from_date(date_today): 194 raise UsageException( 195 f"Cannot set endDate before startDate for current contract: {date_today}" 196 ) 197 198 if start_new_contract: 199 payload["startDate"] = date_today 200 payload["endDate"] = DateLimit.Max 201 202 response = api.post(url, payload) 203 204 if end_current_contract and current_staff: 205 current_staff["endDate"] = date_today 206 current_staff.save() 207 208 return response["jobClue"]["uuId"]
Creates a new Contract for a staff, with updated fields from the payload
end_current: Sets the end date of the current contract to today's date start_new_date: Sets the start date of the new contract to today's date
See Staff Clone API for full details.