plankapy

  1import requests
  2import json
  3
  4API_URL = "http://localhost:3000"
  5API_USER = "demo- demo.demo"
  6API_PASS = "demo"
  7OFFSET = 65535
  8
  9class Planka:
 10    """API wrapper class for Planka
 11    - url: URL of Planka instance
 12    - username: Username of Planka user
 13    - password: Password of Planka user
 14    """
 15    def __init__(self, url:str, username:str, password:str, templates="config/templates.json"):
 16        self.url = url
 17        self.username = username
 18        self.password = password
 19        self.auth = None
 20        with open(templates) as f:
 21            self.templates = json.load(f)
 22        self.authenticate()
 23    
 24    def __repr__(self):
 25        return f"<{type(self).__name__}:\n\tBase URL: {self.url}\n\tLogin User: {self.username}\n\tLogin Pass: {self.password}\n\tAPI Token: {self.auth}\n>"
 26
 27    def deauthenticate(self) -> bool:
 28        """Deletes the auth token from the Planka API
 29        - **return:** True if successful, False if not
 30        """
 31        try:
 32            self.request("DELETE", "/api/access-tokens/me")
 33            self.auth = None
 34            return True
 35        except:
 36            raise InvalidToken(f"No active access token assigned to this instance\n{self.__repr__()}")
 37
 38    def validate(self) -> bool:
 39        """Validates the Planka API connection
 40        - **return:** True if successful, False if not
 41        """
 42        try:
 43            self.request("GET", "/*")
 44            return True
 45        except:
 46            raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
 47
 48    def authenticate(self) -> bool:
 49        """Gets an auth token from the Planka API
 50        - **return:** True if successful, False if not
 51        """
 52        try:
 53            request = requests.post(f"{self.url}/api/access-tokens", data={'emailOrUsername': self.username, 'password': self.password})
 54            self.auth = request.json()['item']
 55            if not self.auth:
 56                raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
 57            return True
 58        except:
 59            raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
 60
 61    def request(self, method:str, endpoint:str, data:dict=None) -> dict:
 62        """Makes a request to the Planka API
 63        - method: HTTP method
 64        - endpoint: API endpoint
 65        - data: Data to send with request (default: None)
 66        - **return:** JSON response from Planka API
 67        """
 68        if not self.auth:
 69            self.authenticate()
 70        headers = \
 71            { 
 72            "Content-Type": "application/json",
 73            "Authorization": f"Bearer {self.auth}"
 74            }
 75        url = f"{self.url}{endpoint}"
 76        response = requests.request(method, url, headers=headers, json=data)
 77
 78        if response.status_code == 401:
 79            raise InvalidToken("Invalid API credentials")
 80
 81        if response.status_code not in [200, 201]:
 82            raise InvalidToken(f"Failed to {method} {url} with status code {response.status_code}")
 83
 84        try:
 85            return response.json()
 86        except:
 87            raise InvalidToken(f"Failed to parse response from {url}")
 88    
 89    def get_template(self, template:str) -> dict:
 90        """Returns a template from the templates.json file
 91        - template: Name of template to return
 92        - **return:** Template dictionary
 93        """
 94        try:
 95            return self.templates[template]
 96        except:
 97            raise InvalidToken(f"Template not found: {template}")
 98        
 99class Controller():
