Add first version for usage as CLI Tool
NOT FINISHED!
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import dataclasses
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@@ -11,6 +12,14 @@ class AcAccount:
|
|||||||
class_: str
|
class_: str
|
||||||
status: 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:
|
def account_from_json(a) -> AcAccount:
|
||||||
a["class_"] = a["class"]
|
a["class_"] = a["class"]
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import dataclasses
|
||||||
|
import json
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@@ -47,6 +49,15 @@ class AcProject:
|
|||||||
updated_on: int
|
updated_on: int
|
||||||
url_path: str
|
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:
|
def project_from_json(json_obj: dict) -> AcProject:
|
||||||
json_obj["class_"] = json_obj["class"]
|
json_obj["class_"] = json_obj["class"]
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import dataclasses
|
||||||
|
import json
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from ActiveCollabAPI.AcAccount import AcAccount
|
from ActiveCollabAPI.AcAccount import AcAccount
|
||||||
@@ -11,3 +13,11 @@ class AcSession:
|
|||||||
accounts: [AcAccount]
|
accounts: [AcAccount]
|
||||||
cur_account: AcAccount
|
cur_account: AcAccount
|
||||||
token: AcToken
|
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())
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import dataclasses
|
||||||
|
import json
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@@ -7,3 +9,10 @@ class AcUser:
|
|||||||
first_name: str
|
first_name: str
|
||||||
last_name: str
|
last_name: str
|
||||||
intent: str
|
intent: str
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
d = dataclasses.asdict(self)
|
||||||
|
return d
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return json.dumps(self.to_dict())
|
||||||
@@ -53,6 +53,11 @@ class ActiveCollab:
|
|||||||
raise Exception('Request token failed! (2)')
|
raise Exception('Request token failed! (2)')
|
||||||
return AcToken(res_data["token"])
|
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):
|
def get_projects(self):
|
||||||
client = ACClient(self.session.cur_account, self.session.token)
|
client = ACClient(self.session.cur_account, self.session.token)
|
||||||
res = client.get_projects()
|
res = client.get_projects()
|
||||||
|
|||||||
@@ -14,39 +14,29 @@
|
|||||||
2. Request a token for some Account (user could have multiple accounts) using the Account `url` and the users `intent`
|
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
|
3. Use the token to authenticate for every other API-Call
|
||||||
|
|
||||||
Example login:
|
## CLI (Work in progress)
|
||||||
|
|
||||||
```python
|
> activecollab [options] [command] [object] [arguments]
|
||||||
auth = ACAuthenticator()
|
|
||||||
session = auth.login(username, password)
|
|
||||||
pprint(session)
|
|
||||||
user = session['user']
|
|
||||||
# use first account
|
|
||||||
account = auth.find_account(account_name)
|
|
||||||
```
|
|
||||||
|
|
||||||
Example request token
|
### Options
|
||||||
|
|
||||||
```python
|
- config=<file> - read config from file (json)
|
||||||
token = ACTokenAuthenticator(account, user).issue_token_intent()
|
|
||||||
pprint(token)
|
|
||||||
```
|
|
||||||
|
|
||||||
Example client request
|
### Command
|
||||||
|
|
||||||
```python
|
- list
|
||||||
ac = ACClient(account, token)
|
- get
|
||||||
res = ac.get_projects()
|
- create
|
||||||
pprint(res.json())
|
- 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"
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,115 +1,79 @@
|
|||||||
import configparser
|
import argparse
|
||||||
import logging
|
import json
|
||||||
from pprint import pprint
|
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
|
from ActiveCollabAPI.ActiveCollab import ActiveCollab
|
||||||
|
|
||||||
HTTPConnection.debuglevel = 1
|
|
||||||
|
|
||||||
logging.basicConfig() # you need to initialize logging, otherwise you will not see anything from requests
|
def run(args, config):
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
# run the command
|
||||||
requests_log = logging.getLogger("urllib3")
|
ac = ActiveCollab(config["base_url"])
|
||||||
requests_log.setLevel(logging.DEBUG)
|
ac.login_to_first_account(config["username"], config["password"])
|
||||||
requests_log.propagate = True
|
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():
|
def main():
|
||||||
config = configparser.ConfigParser()
|
# parse arguments
|
||||||
config.read('config.ini')
|
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']
|
subparsers = parser.add_subparsers(help='sub-command help')
|
||||||
password = config['LOGIN']['password']
|
|
||||||
account_name = config['LOGIN']['account_name']
|
|
||||||
|
|
||||||
# 1. you need to log in to get a session and a list of accounts
|
# account list
|
||||||
ac = ActiveCollab(AC_LOGIN_BASE_URL)
|
parser_account = subparsers.add_parser('account', help='account help')
|
||||||
session = ac.login(username, password)
|
parser_account.set_defaults(object='account')
|
||||||
account = ac.get_account(session) # default returning first account
|
parser_account.add_argument('command', choices=["list"], help='list all accessible accounts')
|
||||||
token = ac.get_token(account, session.user)
|
|
||||||
|
|
||||||
# 2. you need to get a token for this account
|
# project list get
|
||||||
token = ACTokenAuthenticator(account, auth.user).issue_token_intent()
|
parser_project = subparsers.add_parser('project', help='project help')
|
||||||
# pprint(token)
|
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
|
args = parser.parse_args()
|
||||||
ac = ACClient(account, token)
|
|
||||||
|
|
||||||
# check version
|
config = load_config(args)
|
||||||
res = ac.get_info()
|
output = run(args, config)
|
||||||
ac_version = res.json()
|
print(serialize_output(output))
|
||||||
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())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user