projectal.linkers
Linkers provide the interface to add/update/delete links between entities. Only certain entities can link to certain other entities. When using this library, your tooling should show you which link methods are available to the Entity subclass you are using.
Note that some links require additional data in the link. You must ensure
the destination object has this data before adding or updating the link
(example below). See the API documentation for exact link details.
Missing data will raise a projectal.errors.ProjectalException
with
information about what is missing.
An instance of an entity class that inherits from a linker is able to link to an instance of the target entity class directly.
# Get task and staff
task = projectal.Task.get('1b21e445-f29a...')
staff = projectal.Staff.get('1b21e445-f29a...')
# Task-to-Staff links require a 'resourceLink'
staff['resourceLink'] = {'utilization': 0.6}
# Task inherits from StaffLinker, so staff linking available
task.link_staff(staff)
1""" 2Linkers provide the interface to add/update/delete links between entities. 3Only certain entities can link to certain other entities. When using this 4library, your tooling should show you which link methods are available 5to the Entity subclass you are using. 6 7Note that some links require additional data in the link. You must ensure 8the destination object has this data before adding or updating the link 9(example below). See the API documentation for exact link details. 10Missing data will raise a `projectal.errors.ProjectalException` with 11information about what is missing. 12 13 14An instance of an entity class that inherits from a linker is able to link 15to an instance of the target entity class directly. 16 17``` 18# Get task and staff 19task = projectal.Task.get('1b21e445-f29a...') 20staff = projectal.Staff.get('1b21e445-f29a...') 21 22# Task-to-Staff links require a 'resourceLink' 23staff['resourceLink'] = {'utilization': 0.6} 24 25# Task inherits from StaffLinker, so staff linking available 26task.link_staff(staff) 27``` 28 29""" 30 31from projectal import api 32 33 34class BaseLinker: 35 # _link_name is the link name (usually the entity name) 36 _link_name = None 37 38 # _link_key is the key within the source object that points to the 39 # links. E.g., 'skillList' 40 _link_key = None 41 42 # _link_data_name is the key within the linked (target) object that points 43 # to a store of custom values within that link. E.g, Skill objects, 44 # when linked, have a 'skillLink' property that holds data about 45 # the link. 46 _link_data_name = None 47 48 # _link_type is the data type of the value in entity[link_key]. This is 49 # usually a list since most links appear as 'skillList', 'staffList', 50 # etc. But some links are single-entity only and appear as dicts like 51 # project[stage] = Stage. 52 _link_type = list 53 54 # _link_entity is the string name (capitalized, like Stage) of the Entity 55 # class within this library that fetched links will be converted to. 56 # This is useful when the name of the list differs from the entity 57 # name. E.g: stage_list needs to be converted to Stage. 58 _link_entity = None 59 60 61class AccessPolicyLinker(BaseLinker): 62 """Subclass can link to Access Policies""" 63 64 _link_name = "access_policy" 65 _link_key = "accessPolicyList" 66 _link_entity = "AccessPolicy" 67 68 def link_access_policy(self, access_policies): 69 self._add_link("access_policy", access_policies) 70 71 def unlink_access_policy(self, access_policies): 72 self._delete_link("access_policy", access_policies) 73 74 75class ActivityLinker(BaseLinker): 76 """Subclass can link to Activities""" 77 78 _link_name = "activity" 79 80 def link_activity(self, activity): 81 self._add_link("activity", activity) 82 83 def unlink_activity(self, activity): 84 self._delete_link("activity", activity) 85 86 87class BookingLinker(BaseLinker): 88 """Subclass can link to Bookings""" 89 90 _link_name = "booking" 91 92 def link_booking(self, booking): 93 self._add_link("booking", booking) 94 95 def unlink_booking(self, booking): 96 self._delete_link("booking", booking) 97 98 99class CompanyLinker(BaseLinker): 100 """Subclass can link to Companies""" 101 102 _link_name = "company" 103 104 def link_company(self, companies): 105 self._add_link("company", companies) 106 107 def unlink_company(self, companies): 108 self._delete_link("company", companies) 109 110 111class ContactLinker(BaseLinker): 112 """Subclass can link to Contacts""" 113 114 _link_name = "contact" 115 116 def link_contact(self, contacts): 117 self._add_link("contact", contacts) 118 119 def unlink_contact(self, contacts): 120 self._delete_link("contact", contacts) 121 122 123class CustomerLinker(BaseLinker): 124 """Subclass can link to Customers""" 125 126 _link_name = "customer" 127 128 def link_customer(self, customers): 129 self._add_link("customer", customers) 130 131 def unlink_customer(self, customers): 132 self._delete_link("customer", customers) 133 134 135class DepartmentLinker(BaseLinker): 136 """Subclass can link to Departments""" 137 138 _link_name = "department" 139 140 def link_department(self, departments): 141 self._add_link("department", departments) 142 143 def unlink_department(self, departments): 144 self._delete_link("department", departments) 145 146 147class FileLinker(BaseLinker): 148 """Subclass can link to Files""" 149 150 _link_name = "file" 151 _link_key = "storageFileList" 152 153 def link_file(self, files): 154 self._add_link("file", files) 155 156 def unlink_file(self, files): 157 self._delete_link("file", files) 158 159 160class FolderLinker(BaseLinker): 161 """Subclass can link to Folders""" 162 163 _link_name = "folder" 164 _link_key = "folders" 165 166 def link_folder(self, folders): 167 self._add_link("folder", folders) 168 169 def unlink_folder(self, folders): 170 self._delete_link("folder", folders) 171 172 173class LocationLinker(BaseLinker): 174 """Subclass can link to Locations""" 175 176 _link_name = "location" 177 178 def link_location(self, locations): 179 self._add_link("location", locations) 180 181 def unlink_location(self, locations): 182 self._delete_link("location", locations) 183 184 185class PermissionLinker(BaseLinker): 186 """Subclass can link to Permissions""" 187 188 _link_name = "permission" 189 190 def link_permission(self, permissions): 191 return self._add_link("permission", permissions) 192 193 def unlink_permission(self, permissions): 194 return self._delete_link("permission", permissions) 195 196 197class ProjectLinker(BaseLinker): 198 """Subclass can link to Projects""" 199 200 _link_name = "project" 201 202 def link_project(self, projects): 203 self._add_link("project", projects) 204 205 def unlink_project(self, projects): 206 self._delete_link("project", projects) 207 208 209class RebateLinker(BaseLinker): 210 """Subclass can link to Rebates""" 211 212 _link_name = "rebate" 213 214 def link_rebate(self, rebates): 215 self._add_link("rebate", rebates) 216 217 def unlink_rebate(self, rebates): 218 self._delete_link("rebate", rebates) 219 220 221class ResourceLinker(BaseLinker): 222 """Subclass can link to Resources""" 223 224 _link_name = "resource" 225 _link_data_name = "resourceLink" 226 227 def link_resource(self, resources): 228 self._add_link("resource", resources) 229 230 def relink_resource(self, resources): 231 self._update_link("resource", resources) 232 233 def unlink_resource(self, resources): 234 self._delete_link("resource", resources) 235 236 237class SkillLinker(BaseLinker): 238 """Subclass can link to Skills""" 239 240 _link_name = "skill" 241 _link_data_name = "skillLink" 242 243 def link_skill(self, skills): 244 self._add_link("skill", skills) 245 246 def relink_skill(self, skills): 247 self._update_link("skill", skills) 248 249 def unlink_skill(self, skills): 250 self._delete_link("skill", skills) 251 252 253class StaffLinker(BaseLinker): 254 """Subclass can link to Staff""" 255 256 _link_name = "staff" 257 _link_data_name = "resourceLink" 258 259 def link_staff(self, staffs): 260 self._add_link("staff", staffs) 261 262 def relink_staff(self, staffs): 263 self._update_link("staff", staffs) 264 265 def unlink_staff(self, staffs): 266 self._delete_link("staff", staffs) 267 268 269class StageLinker(BaseLinker): 270 """Subclass can link to Stages""" 271 272 _link_name = "stage" 273 _link_key = "stage" 274 _link_type = dict 275 276 def link_stage(self, stages): 277 self._add_link("stage", stages) 278 279 def unlink_stage(self, stages): 280 self._delete_link("stage", stages) 281 282 283class StageListLinker(BaseLinker): 284 """Subclass can link to Stage List""" 285 286 _link_name = "stage_list" 287 _link_key = "stageList" 288 _link_entity = "Stage" 289 290 def link_stage_list(self, stages): 291 if not isinstance(stages, list): 292 raise api.UsageException("Stage list link must be a list") 293 self._add_link("stage_list", stages) 294 295 def unlink_stage_list(self, stages): 296 if not isinstance(stages, list): 297 raise api.UsageException("Stage list unlink must be a list") 298 stages = [{"uuId": s["uuId"]} for s in stages] 299 self._delete_link("stage_list", stages) 300 301 302class UserLinker(BaseLinker): 303 _link_name = "user" 304 305 def link_user(self, users): 306 self._add_link("user", users) 307 308 def unlink_user(self, users): 309 self._delete_link("user", users) 310 311 312class TaskLinker(BaseLinker): 313 _link_name = "task" 314 315 def link_task(self, tasks): 316 self._add_link("task", tasks) 317 318 def unlink_task(self, tasks): 319 self._delete_link("task", tasks) 320 321 322class TaskTemplateLinker(BaseLinker): 323 _link_name = "task_template" 324 _link_entity = "TaskTemplate" 325 326 def link_task_template(self, task_templates): 327 self._add_link("task_template", task_templates) 328 329 def unlink_task_template(self, task_templates): 330 self._delete_link("task_template", task_templates) 331 332 333class TagLinker(BaseLinker): 334 _link_name = "tag" 335 336 def link_tag(self, tags): 337 self._add_link("tag", tags) 338 339 def unlink_tag(self, tags): 340 self._delete_link("tag", tags) 341 342 343class NoteLinker(BaseLinker): 344 _link_name = "note" 345 346 347class CalendarLinker(BaseLinker): 348 _link_name = "calendar" 349 350 351# Projects have a list of tasks that we can fetch using the links= 352# method, but they have no linker methods available. 353class TaskInProjectLinker(BaseLinker): 354 _link_name = "task"
35class BaseLinker: 36 # _link_name is the link name (usually the entity name) 37 _link_name = None 38 39 # _link_key is the key within the source object that points to the 40 # links. E.g., 'skillList' 41 _link_key = None 42 43 # _link_data_name is the key within the linked (target) object that points 44 # to a store of custom values within that link. E.g, Skill objects, 45 # when linked, have a 'skillLink' property that holds data about 46 # the link. 47 _link_data_name = None 48 49 # _link_type is the data type of the value in entity[link_key]. This is 50 # usually a list since most links appear as 'skillList', 'staffList', 51 # etc. But some links are single-entity only and appear as dicts like 52 # project[stage] = Stage. 53 _link_type = list 54 55 # _link_entity is the string name (capitalized, like Stage) of the Entity 56 # class within this library that fetched links will be converted to. 57 # This is useful when the name of the list differs from the entity 58 # name. E.g: stage_list needs to be converted to Stage. 59 _link_entity = None
62class AccessPolicyLinker(BaseLinker): 63 """Subclass can link to Access Policies""" 64 65 _link_name = "access_policy" 66 _link_key = "accessPolicyList" 67 _link_entity = "AccessPolicy" 68 69 def link_access_policy(self, access_policies): 70 self._add_link("access_policy", access_policies) 71 72 def unlink_access_policy(self, access_policies): 73 self._delete_link("access_policy", access_policies)
Subclass can link to Access Policies
76class ActivityLinker(BaseLinker): 77 """Subclass can link to Activities""" 78 79 _link_name = "activity" 80 81 def link_activity(self, activity): 82 self._add_link("activity", activity) 83 84 def unlink_activity(self, activity): 85 self._delete_link("activity", activity)
Subclass can link to Activities
88class BookingLinker(BaseLinker): 89 """Subclass can link to Bookings""" 90 91 _link_name = "booking" 92 93 def link_booking(self, booking): 94 self._add_link("booking", booking) 95 96 def unlink_booking(self, booking): 97 self._delete_link("booking", booking)
Subclass can link to Bookings
100class CompanyLinker(BaseLinker): 101 """Subclass can link to Companies""" 102 103 _link_name = "company" 104 105 def link_company(self, companies): 106 self._add_link("company", companies) 107 108 def unlink_company(self, companies): 109 self._delete_link("company", companies)
Subclass can link to Companies
112class ContactLinker(BaseLinker): 113 """Subclass can link to Contacts""" 114 115 _link_name = "contact" 116 117 def link_contact(self, contacts): 118 self._add_link("contact", contacts) 119 120 def unlink_contact(self, contacts): 121 self._delete_link("contact", contacts)
Subclass can link to Contacts
124class CustomerLinker(BaseLinker): 125 """Subclass can link to Customers""" 126 127 _link_name = "customer" 128 129 def link_customer(self, customers): 130 self._add_link("customer", customers) 131 132 def unlink_customer(self, customers): 133 self._delete_link("customer", customers)
Subclass can link to Customers
136class DepartmentLinker(BaseLinker): 137 """Subclass can link to Departments""" 138 139 _link_name = "department" 140 141 def link_department(self, departments): 142 self._add_link("department", departments) 143 144 def unlink_department(self, departments): 145 self._delete_link("department", departments)
Subclass can link to Departments
148class FileLinker(BaseLinker): 149 """Subclass can link to Files""" 150 151 _link_name = "file" 152 _link_key = "storageFileList" 153 154 def link_file(self, files): 155 self._add_link("file", files) 156 157 def unlink_file(self, files): 158 self._delete_link("file", files)
Subclass can link to Files
161class FolderLinker(BaseLinker): 162 """Subclass can link to Folders""" 163 164 _link_name = "folder" 165 _link_key = "folders" 166 167 def link_folder(self, folders): 168 self._add_link("folder", folders) 169 170 def unlink_folder(self, folders): 171 self._delete_link("folder", folders)
Subclass can link to Folders
174class LocationLinker(BaseLinker): 175 """Subclass can link to Locations""" 176 177 _link_name = "location" 178 179 def link_location(self, locations): 180 self._add_link("location", locations) 181 182 def unlink_location(self, locations): 183 self._delete_link("location", locations)
Subclass can link to Locations
186class PermissionLinker(BaseLinker): 187 """Subclass can link to Permissions""" 188 189 _link_name = "permission" 190 191 def link_permission(self, permissions): 192 return self._add_link("permission", permissions) 193 194 def unlink_permission(self, permissions): 195 return self._delete_link("permission", permissions)
Subclass can link to Permissions
198class ProjectLinker(BaseLinker): 199 """Subclass can link to Projects""" 200 201 _link_name = "project" 202 203 def link_project(self, projects): 204 self._add_link("project", projects) 205 206 def unlink_project(self, projects): 207 self._delete_link("project", projects)
Subclass can link to Projects
210class RebateLinker(BaseLinker): 211 """Subclass can link to Rebates""" 212 213 _link_name = "rebate" 214 215 def link_rebate(self, rebates): 216 self._add_link("rebate", rebates) 217 218 def unlink_rebate(self, rebates): 219 self._delete_link("rebate", rebates)
Subclass can link to Rebates
222class ResourceLinker(BaseLinker): 223 """Subclass can link to Resources""" 224 225 _link_name = "resource" 226 _link_data_name = "resourceLink" 227 228 def link_resource(self, resources): 229 self._add_link("resource", resources) 230 231 def relink_resource(self, resources): 232 self._update_link("resource", resources) 233 234 def unlink_resource(self, resources): 235 self._delete_link("resource", resources)
Subclass can link to Resources
238class SkillLinker(BaseLinker): 239 """Subclass can link to Skills""" 240 241 _link_name = "skill" 242 _link_data_name = "skillLink" 243 244 def link_skill(self, skills): 245 self._add_link("skill", skills) 246 247 def relink_skill(self, skills): 248 self._update_link("skill", skills) 249 250 def unlink_skill(self, skills): 251 self._delete_link("skill", skills)
Subclass can link to Skills
254class StaffLinker(BaseLinker): 255 """Subclass can link to Staff""" 256 257 _link_name = "staff" 258 _link_data_name = "resourceLink" 259 260 def link_staff(self, staffs): 261 self._add_link("staff", staffs) 262 263 def relink_staff(self, staffs): 264 self._update_link("staff", staffs) 265 266 def unlink_staff(self, staffs): 267 self._delete_link("staff", staffs)
Subclass can link to Staff
270class StageLinker(BaseLinker): 271 """Subclass can link to Stages""" 272 273 _link_name = "stage" 274 _link_key = "stage" 275 _link_type = dict 276 277 def link_stage(self, stages): 278 self._add_link("stage", stages) 279 280 def unlink_stage(self, stages): 281 self._delete_link("stage", stages)
Subclass can link to Stages
284class StageListLinker(BaseLinker): 285 """Subclass can link to Stage List""" 286 287 _link_name = "stage_list" 288 _link_key = "stageList" 289 _link_entity = "Stage" 290 291 def link_stage_list(self, stages): 292 if not isinstance(stages, list): 293 raise api.UsageException("Stage list link must be a list") 294 self._add_link("stage_list", stages) 295 296 def unlink_stage_list(self, stages): 297 if not isinstance(stages, list): 298 raise api.UsageException("Stage list unlink must be a list") 299 stages = [{"uuId": s["uuId"]} for s in stages] 300 self._delete_link("stage_list", stages)
Subclass can link to Stage List
323class TaskTemplateLinker(BaseLinker): 324 _link_name = "task_template" 325 _link_entity = "TaskTemplate" 326 327 def link_task_template(self, task_templates): 328 self._add_link("task_template", task_templates) 329 330 def unlink_task_template(self, task_templates): 331 self._delete_link("task_template", task_templates)