100    def __init__(self, instance:Planka) -> None:
101        """Controller class for Planka API
102        - instance: Planka API instance
103        """
104        self.instance = instance
105        self.template:dict = None
106        self.data:dict = None
107        self.response:dict = None
108
109    def __str__(self) -> str:
110        """Returns a string representation of the controller object
111        - **return:** String representation of controller object
112        """
113        return f"{type(self).__name__}:\n{json.dumps(self.data, sort_keys=True, indent=4)}"
114
115    def __repr__(self) -> str:
116        """Returns a string representation of the controller object
117        - **return:** String representation of controller object
118        """
119        return f"<{type(self).__name__}({self.__class__.__bases__[0].__name__})>{self.__str__()}"
120
121    def build(self, **kwargs) -> dict:
122        """Builds the controller data
123        - **return:** Controller data dictionary
124        """
125        if not kwargs:
126            return kwargs
127        valid_keys = self.template.keys()
128        data = {key: value for key, value in kwargs.items() if key in valid_keys}
129        self.data = data
130        return self.data
131
132    def create(self, route:str, data:dict=None) -> dict:
133        """Creates a new controller object (POST)
134        - route: Route for controller object POST request
135        - **return:** POST response dictionary
136        """
137        if not data:
138            data = self.data
139        if not data:
140            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
141        self.response = self.instance.request("POST", route, data)
142        return self.response
143
144    def get(self, route:str) -> dict:
145        """Gets a controller object (GET)
146        - route: Route for controller object GET request
147        - **return:** GET response dictionary
148        """
149        return self.instance.request("GET", route)
150
151    def update(self, route:str, data:dict=None) -> dict:
152        """Updates a controller object (PATCH)
153        - route: Route for controller object PATCH request
154        - oid: ID of controller object
155        - **return:** PATCH response dictionary
156        """
157        if not data:
158            data = self.data
159        if not self.data:
160            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
161        self.response = self.instance.request("PATCH", route, data=data)
162        return self.response
163
164    def delete(self, route:str) -> dict: 
165        """Deletes a controller object (DELETE)
166        - route: Route for controller object DELETE request
167        - oid: ID of controller object
168        - **return:** DELETE response dictionary
169        """
170        return self.instance.request("DELETE", route)
171    
172    def last_response(self) -> dict:
173        """Returns the last response from the controller object
174        - **return:** Last response dictionary
175        """
176        return self.response
177
178class Project(Controller):
179    def __init__(self, instance:Planka, **kwargs) -> None:
180        self.instance = instance
181        self.template = instance.get_template("project")
182        self.data = self.build(**kwargs)
183
184    def get(self, name:str=None, oid:str=None) -> dict:
185        """Gets a project by name
186        - oid: ID of project to get (optional)
187        - name: Name of project if None returns all projects
188        - **return:** GET response dictionary
189        """
190        if oid:
191            return super().get(f"/api/projects/{oid}")
192        prjs = super().get("/api/projects")
193        if not name:
194            return prjs
195        prj_names = [prj["name"] for prj in prjs["items"]]
196        if name not in prj_names:
197            raise InvalidToken(f"Project {name} not found")
198        prj_id = [prj for prj in prjs["items"] if prj["name"] == name][0]["id"]
199        return super().get(f"/api/projects/{prj_id}")
200
201    def get_project_names(self) -> list:
202        """Gets a list of project names
203        - **return:** List of project names
204        """
205        return [prj["name"] for prj in self.get()['items']]
206    
207    def create(self) -> dict:
208        """Creates a new project
209        - **return:** POST response dictionary
210        """
211        if not self.data:
212            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
213        if self.data["name"] in [prj["name"] for prj in self.get()['items']]:
214            raise InvalidToken(f"Project {self.data['name']} already exists")
215        return super().create("/api/projects")
216    
217    def update(self, name:str) -> dict:
218        """Updates a project
219        - name: Name of project to update
220        - **return:** PATCH response dictionary
221        """
222        prj_id = prj_id = self.get(name)['item']['id']
223        return super().update(f"/api/projects/{prj_id}")
224
225    def delete(self, name:str) -> dict:
226        """Deletes a project
227        - name: Name of project to delete
228        - **return:** DELETE response dictionary
229        """
230        prj_id = self.get(name)['item']['id']
231        return super().delete(f"/api/projects/{prj_id}")
232
233class Board(Controller):
234    def __init__(self, instance:Planka, **kwargs) -> None:
235        self.instance = instance
236        self.template = instance.get_template("board")
237        self.data = self.build(**kwargs)
238    
239    def get(self, project_name:str=None, board_name:str=None, oid:str=None) -> dict:
240        """Gets a board by name
241        - oid: ID of board to get (optonal)
242        - name: Name of board if None returns all boards
243        - project_name: Name of project to get boards from
244        - **return:** GET response dictionary
245        """
246        if oid:
247            return super().get(f"/api/boards/{oid}")
248        if not (project_name):
249            raise InvalidToken("Please provide a project name")
250        prj_con = Project(self.instance)
251        prj = prj_con.get(project_name)
252        boards = prj["included"]["boards"]
253        if not board_name:
254            return boards
255        board_names = [board["name"] for board in boards]
256        if board_name not in board_names:
257            raise InvalidToken(f"Board `{board_name}` not found")
258        board_id = [board for board in boards if board["name"] == board_name][0]["id"]
259        return super().get(f"/api/boards/{board_id}")
260    
261    def create(self, project_name:str) -> dict:
262        """Creates a new board
263        - prj_name: Name of project to create board in
264        - **return:** POST response dictionary
265        """
266        if not self.data:
267            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
268        prj_con = Project(self.instance)
269        prj_id = prj_con.get(project_name)['item']['id']
270        return super().create(f"/api/projects/{prj_id}/boards")
271    
272    def update(self, project_name:str=None, board_name:str=None, data:dict=None, oid:str=None) -> dict:
273        """Updates a board
274        - oid: ID of board to update (optional)
275        - project_name: Name of project to update board in
276        - board_name: Name of board to update
277        - **return:** PATCH response dictionary
278        """
279        if not data:
280            data = self.data
281        if not data:
282            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
283        if oid:
284            return super().update(f"/api/boards/{oid}", data=data)
285        if not (project_name and board_name):
286            raise InvalidToken("Please provide project and board names")
287        board_id = self.get(project_name, board_name)['item']['id']
288        return super().update(f"/api/boards/{board_id}", data=self.data)
289    
290    def delete(self, project_name:str=None, board_name:str=None, oid:str=None):
291        """Deletes a board
292        - oid: ID of board to delete (optional)
293        - project_name: Name of project to delete board in
294        - board_name: Name of board to delete
295        - **return:** DELETE response dictionary
296        """
297        if oid:
298            return super().delete(f"/api/boards/{oid}")
299        if not project_name:
300            raise InvalidToken("Please provide a project name")
301        if not board_name:
302            raise InvalidToken("Please provide a board name")
303        board_id = self.get(project_name, board_name)['item']['id']
304        return super().delete(f"/api/boards/{board_id}")
305
306class List(Controller):
307    def __init__(self, instance:Planka, **kwargs) -> None:
308        self.instance = instance
309        self.template = instance.get_template("list")
310        self.data = self.build(**kwargs)
311
312    def get(self, project_name:str=None, board_name:str=None, list_name:str=None):
313        """Gets a list by name
314        NOTE: No GET route for list by ID
315        - project_name: Name of project to get list from
316        - board_name: Name of board to get list from
317        - list_name: Name of list to get
318        - **return:** GET response dictionary
319        """
320        if not (project_name and board_name):
321            raise InvalidToken("Please provide project and board names")
322        board_con = Board(self.instance)
323        board = board_con.get(project_name, board_name)
324        lists = board["included"]["lists"]
325        list_names = [lst["name"] for lst in lists]
326        if not list_name:
327            return lists
328        if list_name not in list_names:
329            raise InvalidToken(f"List `{list_name}` not found")
330        return [lst for lst in lists if lst["name"] == list_name][0]
331    
332    def create(self, project_name:str=None, board_name:str=None, data:dict=None):
333        """Creates a new list
334        - project_name: Name of project to create list in
335        - board_name: Name of board to create list in
336        - **return:** POST response dictionary
337        """
338        if not data:
339            data = self.data
340        if not data:
341            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
342        if not (project_name and board_name):
343            raise InvalidToken("Please provide project and board name")
344        board_con = Board(self.instance)
345        board_id = board_con.get(project_name, board_name)['item']['id']
346        return super().create(f"/api/boards/{board_id}/lists")
347    
348    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None, oid:str=None):
349        """Updates a list
350        - oid: ID of list to update (optional)
351        - project_name: Name of project to update list in
352        - board_name: Name of board to update list in
353        - list_name: Name of list to update
354        - **return:** PATCH response dictionary
355        """        
356        if not data:
357            data = self.data
358        if not data:
359            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
360        if oid:
361            return super().update(f"/api/lists/{oid}", data=data)
362        if not (project_name and board_name and list_name):
363            raise InvalidToken("Please provide project, board, and list names")
364        lst = self.get(project_name, board_name, list_name)
365        return super().update(f"/api/lists/{lst['id']}", data=data)
366    
367    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, oid:str=None):
368        """Deletes a list
369        - oid: ID of list to delete (optional)
370        - project_name: Name of project to delete list in
371        - board_name: Name of board to delete list in
372        - list_name: Name of list to delete
373        - **return:** DELETE response dictionary
374        """
375        if oid:
376            return super().delete(f"/api/lists/{oid}")
377        if not (project_name and board_name and list_name):
378            raise InvalidToken("Please provide a project, board, and list names")
379        lst = self.get(project_name, board_name, list_name)
380        return super().delete(f"/api/lists/{lst['id']}")
381
382class Card(Controller):
383    def __init__(self, instance:Planka, **kwargs) -> None:
384        self.instance = instance
385        self.template = instance.get_template("card")
386        self.data = self.build(**kwargs)
387
388    def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
389        """Gets a card by name
390        - oid: ID of card to get (optional)
391        - project_name: Name of project to get card from
392        - board_name: Name of board to get card from
393        - list_name: Name of list to get card from
394        - card_name: Name of card to get
395        - **return:** GET response dictionary
396        """
397        if oid != None:
398            return super().get(f"/api/cards/{oid}")
399        if not (project_name and board_name and list_name):
400            raise InvalidToken("Please provide project, board, and list names")
401        board_con = Board(self.instance)
402        board = board_con.get(project_name, board_name)
403        lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
404        cards = [card for card in board["included"]["cards"] if card["listId"] == lst_id]
405        card_names = [card["name"] for card in cards]
406        if not card_name:
407            return [self.get(oid=card["id"]) for card in cards]
408        if card_name not in card_names:
409            raise InvalidToken(f"Card `{card_name}` not found")
410        card_id = [card for card in cards if card["name"] == card_name][0]['id']
411        return super().get(f"/api/cards/{card_id}")
412    
413    def create(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None):
414        """Creates a new card
415        - project_name: Name of project to create card in
416        - board_name: Name of board to create card in
417        - list_name: Name of list to create card in
418        - **return:** POST response dictionary
419        """
420        if not data:
421            data = self.data
422        if not data:
423            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
424        if not (project_name and board_name and list_name):
425            raise InvalidToken("Please provide a project, board and list names")
426        board_con = Board(self.instance)
427        board = board_con.get(project_name, board_name)
428        lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
429        return super().create(f"/api/lists/{lst_id}/cards")
430
431    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
432        """Deletes a card
433        - oid: ID of card to delete (optional)
434        - project_name: Name of project to delete card in
435        - board_name: Name of board to delete card in
436        - list_name: Name of list to delete card in
437        - card_name: Name of card to delete
438        - **return:** DELETE response dictionary
439        """
440        if oid != None:
441            return super().delete(f"/api/cards/{oid}")
442        if not (project_name and board_name and list_name and card_name):
443            raise InvalidToken("Please provide a project, board, list, and card name")
444        card = self.get(project_name, board_name, list_name, card_name)
445        return super().delete(f"/api/cards/{card['id']}")
446    
447    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, data:dict=None, oid:str=None):
448        """Updates a card
449        - oid: ID of card to update (optional)
450        - project_name: Name of project to update card in
451        - board_name: Name of board to update card in
452        - list_name: Name of list to update card in
453        - card_name: Name of card to update
454        - **return:** PATCH response dictionary
455        """
456        if not data:
457            data = self.data
458        if not data:
459            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
460        if oid:
461            return super().update(f"/api/cards/{oid}", data=data)
462        if not (project_name and board_name and list_name and card_name):
463            raise InvalidToken("Please provide a project, board, list, and card name")
464        card = self.get(project_name, board_name, list_name, card_name)
465        return super().update(f"/api/cards/{card['id']}", data=data)
466    
467    def get_labels(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
468        """Gets labels for a card
469        - oid: ID of card to get labels from (optional)
470        - project_name: Name of project to get card from
471        - board_name: Name of board to get card from
472        - list_name: Name of list to get card from
473        - card_name: Name of card to get
474        - **return:** GET response dictionary
475        """
476        if oid:
477            return self.get(oid=oid)['included']['cardLabels']
478        if not (project_name and board_name and list_name and card_name):
479            raise InvalidToken("Please provide project, board, list, and card names")
480        card_id = self.get(project_name, board_name, list_name, card_name)['item']['id']
481        return self.get(oid=card_id)['included']['cardLabels']
482    
483class Label(Controller):
484    def __init__(self, instance:Planka, **kwargs) -> None:
485        self.instance = instance
486        self.template = instance.get_template("label")
487        self.options = instance.get_template("colors")
488        self.data = self.build(**kwargs)
489
490    def colors(self) -> list:
491        return self.options
492    
493    def get(self, project_name:str=None, board_name:str=None, label_name:str=None) -> dict:
494        """Gets a label by name
495        - project_name: Name of project to get label from
496        - board_name: Name of board to get label from
497        - label_name: Name of label to get
498        - **return:** GET response dictionary
499        """
500        if not (project_name and board_name):
501            raise InvalidToken("Please provide project and board names")
502        board_con = Board(self.instance)
503        board = board_con.get(project_name, board_name)
504        labels = board["included"]["labels"]
505        label_names = [label["name"] for label in labels]
506        if not label_name:
507            return labels
508        if label_name not in label_names:
509            raise InvalidToken(f"Label `{label_name}` not found")
510        return [label for label in labels if label["name"] == label_name][0]
511    
512    def create(self, project_name:str=None, board_name:str=None, data:dict=None):
513        """Creates a new label
514        - project_name: Name of project to create label in
515        - board_name: Name of board to create label in
516        - **return:** POST response dictionary
517        """
518        if not data:
519            data = self.data
520        if not data:
521            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
522        if not (project_name and board_name):
523            raise InvalidToken("Please provide project and board names")
524        board_con = Board(self.instance)
525        board = board_con.get(project_name, board_name)['item']
526        return super().create(f"/api/boards/{board['id']}/labels")
527    
528    def delete(self, project_name:str=None, board_name:str=None, label_name:str=None, oid:str=None):
529        """Deletes a label
530        - oid: ID of label to delete (optional)
531        - project_name: Name of project to delete label from
532        - board_name: Name of board to delete label from
533        - label_name: Name of label to delete
534        - **return:** DELETE response dictionary
535        """
536        if oid:
537            return super().delete(f"/api/labels/{oid}")
538        if not (project_name and board_name and label_name):
539            raise InvalidToken("Please provide project, board, and label names")
540        label = self.get(project_name, board_name, label_name)
541        return super().delete(f"/api/labels/{label['id']}")
542    
543    def add(self, project_name:str=None, board_name:str=None, list_name:str=None ,card_name:str=None, label_name:str=None, card_id:str=None, label_id:str=None):
544        """Adds a label to a card
545        - project_name: Name of project to add label to card in
546        - board_name: Name of board to add label to card in
547        - label_name: Name of label to add to card
548        - card_name: Name of card to add label to
549        - list_name: Name of list to add label to card in
550        - **return:** POST response dictionary
551        """
552        if label_id and card_id:
553            return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label_id})
554        if not (project_name and board_name and label_name):
555            raise InvalidToken("Please provide a project, board, label name")
556        if card_id:
557            label = self.get(project_name, board_name, label_name)
558            return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label['item']['id']})
559        if not (card_name and list_name):
560            raise InvalidToken("Please provide a card and list name")
561        card_con = Card(self.instance)
562        card = card_con.get(project_name, board_name, list_name, card_name)
563        label = self.get(project_name, board_name, label_name)
564        return super().create(f"/api/cards/{card['item']['id']}/labels", {"labelId":label['item']['id']})
565
566    def remove(self, project_name:str=None, board_name:str=None, list_name:str=None ,card_name:str=None, label_name:str=None, card_id:str=None, label_id:str=None):
567        """Removes a label from a card
568        - project_name: Name of project to remove label from card in
569        - board_name: Name of board to remove label from card in
570        - label_name: Name of label to remove from card
571        - card_name: Name of card to remove label from
572        - list_name: Name of list to remove label from card in
573        - **return:** DELETE response dictionary
574        """
575        if label_id and card_id:
576            return super().delete(f"/api/cards/{card_id}/labels/{label_id}")
577        if not (project_name and board_name and label_name):
578            raise InvalidToken("Please provide a project, board, label name")
579        if card_id:
580            label_id = [label['id'] for label in Card(self.instance).get_labels(oid=card_id) if label['name'] == label_name][0]
581            return super().delete(f"/api/cards/{card_id}/labels/{label['item']['id']}")
582        if not (card_name and list_name):
583            raise InvalidToken("Please provide a card and list name")
584        card_con = Card(self.instance)
585        card = card_con.get(project_name, board_name, list_name, card_name)
586        label = self.get(project_name, board_name, label_name)
587        return super().delete(f"/api/cards/{card['item']['id']}/labels/{label['item']['id']}")
588
589class Task(Controller):
590    def __init__(self, instance:Planka, **kwargs) -> None:
591        self.instance = instance
592        self.template = instance.get_template("task")
593        self.data = self.build(**kwargs)
594    
595    def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None) -> dict:
596        """Gets a task by name
597        NOTE: No GET route for tasks by OID
598        - project_name: Name of project to get task from
599        - board_name: Name of board to get task from
600        - list_name: Name of list to get task from
601        - card_name: Name of card to get task from
602        - task_name: Name of task to get
603        - **return:** GET response dictionary
604        """
605        if not (project_name and board_name and list_name and card_name):
606            raise InvalidToken("Please provide project, board, list, and card names")
607        board_con = Board(self.instance)
608        board = board_con.get(project_name, board_name)
609        list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
610        cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
611        card_id = [card for card in cards if card["name"] == card_name][0]["id"]
612        tasks = [task for task in board["included"]["tasks"] if task["cardId"] == card_id]
613        task_names = [task["name"] for task in tasks]
614        if not task_name:
615            return tasks
616        if task_name not in task_names:
617            raise InvalidToken(f"Task `{task_name}` not found")
618        return [task for task in tasks if task["name"] == task_name][0]
619    
620    def create(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, data:dict=None, card_id:str=None):
621        """Creates a new task
622        - card_id: ID of card to create task in (optional)
623        - project_name: Name of project to create task in
624        - board_name: Name of board to create task in
625        - list_name: Name of list to create task in
626        - card_name: Name of card to create task in
627        - **return:** POST response dictionary
628        """
629        if not data:
630            data = self.data
631        if not data:
632            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
633        if card_id:
634            return super().create(f"/api/cards/{card_id}/tasks")
635        if not (project_name and board_name and list_name and card_name):
636            raise InvalidToken("Please provide project, board, list, and card names")
637        board_con = Board(self.instance)
638        board = board_con.get(project_name, board_name)
639        list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
640        cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
641        card_id = [card for card in cards if card["name"] == card_name][0]["id"]
642        return super().create(f"/api/cards/{card_id}/tasks")
643    
644    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None, data:dict=None, oid:str=None):
645        """Updates a task
646        - oid: Object ID of task to update (optional)
647        - project_name: Name of project to update task in
648        - board_name: Name of board to update task in
649        - list_name: Name of list to update task in
650        - card_name: Name of card to update task in
651        - task_name: Name of task to update
652        - **return:** PATCH response dictionary
653        """
654        if not data:
655            data = self.data
656        if not data:
657            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
658        if oid:
659            return super().update(f"/api/tasks/{oid}")
660        if not (project_name and board_name and list_name and card_name and task_name):
661            raise InvalidToken("Please provide project, board, list, card, and task names")
662        task = self.get(project_name, board_name, list_name, card_name, task_name)
663        return super().update(f"/api/tasks/{task['id']}")
664       
665    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None, oid:str=None):
666        """Deletes a task
667        - oid: ID of task to delete (Use this if you already have the ID)
668        - project_name: Name of project to delete task from
669        - board_name: Name of board to delete task from
670        - list_name: Name of list to delete task from
671        - card_name: Name of card to delete task from
672        - task_name: Name of task to delete
673        - **return:** DELETE response dictionary
674        """
675        if oid:
676            return super().delete(f"/api/tasks/{id}")
677        if not (project_name and board_name and list_name and card_name and task_name):
678            raise InvalidToken("Please provide project, board, list, card, and task names")
679        task = self.get(project_name, board_name, list_name, card_name, task_name)
680        return super().delete(f"/api/tasks/{task['id']}")
681
682class Attachment(Controller):
683    def __init__(self, instance:Planka, **kwargs) -> None:
684        self.instance = instance
685        self.template = instance.get_template("attachment")
686        self.data = self.build(**kwargs)
687
688class Stopwatch(Controller):
689    def __init__(self, instance:Planka, **kwargs) -> None:
690        self.instance = instance
691        self.template = instance.get_template("stopwatch")
692        self.data = self.build(**kwargs)
693
694class Background(Controller):
695    def __init__(self, instance:Planka, **kwargs) -> None:
696        self.instance = instance
697        self.template = instance.get_template("background")
698        self.options = instance.get_template("gradients")
699        self.data = self.build(**kwargs)
700
701    def gradients(self) -> dict:
702        """Gets all gradients
703        - **return:** GET response dictionary
704        """
705        return self.options
706
707    def apply(self, prj_name:str):
708        """Applies a gradient to a project
709        - project: Name of project to apply gradient to
710        - **return:** PATCH response dictionary
711        """
712        project = Project(self.instance)
713        prj_id = project.get(prj_name)["item"]["id"]
714        if "type" not in self.data.keys():
715            raise InvalidToken("Please specify a background type: `gradient` | `image`")
716        if self.data["type"] == "gradient" and self.data["name"] not in self.options:
717            raise InvalidToken(f"Gradient {self.data['name']} not found: please choose from\n{self.options}")
718        return super().update(f"/api/projects/{prj_id}", data={"background": self.data})
719    
720    def clear(self, prj_name:str):
721        """Clears a gradient from a project
722        - project: Name of project to clear gradient from
723        - **return:** PATCH response dictionary
724        """
725        project = Project(self.instance)
726        prj_id = project.get(prj_name)["item"]["id"]
727        return super().update(f"/api/projects/{prj_id}", data={"background": None})
728
729class Comment(Controller):
730    def __init__(self, instance:Planka, **kwargs) -> None:
731        self.instance = instance
732        self.template = instance.get_template("comment-action")
733        self.data = self.build(**kwargs)
734
735class User(Controller):
736    def __init__(self, instance:Planka, **kwargs) -> None:
737        """Creates a user
738        - username: Username of user to create
739        - name: Display name of user to create
740        - password: Password of user to create
741        - email: Email of user to create
742        - subscribe: Subscibe user to own cards (default: False)
743        - organization: Organization of user to create (default: None)
744        - admin: Admin state of user to create (default: False)
745        """
746        self.instance = instance
747        self.template = instance.get_template("user")
748        self.data = self.build(**kwargs)
749
750    def get(self, username:str=None):
751        """Gets a user
752        - username: Username of user to get (all if not provided)
753        - **return:** GET response dictionary
754        """
755        if not username:
756            return super().get("/api/users")["items"]
757        users = super().get("/api/users")["items"]
758        names = [user["username"] for user in users]
759        if username not in names:
760            raise InvalidToken(f"User {username} not found")
761        return users[names.index(username)]
762    
763    def create(self, data:dict=None):
764        """Creates a user
765        - data: Data dictionary to create user with (optional)
766        - **return:** POST response dictionary
767        """
768        if not data:
769            data = self.data
770        if not data:
771            raise InvalidToken("Please either build a user or provide a data dictionary")
772        if self.data["username"] in [user["username"] for user in self.get()]:
773            raise InvalidToken(f"User {self.data['username']} already exists")
774        return super().create("/api/users", data=self.data)
775    
776    def delete(self, username:str, oid:str=None):
777        """Deletes a user
778        - username: Username of user to delete
779        - oid: ID of user to delete (Use this if you already have the ID)
780        - **return:** DELETE response dictionary
781        """
782        if oid:
783            return super().delete(f"/api/users/{oid}")
784        if username not in [user["username"] for user in self.get()]:
785            raise InvalidToken(f"User {username} not found")
786        return super().delete(f"/api/users/{self.get(username)['id']}")
787    
788    def update(self, username:str, oid:str=None, data:dict=None):
789        """Updates a user
790        - username: Username of user to update
791        - oid: ID of user to update (Use this if you already have the ID)
792        - data: Data dictionary to update user with (optional)
793        - **return:** PATCH response dictionary
794        """
795        user = self.get(username)
796        if not data:
797            data = self.data
798        if not data:
799            raise InvalidToken("Please either build a user or provide a data dictionary")
800        return super().update(f"/api/users/{user['id']}", data=data)
801    
802class InvalidToken(Exception):
803    """General Error for invalid API inputs
804    """
805    pass
class Planka:
10class Planka:
11    """API wrapper class for Planka
12    - url: URL of Planka instance
13    - username: Username of Planka user
14    - password: Password of Planka user
15    """
16    def __init__(self, url:str, username:str, password:str, templates="config/templates.json"):
17        self.url = url
18        self.username = username
19        self.password = password
20        self.auth = None
21        with open(templates) as f:
22            self.templates = json.load(f)
23        self.authenticate()
24    
25    def __repr__(self):
26        return f"<{type(self).__name__}:\n\tBase URL: {self.url}\n\tLogin User: {self.username}\n\tLogin Pass: {self.password}\n\tAPI Token: {self.auth}\n>"
27
28    def deauthenticate(self) -> bool:
29        """Deletes the auth token from the Planka API
30        - **return:** True if successful, False if not
31        """
32        try:
33            self.request("DELETE", "/api/access-tokens/me")
34            self.auth = None
35            return True
36        except:
37            raise InvalidToken(f"No active access token assigned to this instance\n{self.__repr__()}")
38
39    def validate(self) -> bool:
40        """Validates the Planka API connection
41        - **return:** True if successful, False if not
42        """
43        try:
44            self.request("GET", "/*")
45            return True
46        except:
47            raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
48
49    def authenticate(self) -> bool:
50        """Gets an auth token from the Planka API
51        - **return:** True if successful, False if not
52        """
53        try:
54            request = requests.post(f"{self.url}/api/access-tokens", data={'emailOrUsername': self.username, 'password': self.password})
55            self.auth = request.json()['item']
56            if not self.auth:
57                raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
58            return True
59        except:
60            raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
61
62    def request(self, method:str, endpoint:str, data:dict=None) -> dict:
63        """Makes a request to the Planka API
64        - method: HTTP method
65        - endpoint: API endpoint
66        - data: Data to send with request (default: None)
67        - **return:** JSON response from Planka API
68        """
69        if not self.auth:
70            self.authenticate()
71        headers = \
72            { 
73            "Content-Type": "application/json",
74            "Authorization": f"Bearer {self.auth}"
75            }
76        url = f"{self.url}{endpoint}"
77        response = requests.request(method, url, headers=headers, json=data)
78
79        if response.status_code == 401:
80            raise InvalidToken("Invalid API credentials")
81
82        if response.status_code not in [200, 201]:
83            raise InvalidToken(f"Failed to {method} {url} with status code {response.status_code}")
84
85        try:
86            return response.json()
87        except:
88            raise InvalidToken(f"Failed to parse response from {url}")
89    
90    def get_template(self, template:str) -> dict:
91        """Returns a template from the templates.json file
92        - template: Name of template to return
93        - **return:** Template dictionary
94        """
95        try:
96            return self.templates[template]
97        except:
98            raise InvalidToken(f"Template not found: {template}")

