805 lines
No EOL
35 KiB
Python
805 lines
No EOL
35 KiB
Python
import requests
|
|
import json
|
|
|
|
API_URL = "http://localhost:3000"
|
|
API_USER = "demo- demo.demo"
|
|
API_PASS = "demo"
|
|
OFFSET = 65535
|
|
|
|
class Planka:
|
|
"""API wrapper class for Planka
|
|
- url: URL of Planka instance
|
|
- username: Username of Planka user
|
|
- password: Password of Planka user
|
|
"""
|
|
def __init__(self, url:str, username:str, password:str, templates="config/templates.json"):
|
|
self.url = url
|
|
self.username = username
|
|
self.password = password
|
|
self.auth = None
|
|
with open(templates) as f:
|
|
self.templates = json.load(f)
|
|
self.authenticate()
|
|
|
|
def __repr__(self):
|
|
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>"
|
|
|
|
def deauthenticate(self) -> bool:
|
|
"""Deletes the auth token from the Planka API
|
|
- return: True if successful, False if not
|
|
"""
|
|
try:
|
|
self.request("DELETE", "/api/access-tokens/me")
|
|
self.auth = None
|
|
return True
|
|
except:
|
|
raise InvalidToken(f"No active access token assigned to this instance\n{self.__repr__()}")
|
|
|
|
def validate(self) -> bool:
|
|
"""Validates the Planka API connection
|
|
- return: True if successful, False if not
|
|
"""
|
|
try:
|
|
self.request("GET", "/*")
|
|
return True
|
|
except:
|
|
raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
|
|
|
|
def authenticate(self) -> bool:
|
|
"""Gets an auth token from the Planka API
|
|
- return: True if successful, False if not
|
|
"""
|
|
try:
|
|
request = requests.post(f"{self.url}/api/access-tokens", data={'emailOrUsername': self.username, 'password': self.password})
|
|
self.auth = request.json()['item']
|
|
if not self.auth:
|
|
raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
|
|
return True
|
|
except:
|
|
raise InvalidToken(f"Invalid API credentials\n{self.__repr__()}")
|
|
|
|
def request(self, method:str, endpoint:str, data:dict=None) -> dict:
|
|
"""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
|
|
"""
|
|
if not self.auth:
|
|
self.authenticate()
|
|
headers = \
|
|
{
|
|
"Content-Type": "application/json",
|
|
"Authorization": f"Bearer {self.auth}"
|
|
}
|
|
url = f"{self.url}{endpoint}"
|
|
response = requests.request(method, url, headers=headers, json=data)
|
|
|
|
if response.status_code == 401:
|
|
raise InvalidToken("Invalid API credentials")
|
|
|
|
if response.status_code not in [200, 201]:
|
|
raise InvalidToken(f"Failed to {method} {url} with status code {response.status_code}")
|
|
|
|
try:
|
|
return response.json()
|
|
except:
|
|
raise InvalidToken(f"Failed to parse response from {url}")
|
|
|
|
def get_template(self, template:str) -> dict:
|
|
"""Returns a template from the templates.json file
|
|
- template: Name of template to return
|
|
- return: Template dictionary
|
|
"""
|
|
try:
|
|
return self.templates[template]
|
|
except:
|
|
raise InvalidToken(f"Template not found: {template}")
|
|
|
|
class Controller():
|
|
def __init__(self, instance:Planka) -> None:
|
|
"""Controller class for Planka API
|
|
- instance: Planka API instance
|
|
"""
|
|
self.instance = instance
|
|
self.template:dict = None
|
|
self.data:dict = None
|
|
self.response:dict = None
|
|
|
|
def __str__(self) -> str:
|
|
"""Returns a string representation of the controller object
|
|
- return: String representation of controller object
|
|
"""
|
|
return f"{type(self).__name__}:\n{json.dumps(self.data, sort_keys=True, indent=4)}"
|
|
|
|
def __repr__(self) -> str:
|
|
"""Returns a string representation of the controller object
|
|
- return: String representation of controller object
|
|
"""
|
|
return f"<{type(self).__name__}({self.__class__.__bases__[0].__name__})>{self.__str__()}"
|
|
|
|
def build(self, **kwargs) -> dict:
|
|
"""Builds the controller data
|
|
- return: Controller data dictionary
|
|
"""
|
|
if not kwargs:
|
|
return kwargs
|
|
valid_keys = self.template.keys()
|
|
data = {key: value for key, value in kwargs.items() if key in valid_keys}
|
|
self.data = data
|
|
return self.data
|
|
|
|
def create(self, route:str, data:dict=None) -> dict:
|
|
"""Creates a new controller object (POST)
|
|
- route: Route for controller object POST request
|
|
- return: POST response dictionary
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
self.response = self.instance.request("POST", route, data)
|
|
return self.response
|
|
|
|
def get(self, route:str) -> dict:
|
|
"""Gets a controller object (GET)
|
|
- route: Route for controller object GET request
|
|
- return: GET response dictionary
|
|
"""
|
|
return self.instance.request("GET", route)
|
|
|
|
def update(self, route:str, data:dict=None) -> dict:
|
|
"""Updates a controller object (PATCH)
|
|
- route: Route for controller object PATCH request
|
|
- oid: ID of controller object
|
|
- return: PATCH response dictionary
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not self.data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
|
|
self.response = self.instance.request("PATCH", route, data=data)
|
|
return self.response
|
|
|
|
def delete(self, route:str) -> dict:
|
|
"""Deletes a controller object (DELETE)
|
|
- route: Route for controller object DELETE request
|
|
- oid: ID of controller object
|
|
- return: DELETE response dictionary
|
|
"""
|
|
return self.instance.request("DELETE", route)
|
|
|
|
def last_response(self) -> dict:
|
|
"""Returns the last response from the controller object
|
|
- return: Last response dictionary
|
|
"""
|
|
return self.response
|
|
|
|
class Project(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("project")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def get(self, name:str=None, oid:str=None) -> dict:
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().get(f"/api/projects/{oid}")
|
|
prjs = super().get("/api/projects")
|
|
if not name:
|
|
return prjs
|
|
prj_names = [prj["name"] for prj in prjs["items"]]
|
|
if name not in prj_names:
|
|
raise InvalidToken(f"Project {name} not found")
|
|
prj_id = [prj for prj in prjs["items"] if prj["name"] == name][0]["id"]
|
|
return super().get(f"/api/projects/{prj_id}")
|
|
|
|
def get_project_names(self) -> list:
|
|
"""Gets a list of project names
|
|
- return: List of project names
|
|
"""
|
|
return [prj["name"] for prj in self.get()['items']]
|
|
|
|
def create(self) -> dict:
|
|
"""Creates a new project
|
|
- return: POST response dictionary
|
|
"""
|
|
if not self.data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
if self.data["name"] in [prj["name"] for prj in self.get()['items']]:
|
|
raise InvalidToken(f"Project {self.data['name']} already exists")
|
|
return super().create("/api/projects")
|
|
|
|
def update(self, name:str) -> dict:
|
|
"""Updates a project
|
|
- name: Name of project to update
|
|
- return: PATCH response dictionary
|
|
"""
|
|
prj_id = prj_id = self.get(name)['item']['id']
|
|
return super().update(f"/api/projects/{prj_id}")
|
|
|
|
def delete(self, name:str) -> dict:
|
|
"""Deletes a project
|
|
- name: Name of project to delete
|
|
- return: DELETE response dictionary
|
|
"""
|
|
prj_id = self.get(name)['item']['id']
|
|
return super().delete(f"/api/projects/{prj_id}")
|
|
|
|
class Board(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("board")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def get(self, project_name:str=None, board_name:str=None, oid:str=None) -> dict:
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().get(f"/api/boards/{oid}")
|
|
if not (project_name):
|
|
raise InvalidToken("Please provide a project name")
|
|
prj_con = Project(self.instance)
|
|
prj = prj_con.get(project_name)
|
|
boards = prj["included"]["boards"]
|
|
if not board_name:
|
|
return boards
|
|
board_names = [board["name"] for board in boards]
|
|
if board_name not in board_names:
|
|
raise InvalidToken(f"Board `{board_name}` not found")
|
|
board_id = [board for board in boards if board["name"] == board_name][0]["id"]
|
|
return super().get(f"/api/boards/{board_id}")
|
|
|
|
def create(self, project_name:str) -> dict:
|
|
"""Creates a new board
|
|
- prj_name: Name of project to create board in
|
|
- return: POST response dictionary
|
|
"""
|
|
if not self.data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
prj_con = Project(self.instance)
|
|
prj_id = prj_con.get(project_name)['item']['id']
|
|
return super().create(f"/api/projects/{prj_id}/boards")
|
|
|
|
def update(self, project_name:str=None, board_name:str=None, data:dict=None, oid:str=None) -> dict:
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
|
|
if oid:
|
|
return super().update(f"/api/boards/{oid}", data=data)
|
|
if not (project_name and board_name):
|
|
raise InvalidToken("Please provide project and board names")
|
|
board_id = self.get(project_name, board_name)['item']['id']
|
|
return super().update(f"/api/boards/{board_id}", data=self.data)
|
|
|
|
def delete(self, project_name:str=None, board_name:str=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().delete(f"/api/boards/{oid}")
|
|
if not project_name:
|
|
raise InvalidToken("Please provide a project name")
|
|
if not board_name:
|
|
raise InvalidToken("Please provide a board name")
|
|
board_id = self.get(project_name, board_name)['item']['id']
|
|
return super().delete(f"/api/boards/{board_id}")
|
|
|
|
class List(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("list")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def get(self, project_name:str=None, board_name:str=None, list_name:str=None):
|
|
"""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
|
|
"""
|
|
if not (project_name and board_name):
|
|
raise InvalidToken("Please provide project and board names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)
|
|
lists = board["included"]["lists"]
|
|
list_names = [lst["name"] for lst in lists]
|
|
if not list_name:
|
|
return lists
|
|
if list_name not in list_names:
|
|
raise InvalidToken(f"List `{list_name}` not found")
|
|
return [lst for lst in lists if lst["name"] == list_name][0]
|
|
|
|
def create(self, project_name:str=None, board_name:str=None, data:dict=None):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
if not (project_name and board_name):
|
|
raise InvalidToken("Please provide project and board name")
|
|
board_con = Board(self.instance)
|
|
board_id = board_con.get(project_name, board_name)['item']['id']
|
|
return super().create(f"/api/boards/{board_id}/lists")
|
|
|
|
def update(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
|
|
if oid:
|
|
return super().update(f"/api/lists/{oid}", data=data)
|
|
if not (project_name and board_name and list_name):
|
|
raise InvalidToken("Please provide project, board, and list names")
|
|
lst = self.get(project_name, board_name, list_name)
|
|
return super().update(f"/api/lists/{lst['id']}", data=data)
|
|
|
|
def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().delete(f"/api/lists/{oid}")
|
|
if not (project_name and board_name and list_name):
|
|
raise InvalidToken("Please provide a project, board, and list names")
|
|
lst = self.get(project_name, board_name, list_name)
|
|
return super().delete(f"/api/lists/{lst['id']}")
|
|
|
|
class Card(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("card")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid != None:
|
|
return super().get(f"/api/cards/{oid}")
|
|
if not (project_name and board_name and list_name):
|
|
raise InvalidToken("Please provide project, board, and list names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)
|
|
lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
|
|
cards = [card for card in board["included"]["cards"] if card["listId"] == lst_id]
|
|
card_names = [card["name"] for card in cards]
|
|
if not card_name:
|
|
return [self.get(oid=card["id"]) for card in cards]
|
|
if card_name not in card_names:
|
|
raise InvalidToken(f"Card `{card_name}` not found")
|
|
card_id = [card for card in cards if card["name"] == card_name][0]['id']
|
|
return super().get(f"/api/cards/{card_id}")
|
|
|
|
def create(self, project_name:str=None, board_name:str=None, list_name:str=None, data:dict=None):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
if not (project_name and board_name and list_name):
|
|
raise InvalidToken("Please provide a project, board and list names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)
|
|
lst_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
|
|
return super().create(f"/api/lists/{lst_id}/cards")
|
|
|
|
def delete(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid != None:
|
|
return super().delete(f"/api/cards/{oid}")
|
|
if not (project_name and board_name and list_name and card_name):
|
|
raise InvalidToken("Please provide a project, board, list, and card name")
|
|
card = self.get(project_name, board_name, list_name, card_name)
|
|
return super().delete(f"/api/cards/{card['id']}")
|
|
|
|
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):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
|
|
if oid:
|
|
return super().update(f"/api/cards/{oid}", data=data)
|
|
if not (project_name and board_name and list_name and card_name):
|
|
raise InvalidToken("Please provide a project, board, list, and card name")
|
|
card = self.get(project_name, board_name, list_name, card_name)
|
|
return super().update(f"/api/cards/{card['id']}", data=data)
|
|
|
|
def get_labels(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return self.get(oid=oid)['included']['cardLabels']
|
|
if not (project_name and board_name and list_name and card_name):
|
|
raise InvalidToken("Please provide project, board, list, and card names")
|
|
card_id = self.get(project_name, board_name, list_name, card_name)['item']['id']
|
|
return self.get(oid=card_id)['included']['cardLabels']
|
|
|
|
class Label(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("label")
|
|
self.options = instance.get_template("colors")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def colors(self) -> list:
|
|
return self.options
|
|
|
|
def get(self, project_name:str=None, board_name:str=None, label_name:str=None) -> dict:
|
|
"""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
|
|
"""
|
|
if not (project_name and board_name):
|
|
raise InvalidToken("Please provide project and board names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)
|
|
labels = board["included"]["labels"]
|
|
label_names = [label["name"] for label in labels]
|
|
if not label_name:
|
|
return labels
|
|
if label_name not in label_names:
|
|
raise InvalidToken(f"Label `{label_name}` not found")
|
|
return [label for label in labels if label["name"] == label_name][0]
|
|
|
|
def create(self, project_name:str=None, board_name:str=None, data:dict=None):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
if not (project_name and board_name):
|
|
raise InvalidToken("Please provide project and board names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)['item']
|
|
return super().create(f"/api/boards/{board['id']}/labels")
|
|
|
|
def delete(self, project_name:str=None, board_name:str=None, label_name:str=None, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().delete(f"/api/labels/{oid}")
|
|
if not (project_name and board_name and label_name):
|
|
raise InvalidToken("Please provide project, board, and label names")
|
|
label = self.get(project_name, board_name, label_name)
|
|
return super().delete(f"/api/labels/{label['id']}")
|
|
|
|
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):
|
|
"""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
|
|
"""
|
|
if label_id and card_id:
|
|
return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label_id})
|
|
if not (project_name and board_name and label_name):
|
|
raise InvalidToken("Please provide a project, board, label name")
|
|
if card_id:
|
|
label = self.get(project_name, board_name, label_name)
|
|
return super().create(f"/api/cards/{card_id}/labels", data={"labelId":label['item']['id']})
|
|
if not (card_name and list_name):
|
|
raise InvalidToken("Please provide a card and list name")
|
|
card_con = Card(self.instance)
|
|
card = card_con.get(project_name, board_name, list_name, card_name)
|
|
label = self.get(project_name, board_name, label_name)
|
|
return super().create(f"/api/cards/{card['item']['id']}/labels", {"labelId":label['item']['id']})
|
|
|
|
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):
|
|
"""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
|
|
"""
|
|
if label_id and card_id:
|
|
return super().delete(f"/api/cards/{card_id}/labels/{label_id}")
|
|
if not (project_name and board_name and label_name):
|
|
raise InvalidToken("Please provide a project, board, label name")
|
|
if card_id:
|
|
label_id = [label['id'] for label in Card(self.instance).get_labels(oid=card_id) if label['name'] == label_name][0]
|
|
return super().delete(f"/api/cards/{card_id}/labels/{label['item']['id']}")
|
|
if not (card_name and list_name):
|
|
raise InvalidToken("Please provide a card and list name")
|
|
card_con = Card(self.instance)
|
|
card = card_con.get(project_name, board_name, list_name, card_name)
|
|
label = self.get(project_name, board_name, label_name)
|
|
return super().delete(f"/api/cards/{card['item']['id']}/labels/{label['item']['id']}")
|
|
|
|
class Task(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("task")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def get(self, project_name:str=None, board_name:str=None, list_name:str=None, card_name:str=None, task_name:str=None) -> dict:
|
|
"""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
|
|
"""
|
|
if not (project_name and board_name and list_name and card_name):
|
|
raise InvalidToken("Please provide project, board, list, and card names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)
|
|
list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
|
|
cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
|
|
card_id = [card for card in cards if card["name"] == card_name][0]["id"]
|
|
tasks = [task for task in board["included"]["tasks"] if task["cardId"] == card_id]
|
|
task_names = [task["name"] for task in tasks]
|
|
if not task_name:
|
|
return tasks
|
|
if task_name not in task_names:
|
|
raise InvalidToken(f"Task `{task_name}` not found")
|
|
return [task for task in tasks if task["name"] == task_name][0]
|
|
|
|
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):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before creating")
|
|
if card_id:
|
|
return super().create(f"/api/cards/{card_id}/tasks")
|
|
if not (project_name and board_name and list_name and card_name):
|
|
raise InvalidToken("Please provide project, board, list, and card names")
|
|
board_con = Board(self.instance)
|
|
board = board_con.get(project_name, board_name)
|
|
list_id = [ls for ls in board["included"]["lists"] if ls["name"] == list_name][0]["id"]
|
|
cards = [card for card in board["included"]["cards"] if card["name"] == card_name and card["listId"] == list_id]
|
|
card_id = [card for card in cards if card["name"] == card_name][0]["id"]
|
|
return super().create(f"/api/cards/{card_id}/tasks")
|
|
|
|
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):
|
|
"""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
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken(f"Please Build a {type(self).__name__} before updating")
|
|
if oid:
|
|
return super().update(f"/api/tasks/{oid}")
|
|
if not (project_name and board_name and list_name and card_name and task_name):
|
|
raise InvalidToken("Please provide project, board, list, card, and task names")
|
|
task = self.get(project_name, board_name, list_name, card_name, task_name)
|
|
return super().update(f"/api/tasks/{task['id']}")
|
|
|
|
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):
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().delete(f"/api/tasks/{id}")
|
|
if not (project_name and board_name and list_name and card_name and task_name):
|
|
raise InvalidToken("Please provide project, board, list, card, and task names")
|
|
task = self.get(project_name, board_name, list_name, card_name, task_name)
|
|
return super().delete(f"/api/tasks/{task['id']}")
|
|
|
|
class Attachment(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("attachment")
|
|
self.data = self.build(**kwargs)
|
|
|
|
class Stopwatch(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("stopwatch")
|
|
self.data = self.build(**kwargs)
|
|
|
|
class Background(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("background")
|
|
self.options = instance.get_template("gradients")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def gradients(self) -> dict:
|
|
"""Gets all gradients
|
|
- return: GET response dictionary
|
|
"""
|
|
return self.options
|
|
|
|
def apply(self, prj_name:str):
|
|
"""Applies a gradient to a project
|
|
- project: Name of project to apply gradient to
|
|
- return: PATCH response dictionary
|
|
"""
|
|
project = Project(self.instance)
|
|
prj_id = project.get(prj_name)["item"]["id"]
|
|
if "type" not in self.data.keys():
|
|
raise InvalidToken("Please specify a background type: `gradient` | `image`")
|
|
if self.data["type"] == "gradient" and self.data["name"] not in self.options:
|
|
raise InvalidToken(f"Gradient {self.data['name']} not found: please choose from\n{self.options}")
|
|
return super().update(f"/api/projects/{prj_id}", data={"background": self.data})
|
|
|
|
def clear(self, prj_name:str):
|
|
"""Clears a gradient from a project
|
|
- project: Name of project to clear gradient from
|
|
- return: PATCH response dictionary
|
|
"""
|
|
project = Project(self.instance)
|
|
prj_id = project.get(prj_name)["item"]["id"]
|
|
return super().update(f"/api/projects/{prj_id}", data={"background": None})
|
|
|
|
class Comment(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
self.instance = instance
|
|
self.template = instance.get_template("comment-action")
|
|
self.data = self.build(**kwargs)
|
|
|
|
class User(Controller):
|
|
def __init__(self, instance:Planka, **kwargs) -> None:
|
|
"""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)
|
|
"""
|
|
self.instance = instance
|
|
self.template = instance.get_template("user")
|
|
self.data = self.build(**kwargs)
|
|
|
|
def get(self, username:str=None):
|
|
"""Gets a user
|
|
- username: Username of user to get (all if not provided)
|
|
- return: GET response dictionary
|
|
"""
|
|
if not username:
|
|
return super().get("/api/users")["items"]
|
|
users = super().get("/api/users")["items"]
|
|
names = [user["username"] for user in users]
|
|
if username not in names:
|
|
raise InvalidToken(f"User {username} not found")
|
|
return users[names.index(username)]
|
|
|
|
def create(self, data:dict=None):
|
|
"""Creates a user
|
|
- data: Data dictionary to create user with (optional)
|
|
- return: POST response dictionary
|
|
"""
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken("Please either build a user or provide a data dictionary")
|
|
if self.data["username"] in [user["username"] for user in self.get()]:
|
|
raise InvalidToken(f"User {self.data['username']} already exists")
|
|
return super().create("/api/users", data=self.data)
|
|
|
|
def delete(self, username:str, oid:str=None):
|
|
"""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
|
|
"""
|
|
if oid:
|
|
return super().delete(f"/api/users/{oid}")
|
|
if username not in [user["username"] for user in self.get()]:
|
|
raise InvalidToken(f"User {username} not found")
|
|
return super().delete(f"/api/users/{self.get(username)['id']}")
|
|
|
|
def update(self, username:str, oid:str=None, data:dict=None):
|
|
"""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
|
|
"""
|
|
user = self.get(username)
|
|
if not data:
|
|
data = self.data
|
|
if not data:
|
|
raise InvalidToken("Please either build a user or provide a data dictionary")
|
|
return super().update(f"/api/users/{user['id']}", data=data)
|
|
|
|
class InvalidToken(Exception):
|
|
"""General Error for invalid API inputs
|
|
"""
|
|
pass |