diff --git a/ActiveCollabAPI/AcAccount.py b/ActiveCollabAPI/AcAccount.py index 0b3c254..6e691e8 100644 --- a/ActiveCollabAPI/AcAccount.py +++ b/ActiveCollabAPI/AcAccount.py @@ -1,3 +1,4 @@ +import dataclasses from dataclasses import dataclass @@ -11,6 +12,14 @@ class AcAccount: class_: str status: str + def to_dict(self): + d = dataclasses.asdict(self) + d["class"] = d["class_"] + del d["class_"] + return d + + def to_json(self): + return json.dumps(self.to_dict()) def account_from_json(a) -> AcAccount: a["class_"] = a["class"] diff --git a/ActiveCollabAPI/AcProject.py b/ActiveCollabAPI/AcProject.py index 4e4b814..07b91ce 100644 --- a/ActiveCollabAPI/AcProject.py +++ b/ActiveCollabAPI/AcProject.py @@ -1,3 +1,5 @@ +import dataclasses +import json from dataclasses import dataclass @@ -47,6 +49,15 @@ class AcProject: updated_on: int url_path: str + def to_dict(self): + d = dataclasses.asdict(self) + d["class"] = d["class_"] + del d["class_"] + return d + + def to_json(self): + return json.dumps(self.to_dict()) + def project_from_json(json_obj: dict) -> AcProject: json_obj["class_"] = json_obj["class"] diff --git a/ActiveCollabAPI/AcSession.py b/ActiveCollabAPI/AcSession.py index 723ec95..32b707c 100644 --- a/ActiveCollabAPI/AcSession.py +++ b/ActiveCollabAPI/AcSession.py @@ -1,3 +1,5 @@ +import dataclasses +import json from dataclasses import dataclass from ActiveCollabAPI.AcAccount import AcAccount @@ -11,3 +13,11 @@ class AcSession: accounts: [AcAccount] cur_account: AcAccount token: AcToken + + def to_dict(self): + d = dataclasses.asdict(self) + d["accounts"] = list(map(lambda a: a.to_dict(), self.accounts)) + return d + + def to_json(self): + return json.dumps(self.to_dict()) \ No newline at end of file diff --git a/ActiveCollabAPI/AcUser.py b/ActiveCollabAPI/AcUser.py index 98f8f93..6e15351 100644 --- a/ActiveCollabAPI/AcUser.py +++ b/ActiveCollabAPI/AcUser.py @@ -1,3 +1,5 @@ +import dataclasses +import json from dataclasses import dataclass @@ -7,3 +9,10 @@ class AcUser: first_name: str last_name: str intent: str + + def to_dict(self): + d = dataclasses.asdict(self) + return d + + def to_json(self): + return json.dumps(self.to_dict()) \ No newline at end of file diff --git a/ActiveCollabAPI/ActiveCollab.py b/ActiveCollabAPI/ActiveCollab.py index f25750f..47a000d 100644 --- a/ActiveCollabAPI/ActiveCollab.py +++ b/ActiveCollabAPI/ActiveCollab.py @@ -53,6 +53,11 @@ class ActiveCollab: raise Exception('Request token failed! (2)') return AcToken(res_data["token"]) + def get_info(self): + client = ACClient(self.session.cur_account, self.session.token) + res = client.get_info() + return res.json() + def get_projects(self): client = ACClient(self.session.cur_account, self.session.token) res = client.get_projects() diff --git a/README.md b/README.md index 9279c70..66c8d17 100644 --- a/README.md +++ b/README.md @@ -14,39 +14,29 @@ 2. Request a token for some Account (user could have multiple accounts) using the Account `url` and the users `intent` 3. Use the token to authenticate for every other API-Call -Example login: +## CLI (Work in progress) -```python -auth = ACAuthenticator() -session = auth.login(username, password) -pprint(session) -user = session['user'] -# use first account -account = auth.find_account(account_name) -``` +> activecollab [options] [command] [object] [arguments] -Example request token +### Options -```python -token = ACTokenAuthenticator(account, user).issue_token_intent() -pprint(token) -``` +- config= - read config from file (json) -Example client request +### Command -```python -ac = ACClient(account, token) -res = ac.get_projects() -pprint(res.json()) -``` +- list +- get +- create +- update +- delete +- +### Object -## Configuration +- info - get Info like version nummer from server +- project +- task +- subtask +- people +- invoice -config.ini: -```ini -[LOGIN] -username = you@example.com -password = very-secret -account_name = "#123456" -``` diff --git a/main.py b/main.py index fe3c53d..4eae362 100644 --- a/main.py +++ b/main.py @@ -1,115 +1,79 @@ -import configparser -import logging +import argparse +import json from pprint import pprint -from ActiveCollabAPI import AC_LOGIN_BASE_URL -from ActiveCollabAPI.ACAuthenticator import ACAuthenticator -from ActiveCollabAPI.ACClient import ACClient -from ActiveCollabAPI.ACTokenAuthenticator import ACTokenAuthenticator - -# Enabling debugging at http.client level (requests->urllib3->http.client) -# you will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA. -# the only thing missing will be the response.body which is not logged. -from http.client import HTTPConnection - from ActiveCollabAPI.ActiveCollab import ActiveCollab -HTTPConnection.debuglevel = 1 -logging.basicConfig() # you need to initialize logging, otherwise you will not see anything from requests -logging.getLogger().setLevel(logging.DEBUG) -requests_log = logging.getLogger("urllib3") -requests_log.setLevel(logging.DEBUG) -requests_log.propagate = True +def run(args, config): + # run the command + ac = ActiveCollab(config["base_url"]) + ac.login_to_first_account(config["username"], config["password"]) + output = None + if args.object == "info": + output = run_info(ac, args) + if args.object == "account": + output = run_account(ac, args) + if args.object == "project": + output = run_project(ac, args, output) + return output + + +def run_account(ac, args): + if args.command == "list": + return ac.session.to_dict() + + +def run_project(ac, args, output): + if args.command == "list": + output = list(map(lambda p: p.to_dict(), ac.get_projects())) + return output + + +def run_info(ac, args): + output = ac.get_info() + return output + + +def serialize_output(output): + # serialize the output + return json.dumps(output) + + +def load_config(args): + with open(args.config, "r", encoding="utf8") as fh: + config = json.load(fh) + return config def main(): - config = configparser.ConfigParser() - config.read('config.ini') + # parse arguments + parser = argparse.ArgumentParser( + prog='activecollab', + description='This is a CLI Client to Active-Collab Service', + epilog='This is not a offical tool provided by the AC Guys!') + parser.add_argument('-c', '--config', required=True, + help="use the named config file") + parser.add_argument('-v', '--version', action='store_true', + help="show version information for this tool") - username = config['LOGIN']['username'] - password = config['LOGIN']['password'] - account_name = config['LOGIN']['account_name'] + subparsers = parser.add_subparsers(help='sub-command help') - # 1. you need to log in to get a session and a list of accounts - ac = ActiveCollab(AC_LOGIN_BASE_URL) - session = ac.login(username, password) - account = ac.get_account(session) # default returning first account - token = ac.get_token(account, session.user) + # account list + parser_account = subparsers.add_parser('account', help='account help') + parser_account.set_defaults(object='account') + parser_account.add_argument('command', choices=["list"], help='list all accessible accounts') - # 2. you need to get a token for this account - token = ACTokenAuthenticator(account, auth.user).issue_token_intent() - # pprint(token) + # project list get + parser_project = subparsers.add_parser('project', help='project help') + parser_project.set_defaults(object='project') + parser_project.add_argument('command', choices=["list"], help='what to do with the account help') - # 3. now you can query the API - ac = ACClient(account, token) + args = parser.parse_args() - # check version - res = ac.get_info() - ac_version = res.json() - pprint(ac_version) - if ac_version['version'] != '7.4.337': - raise Exception("Supporting only Version 7.4.337!") - - # get a list of all projects - # res = ac.get_projects() - # pprint(res.json()) - - # get task with sub-tasks - # "https://app.activecollab.com/416910/projects/310/tasks/6260" - # project_id = 310 - # task_id = 6260 - # task_res = ac.get_project_task(project_id, task_id) - # pprint(task_res.json()) - # task = task_res.json() - # ac.delete_all_open_subtasks(task) - - # create a new subtask for the task without assignment - # subtask = { - # "task_id": task_id, - # "project_id": project_id, - # "assignee_id": "", - # "body": "test qqq" - # } - # res = ac.create_subtask(subtask) - # pprint(res.json()) - - # # bulk create subtasks - # subtask_texts = [] - # with open("sub-tasks.txt", "r", encoding="utf-8") as fh: - # for line in fh: - # subtask_texts.append(line.rstrip('\n')) - # create - # for text in subtask_texts: - # subtask['body'] = text - # ac.create_subtask(subtask) - # delete - # for text in subtask_texts: - # subtask = ac.find_subtask_with_body(task, text) - # if subtask is not None: - # print(subtask) - # ac.delete_subtask(subtask) - - # # unassign a single subtask - # subtask_id = 12203 - # subtask_res = ac.get_project_task_subtask(project_id, task_id, subtask_id) - # # pprint(subtask_res.json()) - # subtask = subtask_res.json()['single'] - # res = ac.unassign_sub_task(subtask) - # pprint(res.json()) - - # delete a subtask - # subtask = { - # "task_id": task_id, - # "project_id": project_id, - # "id": 13764 - # } - # res = ac.delete_subtask(subtask) - # pprint(res.json()) - - res = ac.get_projects() - if res.status_code == 200: - pprint(res.json()) + config = load_config(args) + output = run(args, config) + print(serialize_output(output)) if __name__ == "__main__":