API wrapper class for Planka

  • url: URL of Planka instance
  • username: Username of Planka user
  • password: Password of Planka user
Planka( url: str, username: str, password: str, templates='config/templates.json')
16    def __init__(self, url:str, username:str, password:str, templates="config/templates.json"):
17        self.url = url
18        self.username = username
19        self.password = password
20        self.auth = None
21        with open(templates) as f:
22            self.templates = json.load(f)
23        self.authenticate()
def deauthenticate(self) -> bool:
28    def deauthenticate(self) -> bool:
29        """Deletes the auth token from the Planka API
30        - **return:** True if successful, False if not
31        """
32        try:
33            self.request("DELETE", "/api/access-tokens/me")
34            self.auth = None
35            return True
36        except:
37            raise InvalidToken(f"No active access token assigned to this instance\n{self.__repr__()}")

Deletes the auth token from the Planka API

  • return: True if successful, False if not
def validate(self) -> bool:
39    def validate(self) -> bool:
40        """Validates the Planka API connection
41        - **return:** True if successful, False if not
42        """
43        try:
44            self.request("GET", "/*")
45            return True
46        except:
47            raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")

Validates the Planka API connection

  • return: True if successful, False if not
def authenticate(self) -> bool:
49    def authenticate(self) -> bool:
50        """Gets an auth token from the Planka API
51        - **return:** True if successful, False if not
52        """
53        try:
54            request = requests.post(f"{self.url}/api/access-tokens", data={'emailOrUsername': self.username, 'password': self.password})
55            self.auth = request.json()['item']
56            if not self.auth:
57                raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
58            return True
59        except:
60            raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")

Gets an auth token from the Planka API

  • return: True if successful, False if not
def request(self, method: str, endpoint: str, data: dict = None) -> dict:
62    def request(self, method:str, endpoint:str, data:dict=None) -> dict:
63        """Makes a request to the Planka API
64        - method: HTTP method
65        - endpoint: API endpoint
66        - data: Data to send with request (default: None)
67        - **return:** JSON response from Planka API
68        """
69        if not self.auth:
70            self.authenticate()
71        headers = \
72            { 
73            "Content-Type": "application/json",
74            "Authorization": f"Bearer {self.auth}"
75            }
76        url = f"{self.url}{endpoint}"
77        response = requests.request(method, url, headers=headers, json=data)
78
79        if response.status_code == 401:
80            raise InvalidToken("Invalid API credentials")
81
82        if response.status_code not in [200, 201]:
83            raise InvalidToken(f"Failed to {method} {url} with status code {response.status_code}")
84
85        try:
86            return response.json()
87        except:
88            raise InvalidToken(f"Failed to parse response from {url}")

Makes a request to the Planka API

  • method: HTTP method
  • endpoint: API endpoint
  • data: Data to send with request (default: None)
  • return: JSON response from Planka API
def get_template(self, template: str) -> dict:
90    def get_template(self, template:str) -> dict:
91        """Returns a template from the templates.json file
92        - template: Name of template to return
93        - **return:** Template dictionary
94        """
95        try:
96            return self.templates[template]
97        except:
98            raise InvalidToken(f"Template not found: {template}")

Returns a template from the templates.json file

  • template: Name of template to return
  • return: Template dictionary
class Controller:
100class Controller():
101    def __init__(self, instance:Planka) -> None:
102        """Controller class for Planka API
103        - instance: Planka API instance
104        """
105        self.instance = instance
106        self.template:dict = None
107        self.data:dict = None
108        self.response:dict = None
109
110    def __str__(self) -> str:
111        """Returns a string representation of the controller object
112        - **return:** String representation of controller object
113        """
114        return f"{type(self).__name__}:\n{json.dumps(self.data, sort_keys=True, indent=4)}"
115
116    def __repr__(self) -> str:
117        """Returns a string representation of the controller object
118        - **return:** String representation of controller object
119        """
120        return f"<{type(self).__name__}({self.__class__.__bases__[0].__name__})>{self.__str__()}"
121
122    def build(self, **kwargs) -> dict:
123        """Builds the controller data
124        - **return:** Controller data dictionary
125        """
126        if not kwargs:
127            return kwargs
128        valid_keys = self.template.keys()
129        data = {key: value for key, value in kwargs.items() if key in valid_keys}
130        self.data = data
131        return self.data
132
133    def create(self, route:str, data:dict=None) -> dict:
134        """Creates a new controller object (POST)
135        - route: Route for controller object POST request
136        - **return:** POST response dictionary
137        """
138        if not data:
139            data = self.data
140        if not data:
141            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
142        self.response = self.instance.request("POST", route, data)
143        return self.response
144
145    def get(self, route:str) -> dict:
146        """Gets a controller object (GET)
147        - route: Route for controller object GET request
148        - **return:** GET response dictionary
149        """
150        return self.instance.request("GET", route)
151
152    def update(self, route:str, data:dict=None) -> dict:
153        """Updates a controller object (PATCH)
154        - route: Route for controller object PATCH request
155        - oid: ID of controller object
156        - **return:** PATCH response dictionary
157        """
158        if not data:
159            data = self.data
160        if not self.data:
161            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
162        self.response = self.instance.request("PATCH", route, data=data)
163        return self.response
164
165    def delete(self, route:str) -> dict: 
166        """Deletes a controller object (DELETE)
167        - route: Route for controller object DELETE request
168        - oid: ID of controller object
169        - **return:** DELETE response dictionary
170        """
171        return self.instance.request("DELETE", route)
172    
173    def last_response(self) -> dict:
174        """Returns the last response from the controller object
175        - **return:** Last response dictionary
176        """
177        return self.response
Controller(instance: plankapy.Planka)
101    def __init__(self, instance:Planka) -> None:
102        """Controller class for Planka API
103        - instance: Planka API instance
104        """
105        self.instance = instance
106        self.template:dict = None
107        self.data:dict = None
108        self.response:dict = None

Controller class for Planka API

  • instance: Planka API instance
def build(self, **kwargs) -> dict:
122    def build(self, **kwargs) -> dict:
123        """Builds the controller data
124        - **return:** Controller data dictionary
125        """
126        if not kwargs:
127            return kwargs
128        valid_keys = self.template.keys()
129        data = {key: value for key, value in kwargs.items() if key in valid_keys}
130        self.data = data
131        return self.data

Builds the controller data

  • return: Controller data dictionary
def create(self, route: str, data: dict = None) -> dict:
133    def create(self, route:str, data:dict=None) -> dict:
134        """Creates a new controller object (POST)
135        - route: Route for controller object POST request
136        - **return:** POST response dictionary
137        """
138        if not data:
139            data = self.data
140        if not data:
141            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
142        self.response = self.instance.request("POST", route, data)
143        return self.response

Creates a new controller object (POST)

  • route: Route for controller object POST request
  • return: POST response dictionary
def get(self, route: str) -> dict:
145    def get(self, route:str) -> dict:
146        """Gets a controller object (GET)
147        - route: Route for controller object GET request
148        - **return:** GET response dictionary
149        """
150        return self.instance.request("GET", route)

Gets a controller object (GET)

  • route: Route for controller object GET request
  • return: GET response dictionary
def update(self, route: str, data: dict = None) -> dict:
152    def update(self, route:str, data:dict=None) -> dict:
153        """Updates a controller object (PATCH)
154        - route: Route for controller object PATCH request
155        - oid: ID of controller object
156        - **return:** PATCH response dictionary
157        """
158        if not data:
159            data = self.data
160        if not self.data:
161            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
162        self.response = self.instance.request("PATCH", route, data=data)
163        return self.response

Updates a controller object (PATCH)

  • route: Route for controller object PATCH request
  • oid: ID of controller object
  • return: PATCH response dictionary
def delete(self, route: str) -> dict:
165    def delete(self, route:str) -> dict: 
166        """Deletes a controller object (DELETE)
167        - route: Route for controller object DELETE request
168        - oid: ID of controller object
169        - **return:** DELETE response dictionary
170        """
171        return self.instance.request("DELETE", route)

Deletes a controller object (DELETE)

  • route: Route for controller object DELETE request
  • oid: ID of controller object
  • return: DELETE response dictionary
def last_response(self) -> dict:
173    def last_response(self) -> dict:
174        """Returns the last response from the controller object
175        - **return:** Last response dictionary
176        """
177        return self.response

Returns the last response from the controller object

  • return: Last response dictionary
class Project(Controller):
179class Project(Controller):
180    def __init__(self, instance:Planka, **kwargs) -> None:
181        self.instance = instance
182        self.template = instance.get_template("project")
183        self.data = self.build(**kwargs)
184
185    def get(self, name:str=None, oid:str=None) -> dict:
186        """Gets a project by name
187        - oid: ID of project to get (optional)
188        - name: Name of project if None returns all projects
189        - **return:** GET response dictionary
190        """
191        if oid:
192            return super().get(f"/api/projects/{oid}")
193        prjs = super().get("/api/projects")
194        if not name:
195            return prjs
196        prj_names = [prj["name"] for prj in prjs["items"]]
197        if name not in prj_names:
198            raise InvalidToken(f"Project {name} not found")
199        prj_id = [prj for prj in prjs["items"] if prj["name"] == name][0]["id"]
200        return super().get(f"/api/projects/{prj_id}")
201
202    def get_project_names(self) -> list:
203        """Gets a list of project names
204        - **return:** List of project names
205        """
206        return [prj["name"] for prj in self.get()['items']]
207    
208    def create(self) -> dict:
209        """Creates a new project
210        - **return:** POST response dictionary
211        """
212        if not self.data:
213            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
214        if self.data["name"] in [prj["name"] for prj in self.get()['items']]:
215            raise InvalidToken(f"Project {self.data['name']} already exists")
216        return super().create("/api/projects")
217    
218    def update(self, name:str) -> dict:
219        """Updates a project
220        - name: Name of project to update
221        - **return:** PATCH response dictionary
222        """
223        prj_id = prj_id = self.get(name)['item']['id']
224        return super().update(f"/api/projects/{prj_id}")
225
226    def delete(self, name:str) -> dict:
227        """Deletes a project
228        - name: Name of project to delete
229        - **return:** DELETE response dictionary
230        """
231        prj_id = self.get(name)['item']['id']
232        return super().delete(f"/api/projects/{prj_id}")
Project(instance: plankapy.Planka, **kwargs)
180    def __init__(self, instance:Planka, **kwargs) -> None:
181        self.instance = instance
182        self.template = instance.get_template("project")
183        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def get(self, name: str = None, oid: str = None) -> dict:
185    def get(self, name:str=None, oid:str=None) -> dict:
186        """Gets a project by name
187        - oid: ID of project to get (optional)
188        - name: Name of project if None returns all projects
189        - **return:** GET response dictionary
190        """
191        if oid:
192            return super().get(f"/api/projects/{oid}")
193        prjs = super().get("/api/projects")
194        if not name:
195            return prjs
196        prj_names = [prj["name"] for prj in prjs["items"]]
197        if name not in prj_names:
198            raise InvalidToken(f"Project {name} not found")
199        prj_id = [prj for prj in prjs["items"] if prj["name"] == name][0]["id"]
200        return super().get(f"/api/projects/{prj_id}")

Gets a project by name

  • oid: ID of project to get (optional)
  • name: Name of project if None returns all projects
  • return: GET response dictionary
def get_project_names(self) -> list:
202    def get_project_names(self) -> list:
203        """Gets a list of project names
204        - **return:** List of project names
205        """
206        return [prj["name"] for prj in self.get()['items']]

Gets a list of project names

  • return: List of project names
def create(self) -> dict:
208    def create(self) -> dict:
209        """Creates a new project
210        - **return:** POST response dictionary
211        """
212        if not self.data:
213            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
214        if self.data["name"] in [prj["name"] for prj in self.get()['items']]:
215            raise InvalidToken(f"Project {self.data['name']} already exists")
216        return super().create("/api/projects")

Creates a new project

  • return: POST response dictionary
def update(self, name: str) -> dict:
218    def update(self, name:str) -> dict:
219        """Updates a project
220        - name: Name of project to update
221        - **return:** PATCH response dictionary
222        """
223        prj_id = prj_id = self.get(name)['item']['id']
224        return super().update(f"/api/projects/{prj_id}")

Updates a project

  • name: Name of project to update
  • return: PATCH response dictionary
def delete(self, name: str) -> dict:
226    def delete(self, name:str) -> dict:
227        """Deletes a project
228        - name: Name of project to delete
229        - **return:** DELETE response dictionary
230        """
231        prj_id = self.get(name)['item']['id']
232        return super().delete(f"/api/projects/{prj_id}")

Deletes a project

  • name: Name of project to delete
  • return: DELETE response dictionary
Inherited Members
Controller
build
last_response
class Board(Controller):
234class Board(Controller):
235    def __init__(self, instance:Planka, **kwargs) -> None:
236        self.instance = instance
237        self.template = instance.get_template("board")
238        self.data = self.build(**kwargs)
239    
240    def get(self, project_name:str=None, board_name:str=None, oid:str=None) -> dict:
241        """Gets a board by name
242        - oid: ID of board to get (optonal)
243        - name: Name of board if None returns all boards
244        - project_name: Name of project to get boards from
245        - **return:** GET response dictionary
246        """
247        if oid:
248            return super().get(f"/api/boards/{oid}")
249        if not (project_name):
250            raise InvalidToken("Please provide a project name")
251        prj_con = Project(self.instance)
252        prj = prj_con.get(project_name)
253        boards = prj["included"]["boards"]
254        if not board_name:
255            return boards
256        board_names = [board["name"] for board in boards]
257        if board_name not in board_names:
258            raise InvalidToken(f"Board `{board_name}` not found")
259        board_id = [board for board in boards if board["name"] == board_name][0]["id"]
260        return super().get(f"/api/boards/{board_id}")
261    
262    def create(self, project_name:str) -> dict:
263        """Creates a new board
264        - prj_name: Name of project to create board in
265        - **return:** POST response dictionary
266        """
267        if not self.data:
268            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
269        prj_con = Project(self.instance)
270        prj_id = prj_con.get(project_name)['item']['id']
271        return super().create(f"/api/projects/{prj_id}/boards")
272    
273    def update(self, project_name:str=None, board_name:str=None, data:dict=None, oid:str=None) -> dict:
274        """Updates a board
275        - oid: ID of board to update (optional)
276        - project_name: Name of project to update board in
277        - board_name: Name of board to update
278        - **return:** PATCH response dictionary
279        """
280        if not data:
281            data = self.data
282        if not data:
283            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
284        if oid:
285            return super().update(f"/api/boards/{oid}", data=data)
286        if not (project_name and board_name):
287            raise InvalidToken("Please provide project and board names")
288        board_id = self.get(project_name, board_name)['item']['id']
289        return super().update(f"/api/boards/{board_id}", data=self.data)
290    
291    def delete(self, project_name:str=None, board_name:str=None, oid:str=None):
292        """Deletes a board
293        - oid: ID of board to delete (optional)
294        - project_name: Name of project to delete board in
295        - board_name: Name of board to delete
296        - **return:** DELETE response dictionary
297        """
298        if oid:
299            return super().delete(f"/api/boards/{oid}")
300        if not project_name:
301            raise InvalidToken("Please provide a project name")
302        if not board_name:
303            raise InvalidToken("Please provide a board name")
304        board_id = self.get(project_name, board_name)['item']['id']
305        return super().delete(f"/api/boards/{board_id}")
Board(instance: plankapy.Planka, **kwargs)
235    def __init__(self, instance:Planka, **kwargs) -> None:
236        self.instance = instance
237        self.template = instance.get_template("board")
238        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def get( self, project_name: str = None, board_name: str = None, oid: str = None) -> dict:
240    def get(self, project_name:str=None, board_name:str=None, oid:str=None) -> dict:
241        """Gets a board by name
242        - oid: ID of board to get (optonal)
243        - name: Name of board if None returns all boards
244        - project_name: Name of project to get boards from
245        - **return:** GET response dictionary
246        """
247        if oid:
248            return super().get(f"/api/boards/{oid}")
249        if not (project_name):
250            raise InvalidToken("Please provide a project name")
251        prj_con = Project(self.instance)
252        prj = prj_con.get(project_name)
253        boards = prj["included"]["boards"]
254        if not board_name:
255            return boards
256        board_names = [board["name"] for board in boards]
257        if board_name not in board_names:
258            raise InvalidToken(f"Board `{board_name}` not found")
259        board_id = [board for board in boards if board["name"] == board_name][0]["id"]
260        return super().get(f"/api/boards/{board_id}")

Gets a board by name

  • oid: ID of board to get (optonal)
  • name: Name of board if None returns all boards
  • project_name: Name of project to get boards from
  • return: GET response dictionary
def create(self, project_name: str) -> dict:
262    def create(self, project_name:str) -> dict:
263        """Creates a new board
264        - prj_name: Name of project to create board in
265        - **return:** POST response dictionary
266        """
267        if not self.data:
268            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
269        prj_con = Project(self.instance)
270        prj_id = prj_con.get(project_name)['item']['id']
271        return super().create(f"/api/projects/{prj_id}/boards")

Creates a new board

  • prj_name: Name of project to create board in
  • return: POST response dictionary
def update( self, project_name: str = None, board_name: str = None, data: dict = None, oid: str = None) -> dict:
273    def update(self, project_name:str=None, board_name:str=None, data:dict=None, oid:str=None) -> dict:
274        """Updates a board
275        - oid: ID of board to update (optional)
276        - project_name: Name of project to update board in
277        - board_name: Name of board to update
278        - **return:** PATCH response dictionary
279        """
280        if not data:
281            data = self.data
282        if not data:
283            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
284        if oid:
285            return super().update(f"/api/boards/{oid}", data=data)
286        if not (project_name and board_name):
287            raise InvalidToken("Please provide project and board names")
288        board_id = self.get(project_name, board_name)['item']['id']
289        return super().update(f"/api/boards/{board_id}", data=self.data)

Updates a board

  • oid: ID of board to update (optional)
  • project_name: Name of project to update board in
  • board_name: Name of board to update
  • return: PATCH response dictionary
def delete( self, project_name: str = None, board_name: str = None, oid: str = None):
291    def delete(self, project_name:str=None, board_name:str=None, oid:str=None):
292        """Deletes a board
293        - oid: ID of board to delete (optional)
294        - project_name: Name of project to delete board in
295        - board_name: Name of board to delete
296        - **return:** DELETE response dictionary
297        """
298        if oid:
299            return super().delete(f"/api/boards/{oid}")
300        if not project_name:
301            raise InvalidToken("Please provide a project name")
302        if not board_name:
303            raise InvalidToken("Please provide a board name")
304        board_id = self.get(project_name, board_name)['item']['id']
305        return super().delete(f"/api/boards/{board_id}")

Deletes a board

  • oid: ID of board to delete (optional)
  • project_name: Name of project to delete board in
  • board_name: Name of board to delete
  • return: DELETE response dictionary
Inherited Members
Controller
build
last_response
class List(Controller):
307class List(Controller):
308    def __init__(self, instance:Planka, **kwargs) -> None:
309        self.instance = instance
310        self.template = instance.get_template("list")
311        self.data = self.build(**kwargs)
312
313    def get(self, project_name:str=None, board_name:str=None, list_name:str=None):
314        """Gets a list by name
315        NOTE: No GET route for list by ID
316        - project_name: Name of project to get list from
317        - board_name: Name of board to get list from
318        - list_name: Name of list to get
319        - **return:** GET response dictionary
320        """
321        if not (project_name and board_name):
322            raise InvalidToken("Please provide project and board names")
323        board_con = Board(self.instance)
324        board = board_con.get(project_name, board_name)
325        lists = board["included"]["lists"]
326        list_names = [lst["name"] for lst in lists]
327        if not list_name:
328            return lists
329        if list_name not in list_names:
330            raise InvalidToken(f"List `{list_name}` not found")
331        return [lst for lst in lists if lst["name"] == list_name][0]
332    
333    def create(self, project_name:str=None, board_name:str=None, data:dict=None):
334        """Creates a new list
335        - project_name: Name of project to create list in
336        - board_name: Name of board to create list in
337        - **return:** POST response dictionary
338        """
339        if not data:
340            data = self.data
341        if not data:
342            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
343        if not (project_name and board_name):
344            raise InvalidToken("Please provide project and board name")
345        board_con = Board(self.instance)
346        board_id = board_con.get(project_name, board_name)['item']['id']
347        return super().create(f"/api/boards/{board_id}/lists")
348    
349    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None, oid:str=None):
350        """Updates a list
351        - oid: ID of list to update (optional)
352        - project_name: Name of project to update list in
353        - board_name: Name of board to update list in
354        - list_name: Name of list to update
355        - **return:** PATCH response dictionary
356        """        
357        if not data:
358            data = self.data
359        if not data:
360            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
361        if oid:
362            return super().update(f"/api/lists/{oid}", data=data)
363        if not (project_name and board_name and list_name):
364            raise InvalidToken("Please provide project, board, and list names")
365        lst = self.get(project_name, board_name, list_name)
366        return super().update(f"/api/lists/{lst['id']}", data=data)
367    
368    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, oid:str=None):
369        """Deletes a list
370        - oid: ID of list to delete (optional)
371        - project_name: Name of project to delete list in
372        - board_name: Name of board to delete list in
373        - list_name: Name of list to delete
374        - **return:** DELETE response dictionary
375        """
376        if oid:
377            return super().delete(f"/api/lists/{oid}")
378        if not (project_name and board_name and list_name):
379            raise InvalidToken("Please provide a project, board, and list names")
380        lst = self.get(project_name, board_name, list_name)
381        return super().delete(f"/api/lists/{lst['id']}")
List(instance: plankapy.Planka, **kwargs)
308    def __init__(self, instance:Planka, **kwargs) -> None:
309        self.instance = instance
310        self.template = instance.get_template("list")
311        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def get( self, project_name: str = None, board_name: str = None, list_name: str = None):
313    def get(self, project_name:str=None, board_name:str=None, list_name:str=None):
314        """Gets a list by name
315        NOTE: No GET route for list by ID
316        - project_name: Name of project to get list from
317        - board_name: Name of board to get list from
318        - list_name: Name of list to get
319        - **return:** GET response dictionary
320        """
321        if not (project_name and board_name):
322            raise InvalidToken("Please provide project and board names")
323        board_con = Board(self.instance)
324        board = board_con.get(project_name, board_name)
325        lists = board["included"]["lists"]
326        list_names = [lst["name"] for lst in lists]
327        if not list_name:
328            return lists
329        if list_name not in list_names:
330            raise InvalidToken(f"List `{list_name}` not found")
331        return [lst for lst in lists if lst["name"] == list_name][0]

Gets a list by name NOTE: No GET route for list by ID

  • project_name: Name of project to get list from
  • board_name: Name of board to get list from
  • list_name: Name of list to get
  • return: GET response dictionary
def create( self, project_name: str = None, board_name: str = None, data: dict = None):
333    def create(self, project_name:str=None, board_name:str=None, data:dict=None):
334        """Creates a new list
335        - project_name: Name of project to create list in
336        - board_name: Name of board to create list in
337        - **return:** POST response dictionary
338        """
339        if not data:
340            data = self.data
341        if not data:
342            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
343        if not (project_name and board_name):
344            raise InvalidToken("Please provide project and board name")
345        board_con = Board(self.instance)
346        board_id = board_con.get(project_name, board_name)['item']['id']
347        return super().create(f"/api/boards/{board_id}/lists")

Creates a new list

  • project_name: Name of project to create list in
  • board_name: Name of board to create list in
  • return: POST response dictionary
def update( self, project_name: str = None, board_name: str = None, list_name: str = None, data: dict = None, oid: str = None):
349    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None, oid:str=None):
350        """Updates a list
351        - oid: ID of list to update (optional)
352        - project_name: Name of project to update list in
353        - board_name: Name of board to update list in
354        - list_name: Name of list to update
355        - **return:** PATCH response dictionary
356        """        
357        if not data:
358            data = self.data
359        if not data:
360            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
361        if oid:
362            return super().update(f"/api/lists/{oid}", data=data)
363        if not (project_name and board_name and list_name):
364            raise InvalidToken("Please provide project, board, and list names")
365        lst = self.get(project_name, board_name, list_name)
366        return super().update(f"/api/lists/{lst['id']}", data=data)

Updates a list

  • oid: ID of list to update (optional)
  • project_name: Name of project to update list in
  • board_name: Name of board to update list in
  • list_name: Name of list to update
  • return: PATCH response dictionary
def delete( self, project_name: str = None, board_name: str = None, list_name: str = None, oid: str = None):
368    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, oid:str=None):
369        """Deletes a list
370        - oid: ID of list to delete (optional)
371        - project_name: Name of project to delete list in
372        - board_name: Name of board to delete list in
373        - list_name: Name of list to delete
374        - **return:** DELETE response dictionary
375        """
376        if oid:
377            return super().delete(f"/api/lists/{oid}")
378        if not (project_name and board_name and list_name):
379            raise InvalidToken("Please provide a project, board, and list names")
380        lst = self.get(project_name, board_name, list_name)
381        return super().delete(f"/api/lists/{lst['id']}")

Deletes a list

  • oid: ID of list to delete (optional)
  • project_name: Name of project to delete list in
  • board_name: Name of board to delete list in
  • list_name: Name of list to delete
  • return: DELETE response dictionary
Inherited Members
Controller
build
last_response
class Card(Controller):
383class Card(Controller):
384    def __init__(self, instance:Planka, **kwargs) -> None:
385        self.instance = instance
386        self.template = instance.get_template("card")
387        self.data = self.build(**kwargs)
388
389    def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
390        """Gets a card by name
391        - oid: ID of card to get (optional)
392        - project_name: Name of project to get card from
393        - board_name: Name of board to get card from
394        - list_name: Name of list to get card from
395        - card_name: Name of card to get
396        - **return:** GET response dictionary
397        """
398        if oid != None:
399            return super().get(f"/api/cards/{oid}")
400        if not (project_name and board_name and list_name):
401            raise InvalidToken("Please provide project, board, and list names")
402        board_con = Board(self.instance)
403        board = board_con.get(project_name, board_name)
404        lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
405        cards = [card for card in board["included"]["cards"] if card["listId"] == lst_id]
406        card_names = [card["name"] for card in cards]
407        if not card_name:
408            return [self.get(oid=card["id"]) for card in cards]
409        if card_name not in card_names:
410            raise InvalidToken(f"Card `{card_name}` not found")
411        card_id = [card for card in cards if card["name"] == card_name][0]['id']
412        return super().get(f"/api/cards/{card_id}")
413    
414    def create(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None):
415        """Creates a new card
416        - project_name: Name of project to create card in
417        - board_name: Name of board to create card in
418        - list_name: Name of list to create card in
419        - **return:** POST response dictionary
420        """
421        if not data:
422            data = self.data
423        if not data:
424            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
425        if not (project_name and board_name and list_name):
426            raise InvalidToken("Please provide a project, board and list names")
427        board_con = Board(self.instance)
428        board = board_con.get(project_name, board_name)
429        lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
430        return super().create(f"/api/lists/{lst_id}/cards")
431
432    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
433        """Deletes a card
434        - oid: ID of card to delete (optional)
435        - project_name: Name of project to delete card in
436        - board_name: Name of board to delete card in
437        - list_name: Name of list to delete card in
438        - card_name: Name of card to delete
439        - **return:** DELETE response dictionary
440        """
441        if oid != None:
442            return super().delete(f"/api/cards/{oid}")
443        if not (project_name and board_name and list_name and card_name):
444            raise InvalidToken("Please provide a project, board, list, and card name")
445        card = self.get(project_name, board_name, list_name, card_name)
446        return super().delete(f"/api/cards/{card['id']}")
447    
448    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, data:dict=None, oid:str=None):
449        """Updates a card
450        - oid: ID of card to update (optional)
451        - project_name: Name of project to update card in
452        - board_name: Name of board to update card in
453        - list_name: Name of list to update card in
454        - card_name: Name of card to update
455        - **return:** PATCH response dictionary
456        """
457        if not data:
458            data = self.data
459        if not data:
460            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
461        if oid:
462            return super().update(f"/api/cards/{oid}", data=data)
463        if not (project_name and board_name and list_name and card_name):
464            raise InvalidToken("Please provide a project, board, list, and card name")
465        card = self.get(project_name, board_name, list_name, card_name)
466        return super().update(f"/api/cards/{card['id']}", data=data)
467    
468    def get_labels(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
469        """Gets labels for a card
470        - oid: ID of card to get labels from (optional)
471        - project_name: Name of project to get card from
472        - board_name: Name of board to get card from
473        - list_name: Name of list to get card from
474        - card_name: Name of card to get
475        - **return:** GET response dictionary
476        """
477        if oid:
478            return self.get(oid=oid)['included']['cardLabels']
479        if not (project_name and board_name and list_name and card_name):
480            raise InvalidToken("Please provide project, board, list, and card names")
481        card_id = self.get(project_name, board_name, list_name, card_name)['item']['id']
482        return self.get(oid=card_id)['included']['cardLabels']
Card(instance: plankapy.Planka, **kwargs)
384    def __init__(self, instance:Planka, **kwargs) -> None:
385        self.instance = instance
386        self.template = instance.get_template("card")
387        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def get( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, oid: str = None):
389    def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
390        """Gets a card by name
391        - oid: ID of card to get (optional)
392        - project_name: Name of project to get card from
393        - board_name: Name of board to get card from
394        - list_name: Name of list to get card from
395        - card_name: Name of card to get
396        - **return:** GET response dictionary
397        """
398        if oid != None:
399            return super().get(f"/api/cards/{oid}")
400        if not (project_name and board_name and list_name):
401            raise InvalidToken("Please provide project, board, and list names")
402        board_con = Board(self.instance)
403        board = board_con.get(project_name, board_name)
404        lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
405        cards = [card for card in board["included"]["cards"] if card["listId"] == lst_id]
406        card_names = [card["name"] for card in cards]
407        if not card_name:
408            return [self.get(oid=card["id"]) for card in cards]
409        if card_name not in card_names:
410            raise InvalidToken(f"Card `{card_name}` not found")
411        card_id = [card for card in cards if card["name"] == card_name][0]['id']
412        return super().get(f"/api/cards/{card_id}")

Gets a card by name

  • oid: ID of card to get (optional)
  • project_name: Name of project to get card from
  • board_name: Name of board to get card from
  • list_name: Name of list to get card from
  • card_name: Name of card to get
  • return: GET response dictionary
def create( self, project_name: str = None, board_name: str = None, list_name: str = None, data: dict = None):
414    def create(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None):
415        """Creates a new card
416        - project_name: Name of project to create card in
417        - board_name: Name of board to create card in
418        - list_name: Name of list to create card in
419        - **return:** POST response dictionary
420        """
421        if not data:
422            data = self.data
423        if not data:
424            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
425        if not (project_name and board_name and list_name):
426            raise InvalidToken("Please provide a project, board and list names")
427        board_con = Board(self.instance)
428        board = board_con.get(project_name, board_name)
429        lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
430        return super().create(f"/api/lists/{lst_id}/cards")

Creates a new card

  • project_name: Name of project to create card in
  • board_name: Name of board to create card in
  • list_name: Name of list to create card in
  • return: POST response dictionary
def delete( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, oid: str = None):
432    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
433        """Deletes a card
434        - oid: ID of card to delete (optional)
435        - project_name: Name of project to delete card in
436        - board_name: Name of board to delete card in
437        - list_name: Name of list to delete card in
438        - card_name: Name of card to delete
439        - **return:** DELETE response dictionary
440        """
441        if oid != None:
442            return super().delete(f"/api/cards/{oid}")
443        if not (project_name and board_name and list_name and card_name):
444            raise InvalidToken("Please provide a project, board, list, and card name")
445        card = self.get(project_name, board_name, list_name, card_name)
446        return super().delete(f"/api/cards/{card['id']}")

Deletes a card

  • oid: ID of card to delete (optional)
  • project_name: Name of project to delete card in
  • board_name: Name of board to delete card in
  • list_name: Name of list to delete card in
  • card_name: Name of card to delete
  • return: DELETE response dictionary
def update( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, data: dict = None, oid: str = None):
448    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, data:dict=None, oid:str=None):
449        """Updates a card
450        - oid: ID of card to update (optional)
451        - project_name: Name of project to update card in
452        - board_name: Name of board to update card in
453        - list_name: Name of list to update card in
454        - card_name: Name of card to update
455        - **return:** PATCH response dictionary
456        """
457        if not data:
458            data = self.data
459        if not data:
460            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
461        if oid:
462            return super().update(f"/api/cards/{oid}", data=data)
463        if not (project_name and board_name and list_name and card_name):
464            raise InvalidToken("Please provide a project, board, list, and card name")
465        card = self.get(project_name, board_name, list_name, card_name)
466        return super().update(f"/api/cards/{card['id']}", data=data)

Updates a card

  • oid: ID of card to update (optional)
  • project_name: Name of project to update card in
  • board_name: Name of board to update card in
  • list_name: Name of list to update card in
  • card_name: Name of card to update
  • return: PATCH response dictionary
def get_labels( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, oid: str = None):
468    def get_labels(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
469        """Gets labels for a card
470        - oid: ID of card to get labels from (optional)
471        - project_name: Name of project to get card from
472        - board_name: Name of board to get card from
473        - list_name: Name of list to get card from
474        - card_name: Name of card to get
475        - **return:** GET response dictionary
476        """
477        if oid:
478            return self.get(oid=oid)['included']['cardLabels']
479        if not (project_name and board_name and list_name and card_name):
480            raise InvalidToken("Please provide project, board, list, and card names")
481        card_id = self.get(project_name, board_name, list_name, card_name)['item']['id']
482        return self.get(oid=card_id)['included']['cardLabels']

Gets labels for a card

  • oid: ID of card to get labels from (optional)
  • project_name: Name of project to get card from
  • board_name: Name of board to get card from
  • list_name: Name of list to get card from
  • card_name: Name of card to get
  • return: GET response dictionary
Inherited Members
Controller
build
last_response
class Label(Controller):
484class Label(Controller):
485    def __init__(self, instance:Planka, **kwargs) -> None:
486        self.instance = instance
487        self.template = instance.get_template("label")
488        self.options = instance.get_template("colors")
489        self.data = self.build(**kwargs)
490
491    def colors(self) -> list:
492        return self.options
493    
494    def get(self, project_name:str=None, board_name:str=None, label_name:str=None) -> dict:
495        """Gets a label by name
496        - project_name: Name of project to get label from
497        - board_name: Name of board to get label from
498        - label_name: Name of label to get
499        - **return:** GET response dictionary
500        """
501        if not (project_name and board_name):
502            raise InvalidToken("Please provide project and board names")
503        board_con = Board(self.instance)
504        board = board_con.get(project_name, board_name)
505        labels = board["included"]["labels"]
506        label_names = [label["name"] for label in labels]
507        if not label_name:
508            return labels
509        if label_name not in label_names:
510            raise InvalidToken(f"Label `{label_name}` not found")
511        return [label for label in labels if label["name"] == label_name][0]
512    
513    def create(self, project_name:str=None, board_name:str=None, data:dict=None):
514        """Creates a new label
515        - project_name: Name of project to create label in
516        - board_name: Name of board to create label in
517        - **return:** POST response dictionary
518        """
519        if not data:
520            data = self.data
521        if not data:
522            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
523        if not (project_name and board_name):
524            raise InvalidToken("Please provide project and board names")
525        board_con = Board(self.instance)
526        board = board_con.get(project_name, board_name)['item']
527        return super().create(f"/api/boards/{board['id']}/labels")
528    
529    def delete(self, project_name:str=None, board_name:str=None, label_name:str=None, oid:str=None):
530        """Deletes a label
531        - oid: ID of label to delete (optional)
532        - project_name: Name of project to delete label from
533        - board_name: Name of board to delete label from
534        - label_name: Name of label to delete
535        - **return:** DELETE response dictionary
536        """
537        if oid:
538            return super().delete(f"/api/labels/{oid}")
539        if not (project_name and board_name and label_name):
540            raise InvalidToken("Please provide project, board, and label names")
541        label = self.get(project_name, board_name, label_name)
542        return super().delete(f"/api/labels/{label['id']}")
543    
544    def add(self, project_name:str=None, board_name:str=None, list_name:str=None ,card_name:str=None, label_name:str=None, card_id:str=None, label_id:str=None):
545        """Adds a label to a card
546        - project_name: Name of project to add label to card in
547        - board_name: Name of board to add label to card in
548        - label_name: Name of label to add to card
549        - card_name: Name of card to add label to
550        - list_name: Name of list to add label to card in
551        - **return:** POST response dictionary
552        """
553        if label_id and card_id:
554            return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label_id})
555        if not (project_name and board_name and label_name):
556            raise InvalidToken("Please provide a project, board, label name")
557        if card_id:
558            label = self.get(project_name, board_name, label_name)
559            return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label['item']['id']})
560        if not (card_name and list_name):
561            raise InvalidToken("Please provide a card and list name")
562        card_con = Card(self.instance)
563        card = card_con.get(project_name, board_name, list_name, card_name)
564        label = self.get(project_name, board_name, label_name)
565        return super().create(f"/api/cards/{card['item']['id']}/labels", {"labelId":label['item']['id']})
566
567    def remove(self, project_name:str=None, board_name:str=None, list_name:str=None ,card_name:str=None, label_name:str=None, card_id:str=None, label_id:str=None):
568        """Removes a label from a card
569        - project_name: Name of project to remove label from card in
570        - board_name: Name of board to remove label from card in
571        - label_name: Name of label to remove from card
572        - card_name: Name of card to remove label from
573        - list_name: Name of list to remove label from card in
574        - **return:** DELETE response dictionary
575        """
576        if label_id and card_id:
577            return super().delete(f"/api/cards/{card_id}/labels/{label_id}")
578        if not (project_name and board_name and label_name):
579            raise InvalidToken("Please provide a project, board, label name")
580        if card_id:
581            label_id = [label['id'] for label in Card(self.instance).get_labels(oid=card_id) if label['name'] == label_name][0]
582            return super().delete(f"/api/cards/{card_id}/labels/{label['item']['id']}")
583        if not (card_name and list_name):
584            raise InvalidToken("Please provide a card and list name")
585        card_con = Card(self.instance)
586        card = card_con.get(project_name, board_name, list_name, card_name)
587        label = self.get(project_name, board_name, label_name)
588        return super().delete(f"/api/cards/{card['item']['id']}/labels/{label['item']['id']}")
Label(instance: plankapy.Planka, **kwargs)
485    def __init__(self, instance:Planka, **kwargs) -> None:
486        self.instance = instance
487        self.template = instance.get_template("label")
488        self.options = instance.get_template("colors")
489        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def colors(self) -> list:
491    def colors(self) -> list:
492        return self.options
def get( self, project_name: str = None, board_name: str = None, label_name: str = None) -> dict:
494    def get(self, project_name:str=None, board_name:str=None, label_name:str=None) -> dict:
495        """Gets a label by name
496        - project_name: Name of project to get label from
497        - board_name: Name of board to get label from
498        - label_name: Name of label to get
499        - **return:** GET response dictionary
500        """
501        if not (project_name and board_name):
502            raise InvalidToken("Please provide project and board names")
503        board_con = Board(self.instance)
504        board = board_con.get(project_name, board_name)
505        labels = board["included"]["labels"]
506        label_names = [label["name"] for label in labels]
507        if not label_name:
508            return labels
509        if label_name not in label_names:
510            raise InvalidToken(f"Label `{label_name}` not found")
511        return [label for label in labels if label["name"] == label_name][0]

Gets a label by name

  • project_name: Name of project to get label from
  • board_name: Name of board to get label from
  • label_name: Name of label to get
  • return: GET response dictionary
def create( self, project_name: str = None, board_name: str = None, data: dict = None):
513    def create(self, project_name:str=None, board_name:str=None, data:dict=None):
514        """Creates a new label
515        - project_name: Name of project to create label in
516        - board_name: Name of board to create label in
517        - **return:** POST response dictionary
518        """
519        if not data:
520            data = self.data
521        if not data:
522            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
523        if not (project_name and board_name):
524            raise InvalidToken("Please provide project and board names")
525        board_con = Board(self.instance)
526        board = board_con.get(project_name, board_name)['item']
527        return super().create(f"/api/boards/{board['id']}/labels")

Creates a new label

  • project_name: Name of project to create label in
  • board_name: Name of board to create label in
  • return: POST response dictionary
def delete( self, project_name: str = None, board_name: str = None, label_name: str = None, oid: str = None):
529    def delete(self, project_name:str=None, board_name:str=None, label_name:str=None, oid:str=None):
530        """Deletes a label
531        - oid: ID of label to delete (optional)
532        - project_name: Name of project to delete label from
533        - board_name: Name of board to delete label from
534        - label_name: Name of label to delete
535        - **return:** DELETE response dictionary
536        """
537        if oid:
538            return super().delete(f"/api/labels/{oid}")
539        if not (project_name and board_name and label_name):
540            raise InvalidToken("Please provide project, board, and label names")
541        label = self.get(project_name, board_name, label_name)
542        return super().delete(f"/api/labels/{label['id']}")

Deletes a label

  • oid: ID of label to delete (optional)
  • project_name: Name of project to delete label from
  • board_name: Name of board to delete label from
  • label_name: Name of label to delete
  • return: DELETE response dictionary
def add( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, label_name: str = None, card_id: str = None, label_id: str = None):
544    def add(self, project_name:str=None, board_name:str=None, list_name:str=None ,card_name:str=None, label_name:str=None, card_id:str=None, label_id:str=None):
545        """Adds a label to a card
546        - project_name: Name of project to add label to card in
547        - board_name: Name of board to add label to card in
548        - label_name: Name of label to add to card
549        - card_name: Name of card to add label to
550        - list_name: Name of list to add label to card in
551        - **return:** POST response dictionary
552        """
553        if label_id and card_id:
554            return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label_id})
555        if not (project_name and board_name and label_name):
556            raise InvalidToken("Please provide a project, board, label name")
557        if card_id:
558            label = self.get(project_name, board_name, label_name)
559            return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label['item']['id']})
560        if not (card_name and list_name):
561            raise InvalidToken("Please provide a card and list name")
562        card_con = Card(self.instance)
563        card = card_con.get(project_name, board_name, list_name, card_name)
564        label = self.get(project_name, board_name, label_name)
565        return super().create(f"/api/cards/{card['item']['id']}/labels", {"labelId":label['item']['id']})

Adds a label to a card

  • project_name: Name of project to add label to card in
  • board_name: Name of board to add label to card in
  • label_name: Name of label to add to card
  • card_name: Name of card to add label to
  • list_name: Name of list to add label to card in
  • return: POST response dictionary
def remove( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, label_name: str = None, card_id: str = None, label_id: str = None):
567    def remove(self, project_name:str=None, board_name:str=None, list_name:str=None ,card_name:str=None, label_name:str=None, card_id:str=None, label_id:str=None):
568        """Removes a label from a card
569        - project_name: Name of project to remove label from card in
570        - board_name: Name of board to remove label from card in
571        - label_name: Name of label to remove from card
572        - card_name: Name of card to remove label from
573        - list_name: Name of list to remove label from card in
574        - **return:** DELETE response dictionary
575        """
576        if label_id and card_id:
577            return super().delete(f"/api/cards/{card_id}/labels/{label_id}")
578        if not (project_name and board_name and label_name):
579            raise InvalidToken("Please provide a project, board, label name")
580        if card_id:
581            label_id = [label['id'] for label in Card(self.instance).get_labels(oid=card_id) if label['name'] == label_name][0]
582            return super().delete(f"/api/cards/{card_id}/labels/{label['item']['id']}")
583        if not (card_name and list_name):
584            raise InvalidToken("Please provide a card and list name")
585        card_con = Card(self.instance)
586        card = card_con.get(project_name, board_name, list_name, card_name)
587        label = self.get(project_name, board_name, label_name)
588        return super().delete(f"/api/cards/{card['item']['id']}/labels/{label['item']['id']}")

Removes a label from a card

  • project_name: Name of project to remove label from card in
  • board_name: Name of board to remove label from card in
  • label_name: Name of label to remove from card
  • card_name: Name of card to remove label from
  • list_name: Name of list to remove label from card in
  • return: DELETE response dictionary
Inherited Members
Controller
build
update
last_response
class Task(Controller):
590class Task(Controller):
591    def __init__(self, instance:Planka, **kwargs) -> None:
592        self.instance = instance
593        self.template = instance.get_template("task")
594        self.data = self.build(**kwargs)
595    
596    def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None) -> dict:
597        """Gets a task by name
598        NOTE: No GET route for tasks by OID
599        - project_name: Name of project to get task from
600        - board_name: Name of board to get task from
601        - list_name: Name of list to get task from
602        - card_name: Name of card to get task from
603        - task_name: Name of task to get
604        - **return:** GET response dictionary
605        """
606        if not (project_name and board_name and list_name and card_name):
607            raise InvalidToken("Please provide project, board, list, and card names")
608        board_con = Board(self.instance)
609        board = board_con.get(project_name, board_name)
610        list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
611        cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
612        card_id = [card for card in cards if card["name"] == card_name][0]["id"]
613        tasks = [task for task in board["included"]["tasks"] if task["cardId"] == card_id]
614        task_names = [task["name"] for task in tasks]
615        if not task_name:
616            return tasks
617        if task_name not in task_names:
618            raise InvalidToken(f"Task `{task_name}` not found")
619        return [task for task in tasks if task["name"] == task_name][0]
620    
621    def create(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, data:dict=None, card_id:str=None):
622        """Creates a new task
623        - card_id: ID of card to create task in (optional)
624        - project_name: Name of project to create task in
625        - board_name: Name of board to create task in
626        - list_name: Name of list to create task in
627        - card_name: Name of card to create task in
628        - **return:** POST response dictionary
629        """
630        if not data:
631            data = self.data
632        if not data:
633            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
634        if card_id:
635            return super().create(f"/api/cards/{card_id}/tasks")
636        if not (project_name and board_name and list_name and card_name):
637            raise InvalidToken("Please provide project, board, list, and card names")
638        board_con = Board(self.instance)
639        board = board_con.get(project_name, board_name)
640        list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
641        cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
642        card_id = [card for card in cards if card["name"] == card_name][0]["id"]
643        return super().create(f"/api/cards/{card_id}/tasks")
644    
645    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None, data:dict=None, oid:str=None):
646        """Updates a task
647        - oid: Object ID of task to update (optional)
648        - project_name: Name of project to update task in
649        - board_name: Name of board to update task in
650        - list_name: Name of list to update task in
651        - card_name: Name of card to update task in
652        - task_name: Name of task to update
653        - **return:** PATCH response dictionary
654        """
655        if not data:
656            data = self.data
657        if not data:
658            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
659        if oid:
660            return super().update(f"/api/tasks/{oid}")
661        if not (project_name and board_name and list_name and card_name and task_name):
662            raise InvalidToken("Please provide project, board, list, card, and task names")
663        task = self.get(project_name, board_name, list_name, card_name, task_name)
664        return super().update(f"/api/tasks/{task['id']}")
665       
666    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None, oid:str=None):
667        """Deletes a task
668        - oid: ID of task to delete (Use this if you already have the ID)
669        - project_name: Name of project to delete task from
670        - board_name: Name of board to delete task from
671        - list_name: Name of list to delete task from
672        - card_name: Name of card to delete task from
673        - task_name: Name of task to delete
674        - **return:** DELETE response dictionary
675        """
676        if oid:
677            return super().delete(f"/api/tasks/{id}")
678        if not (project_name and board_name and list_name and card_name and task_name):
679            raise InvalidToken("Please provide project, board, list, card, and task names")
680        task = self.get(project_name, board_name, list_name, card_name, task_name)
681        return super().delete(f"/api/tasks/{task['id']}")
Task(instance: plankapy.Planka, **kwargs)
591    def __init__(self, instance:Planka, **kwargs) -> None:
592        self.instance = instance
593        self.template = instance.get_template("task")
594        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def get( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, task_name: str = None) -> dict:
596    def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None) -> dict:
597        """Gets a task by name
598        NOTE: No GET route for tasks by OID
599        - project_name: Name of project to get task from
600        - board_name: Name of board to get task from
601        - list_name: Name of list to get task from
602        - card_name: Name of card to get task from
603        - task_name: Name of task to get
604        - **return:** GET response dictionary
605        """
606        if not (project_name and board_name and list_name and card_name):
607            raise InvalidToken("Please provide project, board, list, and card names")
608        board_con = Board(self.instance)
609        board = board_con.get(project_name, board_name)
610        list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
611        cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
612        card_id = [card for card in cards if card["name"] == card_name][0]["id"]
613        tasks = [task for task in board["included"]["tasks"] if task["cardId"] == card_id]
614        task_names = [task["name"] for task in tasks]
615        if not task_name:
616            return tasks
617        if task_name not in task_names:
618            raise InvalidToken(f"Task `{task_name}` not found")
619        return [task for task in tasks if task["name"] == task_name][0]

Gets a task by name NOTE: No GET route for tasks by OID

  • project_name: Name of project to get task from
  • board_name: Name of board to get task from
  • list_name: Name of list to get task from
  • card_name: Name of card to get task from
  • task_name: Name of task to get
  • return: GET response dictionary
def create( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, data: dict = None, card_id: str = None):
621    def create(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, data:dict=None, card_id:str=None):
622        """Creates a new task
623        - card_id: ID of card to create task in (optional)
624        - project_name: Name of project to create task in
625        - board_name: Name of board to create task in
626        - list_name: Name of list to create task in
627        - card_name: Name of card to create task in
628        - **return:** POST response dictionary
629        """
630        if not data:
631            data = self.data
632        if not data:
633            raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
634        if card_id:
635            return super().create(f"/api/cards/{card_id}/tasks")
636        if not (project_name and board_name and list_name and card_name):
637            raise InvalidToken("Please provide project, board, list, and card names")
638        board_con = Board(self.instance)
639        board = board_con.get(project_name, board_name)
640        list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
641        cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
642        card_id = [card for card in cards if card["name"] == card_name][0]["id"]
643        return super().create(f"/api/cards/{card_id}/tasks")

Creates a new task

  • card_id: ID of card to create task in (optional)
  • project_name: Name of project to create task in
  • board_name: Name of board to create task in
  • list_name: Name of list to create task in
  • card_name: Name of card to create task in
  • return: POST response dictionary
def update( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, task_name: str = None, data: dict = None, oid: str = None):
645    def update(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None, data:dict=None, oid:str=None):
646        """Updates a task
647        - oid: Object ID of task to update (optional)
648        - project_name: Name of project to update task in
649        - board_name: Name of board to update task in
650        - list_name: Name of list to update task in
651        - card_name: Name of card to update task in
652        - task_name: Name of task to update
653        - **return:** PATCH response dictionary
654        """
655        if not data:
656            data = self.data
657        if not data:
658            raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
659        if oid:
660            return super().update(f"/api/tasks/{oid}")
661        if not (project_name and board_name and list_name and card_name and task_name):
662            raise InvalidToken("Please provide project, board, list, card, and task names")
663        task = self.get(project_name, board_name, list_name, card_name, task_name)
664        return super().update(f"/api/tasks/{task['id']}")

Updates a task

  • oid: Object ID of task to update (optional)
  • project_name: Name of project to update task in
  • board_name: Name of board to update task in
  • list_name: Name of list to update task in
  • card_name: Name of card to update task in
  • task_name: Name of task to update
  • return: PATCH response dictionary
def delete( self, project_name: str = None, board_name: str = None, list_name: str = None, card_name: str = None, task_name: str = None, oid: str = None):
666    def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None, oid:str=None):
667        """Deletes a task
668        - oid: ID of task to delete (Use this if you already have the ID)
669        - project_name: Name of project to delete task from
670        - board_name: Name of board to delete task from
671        - list_name: Name of list to delete task from
672        - card_name: Name of card to delete task from
673        - task_name: Name of task to delete
674        - **return:** DELETE response dictionary
675        """
676        if oid:
677            return super().delete(f"/api/tasks/{id}")
678        if not (project_name and board_name and list_name and card_name and task_name):
679            raise InvalidToken("Please provide project, board, list, card, and task names")
680        task = self.get(project_name, board_name, list_name, card_name, task_name)
681        return super().delete(f"/api/tasks/{task['id']}")

Deletes a task

  • oid: ID of task to delete (Use this if you already have the ID)
  • project_name: Name of project to delete task from
  • board_name: Name of board to delete task from
  • list_name: Name of list to delete task from
  • card_name: Name of card to delete task from
  • task_name: Name of task to delete
  • return: DELETE response dictionary
Inherited Members
Controller
build
last_response
class Attachment(Controller):
683class Attachment(Controller):
684    def __init__(self, instance:Planka, **kwargs) -> None:
685        self.instance = instance
686        self.template = instance.get_template("attachment")
687        self.data = self.build(**kwargs)
Attachment(instance: plankapy.Planka, **kwargs)
684    def __init__(self, instance:Planka, **kwargs) -> None:
685        self.instance = instance
686        self.template = instance.get_template("attachment")
687        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
class Stopwatch(Controller):
689class Stopwatch(Controller):
690    def __init__(self, instance:Planka, **kwargs) -> None:
691        self.instance = instance
692        self.template = instance.get_template("stopwatch")
693        self.data = self.build(**kwargs)
Stopwatch(instance: plankapy.Planka, **kwargs)
690    def __init__(self, instance:Planka, **kwargs) -> None:
691        self.instance = instance
692        self.template = instance.get_template("stopwatch")
693        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
class Background(Controller):
695class Background(Controller):
696    def __init__(self, instance:Planka, **kwargs) -> None:
697        self.instance = instance
698        self.template = instance.get_template("background")
699        self.options = instance.get_template("gradients")
700        self.data = self.build(**kwargs)
701
702    def gradients(self) -> dict:
703        """Gets all gradients
704        - **return:** GET response dictionary
705        """
706        return self.options
707
708    def apply(self, prj_name:str):
709        """Applies a gradient to a project
710        - project: Name of project to apply gradient to
711        - **return:** PATCH response dictionary
712        """
713        project = Project(self.instance)
714        prj_id = project.get(prj_name)["item"]["id"]
715        if "type" not in self.data.keys():
716            raise InvalidToken("Please specify a background type: `gradient` | `image`")
717        if self.data["type"] == "gradient" and self.data["name"] not in self.options:
718            raise InvalidToken(f"Gradient {self.data['name']} not found: please choose from\n{self.options}")
719        return super().update(f"/api/projects/{prj_id}", data={"background": self.data})
720    
721    def clear(self, prj_name:str):
722        """Clears a gradient from a project
723        - project: Name of project to clear gradient from
724        - **return:** PATCH response dictionary
725        """
726        project = Project(self.instance)
727        prj_id = project.get(prj_name)["item"]["id"]
728        return super().update(f"/api/projects/{prj_id}", data={"background": None})
Background(instance: plankapy.Planka, **kwargs)
696    def __init__(self, instance:Planka, **kwargs) -> None:
697        self.instance = instance
698        self.template = instance.get_template("background")
699        self.options = instance.get_template("gradients")
700        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
def gradients(self) -> dict:
702    def gradients(self) -> dict:
703        """Gets all gradients
704        - **return:** GET response dictionary
705        """
706        return self.options

Gets all gradients

  • return: GET response dictionary
def apply(self, prj_name: str):
708    def apply(self, prj_name:str):
709        """Applies a gradient to a project
710        - project: Name of project to apply gradient to
711        - **return:** PATCH response dictionary
712        """
713        project = Project(self.instance)
714        prj_id = project.get(prj_name)["item"]["id"]
715        if "type" not in self.data.keys():
716            raise InvalidToken("Please specify a background type: `gradient` | `image`")
717        if self.data["type"] == "gradient" and self.data["name"] not in self.options:
718            raise InvalidToken(f"Gradient {self.data['name']} not found: please choose from\n{self.options}")
719        return super().update(f"/api/projects/{prj_id}", data={"background": self.data})

Applies a gradient to a project

  • project: Name of project to apply gradient to
  • return: PATCH response dictionary
def clear(self, prj_name: str):
721    def clear(self, prj_name:str):
722        """Clears a gradient from a project
723        - project: Name of project to clear gradient from
724        - **return:** PATCH response dictionary
725        """
726        project = Project(self.instance)
727        prj_id = project.get(prj_name)["item"]["id"]
728        return super().update(f"/api/projects/{prj_id}", data={"background": None})

Clears a gradient from a project

  • project: Name of project to clear gradient from
  • return: PATCH response dictionary
class Comment(Controller):
730class Comment(Controller):
731    def __init__(self, instance:Planka, **kwargs) -> None:
732        self.instance = instance
733        self.template = instance.get_template("comment-action")
734        self.data = self.build(**kwargs)
Comment(instance: plankapy.Planka, **kwargs)
731    def __init__(self, instance:Planka, **kwargs) -> None:
732        self.instance = instance
733        self.template = instance.get_template("comment-action")
734        self.data = self.build(**kwargs)

Controller class for Planka API

  • instance: Planka API instance
class User(Controller):
736class User(Controller):
737    def __init__(self, instance:Planka, **kwargs) -> None:
738        """Creates a user
739        - username: Username of user to create
740        - name: Display name of user to create
741        - password: Password of user to create
742        - email: Email of user to create
743        - subscribe: Subscibe user to own cards (default: False)
744        - organization: Organization of user to create (default: None)
745        - admin: Admin state of user to create (default: False)
746        """
747        self.instance = instance
748        self.template = instance.get_template("user")
749        self.data = self.build(**kwargs)
750
751    def get(self, username:str=None):
752        """Gets a user
753        - username: Username of user to get (all if not provided)
754        - **return:** GET response dictionary
755        """
756        if not username:
757            return super().get("/api/users")["items"]
758        users = super().get("/api/users")["items"]
759        names = [user["username"] for user in users]
760        if username not in names:
761            raise InvalidToken(f"User {username} not found")
762        return users[names.index(username)]
763    
764    def create(self, data:dict=None):
765        """Creates a user
766        - data: Data dictionary to create user with (optional)
767        - **return:** POST response dictionary
768        """
769        if not data:
770            data = self.data
771        if not data:
772            raise InvalidToken("Please either build a user or provide a data dictionary")
773        if self.data["username"] in [user["username"] for user in self.get()]:
774            raise InvalidToken(f"User {self.data['username']} already exists")
775        return super().create("/api/users", data=self.data)
776    
777    def delete(self, username:str, oid:str=None):
778        """Deletes a user
779        - username: Username of user to delete
780        - oid: ID of user to delete (Use this if you already have the ID)
781        - **return:** DELETE response dictionary
782        """
783        if oid:
784            return super().delete(f"/api/users/{oid}")
785        if username not in [user["username"] for user in self.get()]:
786            raise InvalidToken(f"User {username} not found")
787        return super().delete(f"/api/users/{self.get(username)['id']}")
788    
789    def update(self, username:str, oid:str=None, data:dict=None):
790        """Updates a user
791        - username: Username of user to update
792        - oid: ID of user to update (Use this if you already have the ID)
793        - data: Data dictionary to update user with (optional)
794        - **return:** PATCH response dictionary
795        """
796        user = self.get(username)
797        if not data:
798            data = self.data
799        if not data:
800            raise InvalidToken("Please either build a user or provide a data dictionary")
801        return super().update(f"/api/users/{user['id']}", data=data)
User(instance: plankapy.Planka, **kwargs)
737    def __init__(self, instance:Planka, **kwargs) -> None:
738        """Creates a user
739        - username: Username of user to create
740        - name: Display name of user to create
741        - password: Password of user to create
742        - email: Email of user to create
743        - subscribe: Subscibe user to own cards (default: False)
744        - organization: Organization of user to create (default: None)
745        - admin: Admin state of user to create (default: False)
746        """
747        self.instance = instance
748        self.template = instance.get_template("user")
749        self.data = self.build(**kwargs)

Creates a user

  • username: Username of user to create
  • name: Display name of user to create
  • password: Password of user to create
  • email: Email of user to create
  • subscribe: Subscibe user to own cards (default: False)
  • organization: Organization of user to create (default: None)
  • admin: Admin state of user to create (default: False)
def get(self, username: str = None):
751    def get(self, username:str=None):
752        """Gets a user
753        - username: Username of user to get (all if not provided)
754        - **return:** GET response dictionary
755        """
756        if not username:
757            return super().get("/api/users")["items"]
758        users = super().get("/api/users")["items"]
759        names = [user["username"] for user in users]
760        if username not in names:
761            raise InvalidToken(f"User {username} not found")
762        return users[names.index(username)]

Gets a user

  • username: Username of user to get (all if not provided)
  • return: GET response dictionary
def create(self, data: dict = None):
764    def create(self, data:dict=None):
765        """Creates a user
766        - data: Data dictionary to create user with (optional)
767        - **return:** POST response dictionary
768        """
769        if not data:
770            data = self.data
771        if not data:
772            raise InvalidToken("Please either build a user or provide a data dictionary")
773        if self.data["username"] in [user["username"] for user in self.get()]:
774            raise InvalidToken(f"User {self.data['username']} already exists")
775        return super().create("/api/users", data=self.data)

Creates a user

  • data: Data dictionary to create user with (optional)
  • return: POST response dictionary
def delete(self, username: str, oid: str = None):
777    def delete(self, username:str, oid:str=None):
778        """Deletes a user
779        - username: Username of user to delete
780        - oid: ID of user to delete (Use this if you already have the ID)
781        - **return:** DELETE response dictionary
782        """
783        if oid:
784            return super().delete(f"/api/users/{oid}")
785        if username not in [user["username"] for user in self.get()]:
786            raise InvalidToken(f"User {username} not found")
787        return super().delete(f"/api/users/{self.get(username)['id']}")

Deletes a user

  • username: Username of user to delete
  • oid: ID of user to delete (Use this if you already have the ID)
  • return: DELETE response dictionary
def update(self, username: str, oid: str = None, data: dict = None):
789    def update(self, username:str, oid:str=None, data:dict=None):
790        """Updates a user
791        - username: Username of user to update
792        - oid: ID of user to update (Use this if you already have the ID)
793        - data: Data dictionary to update user with (optional)
794        - **return:** PATCH response dictionary
795        """
796        user = self.get(username)
797        if not data:
798            data = self.data
799        if not data:
800            raise InvalidToken("Please either build a user or provide a data dictionary")
801        return super().update(f"/api/users/{user['id']}", data=data)

Updates a user

  • username: Username of user to update
  • oid: ID of user to update (Use this if you already have the ID)
  • data: Data dictionary to update user with (optional)
  • return: PATCH response dictionary
Inherited Members
Controller
build
last_response
class InvalidToken(builtins.Exception):
803class InvalidToken(Exception):
804    """General Error for invalid API inputs
805    """
806    pass

General Error for invalid API inputs

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note