def get_data( ctx: click.Context, queue_id: int, files: Tuple[str], indent: int, ensure_ascii: bool, format_: str, output_file: Optional[IO[str]], ): annotations_to_export = list() with RossumClient(context=ctx.obj) as rossum: for file in files: json_response = rossum.upload_document(queue_id, file) annotation_id = get_id(json_response) annotations_to_export.append(annotation_id) rossum.poll_annotation(annotation_id, _is_done) export_data = rossum.export_data(queue_id, annotations_to_export, format_) if format_ == "json": output = json.dumps(get_json(export_data), indent=indent, ensure_ascii=ensure_ascii) else: output = get_text(export_data) click.echo(output.encode("utf-8"), file=output_file, nl=False)
def create_command( ctx: click.Context, username: str, password: Optional[str], queue_ids: Tuple[int], organization_id: Optional[int], group: str, locale: str, ) -> None: """ Create user with USERNAME and add him to QUEUES specified by ids. """ password = password or generate_secret() with RossumClient(context=ctx.obj) as rossum: if rossum.get_users(username=username): raise click.ClickException(f"User with username {username} already exists.") organization = rossum.get_organization(organization_id) workspaces = rossum.get_workspaces(organization=organization["id"], sideloads=(QUEUES,)) queues = chain.from_iterable(w[str(QUEUES)] for w in workspaces) queue_urls = [q["url"] for q in queues if q["id"] in queue_ids] response = rossum.create_user( username, organization["url"], queue_urls, password, group, locale ) click.echo(f"{response['id']}, {password}")
def list_command(ctx: click.Context, user_ids: Tuple[int], queue_ids: Tuple[int]) -> None: """List all users and their assignments to queues.""" with RossumClient(context=ctx.obj) as rossum: queue_users = rossum.get_queues((USERS, ), users=user_ids) user_queues: Dict[int, List[List[Optional[str]]]] = {} for queue in queue_users: if queue_ids and int(queue["id"]) not in queue_ids: continue for user in queue["users"]: user_id = int(user["id"]) if user_ids and user_id not in user_ids: continue if user_id not in user_queues: user_queues[user_id] = [[ user["id"], user["username"], queue["id"], queue["name"] ]] else: user_queues[user_id].append( [None, None, queue["id"], queue["name"]]) user_queues = dict(sorted(user_queues.items())) click.echo( tabulate( chain.from_iterable(user_queues.values()), headers=["id", "username", "queue id", "queue name"], ))
def list_command(ctx: click.Context, ): with RossumClient(context=ctx.obj) as rossum: hooks_list = rossum.get_hooks((QUEUES, )) headers = ["id", "name", "events", "queues", "active", "sideload"] def get_row(hook: dict) -> List[str]: fields = [ hook["id"], hook["name"], ", ".join(e for e in hook["events"]), ", ".join(str(q.get("id", "")) for q in hook["queues"]), hook["active"], hook["sideload"], ] additional = ["url", "insecure_ssl", "secret"] for field in additional: if field in hook["config"]: fields.append(hook["config"][field]) for header in additional: if header not in headers: headers.append(header) hook_list = [item for item in fields] return hook_list table = [get_row(hook) for hook in hooks_list] click.echo(tabulate(table, headers=headers))
def change_command( ctx: click.Context, id_: str, queue_ids: Tuple[int], name: Optional[str], service_url: str, auth_token: str, params: Optional[str], asynchronous: Optional[bool], ) -> None: if not any([queue_ids, service_url, auth_token, params, asynchronous]): return data: Dict[str, Any] = {} with RossumClient(context=ctx.obj) as rossum: if queue_ids: data["queues"] = [ rossum.get_queue(queue)["url"] for queue in queue_ids ] if name is not None: data["name"] = name if service_url is not None: data["service_url"] = service_url if auth_token is not None: data["authorization_token"] = auth_token if params is not None: data["params"] = params if asynchronous is not None: data["asynchronous"] = asynchronous rossum.patch(f"connectors/{id_}", data)
def create_command( ctx: click.Context, name: str, queue_ids: Tuple[int, ...], service_url: str, auth_token: str, params: Optional[str], asynchronous: Optional[bool], ) -> None: token = auth_token or _generate_token() with RossumClient(context=ctx.obj) as rossum: if not queue_ids: queue_urls = [rossum.get_queue()["url"]] else: queue_urls = [] for id_ in queue_ids: queue_dict = rossum.get_queue(id_) if queue_dict: queue_urls.append(queue_dict["url"]) response = rossum.create_connector( name=name, queues=queue_urls, service_url=service_url, authorization_token=token, params=params, asynchronous=asynchronous, ) click.echo( f"{response['id']}, {response['name']}, {response['queues']}")
class TestRetryMechanism: api_client = RossumClient(None, retry_logic_rules={ "attempts": 2, "wait_s": 0.1 }) @pytest.mark.usefixtures("mock_login_request") def test_retry_logic_if_api_responds_with_502(self, requests_mock): user_json = {"user": 123} get_user_called = requests_mock.get( f"{API_URL}/v1/auth/user", [ { "exc": requests.exceptions.ConnectionError("Connection refused") }, { "json": user_json, "status_code": 200 }, ], ) assert user_json == self.api_client.get_user() assert get_user_called.call_count == 2
def list_command(ctx: click.Context, ): with RossumClient(context=ctx.obj) as rossum: connectors_list = rossum.get_connectors((QUEUES, )) headers = ["id", "name", "service url", "queues", "params", "asynchronous"] def get_row(connector: dict) -> List[str]: res = [ connector["id"], connector["name"], connector["service_url"], ", ".join(str(q.get("id", "")) for q in connector["queues"]), connector["params"], connector["asynchronous"], ] try: token = connector["authorization_token"] except KeyError: pass else: res.append(token) if "authorization_token" not in headers: headers.append("authorization_token") return res table = [get_row(connector) for connector in connectors_list] click.echo(tabulate(table, headers=headers))
def upload_command(ctx: click.Context, id_: str, schema_content: List[dict], rewrite: bool, name: Optional[str]): """ Update schema in ROSSUM. """ upload_func = _rewrite_schema if rewrite else _create_schema with RossumClient(context=ctx.obj) as rossum: upload_func(id_, schema_content, rossum, name)
def create_command(ctx: click.Context, name: str, organization_id: Optional[int]) -> None: with RossumClient(context=ctx.obj) as rossum: organization_url = rossum.get_organization(organization_id)["url"] workspace_response = rossum.create_workspace(name, organization_url) click.echo(workspace_response["id"])
def change_command( ctx: click.Context, id_: int, name: Optional[str], schema_content: Optional[List[dict]], email_prefix: Optional[str], bounce_email: Optional[str], connector_id: Optional[int], hook_id: Optional[Tuple[int, ...]], locale: Optional[str], ) -> None: if not any([ name, schema_content, email_prefix, bounce_email, connector_id, locale, hook_id ]): return data: Dict[str, Any] = {} if name is not None: data["name"] = name if locale is not None: data["locale"] = locale with RossumClient(context=ctx.obj) as rossum: if email_prefix or bounce_email: queue_dict = rossum.get_queue(id_) if not queue_dict["inbox"]: inbox_dict = _create_inbox(rossum, queue_dict, email_prefix, bounce_email, name) else: inbox_dict = _patch_inbox(rossum, queue_dict, email_prefix, bounce_email) click.echo( f"{inbox_dict['id']}, {inbox_dict['email']}, {inbox_dict['bounce_email_to']}" ) if connector_id is not None: data["connector"] = get_json( rossum.get(f"connectors/{connector_id}"))["url"] if hook_id: hooks_urls = [] for hook in hook_id: hook_url = get_json(rossum.get(f"hooks/{hook}"))["url"] hooks_urls.append(hook_url) data["hooks"] = hooks_urls if schema_content is not None: name = name or rossum.get_queue(id_)["name"] schema_dict = rossum.create_schema(f"{name} schema", schema_content) data["schema"] = schema_dict["url"] if data: rossum.patch(f"queues/{id_}", data)
def change_command( ctx: click.Context, id_: int, queue_ids: Tuple[int, ...], name: Optional[str], hook_type: str, events: Tuple[str, ...], active: Optional[bool], sideload: Tuple[str, ...], token_owner: Optional[int], run_after: List[str] = None, test: Optional[str] = None, **kwargs, ) -> None: config = {**kwargs} config = cleanup_config(config) if not any([queue_ids, name, active, events, sideload, config]): return data: Dict[str, Any] = {"config": {}} with RossumClient(context=ctx.obj) as rossum: if queue_ids: data["queues"] = [ rossum.get_queue(queue)["url"] for queue in queue_ids ] if name is not None: data["name"] = name if hook_type: data["type"] = hook_type if active is not None: data["active"] = active if events: data["events"] = list(events) if config: data["config"] = config if sideload: data["sideload"] = list(sideload) if token_owner: data["token_owner"] = f"{rossum.url}/users/{token_owner}" if run_after: data["run_after"] = [ f"{rossum.url}/hooks/{hook_id}" for hook_id in run_after ] if test: try: loaded_test = json.loads(test) data["test"] = loaded_test except JSONDecodeError: click.echo( "Could not parse value for --test. Did you pass a valid JSON?" ) return rossum.patch(f"hooks/{id_}", data)
def remove_command(ctx: click.Context, user_ids: Tuple[int], queue_ids: Tuple[int]) -> None: with RossumClient(context=ctx.obj) as rossum: for user_id in user_ids: queues = rossum.get_queues(users=[user_id]) new_queues = [ q["url"] for q in queues if int(q["id"]) not in queue_ids ] rossum.patch(f"{USERS}/{user_id}", {str(QUEUES): new_queues})
def add_command(ctx: click.Context, user_ids: Tuple[int], queue_ids: Tuple[int]) -> None: with RossumClient(context=ctx.obj) as rossum: for user_id in user_ids: user = rossum.get_user(user_id) new_queues = user["queues"] + [ rossum.get_queue(q_id)["url"] for q_id in queue_ids ] rossum.patch(f"{USERS}/{user_id}", {str(QUEUES): new_queues})
def change_command(ctx: click.Context, id_: str, name: Optional[str]) -> None: if not any([name]): return data: Dict[str, Any] = {} if name is not None: data["name"] = name with RossumClient(context=ctx.obj) as rossum: rossum.patch(f"workspaces/{id_}", data)
def list_command(ctx: click.Context, ): with RossumClient(context=ctx.obj) as rossum: schemas = rossum.get_schemas((QUEUES, )) table = [[ schema["id"], schema["name"], ", ".join(str(s.get("id", "")) for s in schema["queues"]) ] for schema in schemas] click.echo(tabulate(table, headers=["id", "name", "queues"]))
def list_command(ctx: click.Context, ): with RossumClient(context=ctx.obj) as rossum: workspaces = rossum.get_workspaces((QUEUES, )) table = [[ workspace["id"], workspace["name"], ", ".join(str(q.get("id", "")) for q in workspace["queues"]), ] for workspace in workspaces] click.echo(tabulate(table, headers=["id", "name", "queues"]))
def list_command(ctx: click.Context,): with RossumClient(context=ctx.obj) as rossum: users_list = rossum.get_users((QUEUES, GROUPS), is_active=True) table = [ [ user["id"], user["username"], ", ".join(str(g["name"]) for g in user[str(GROUPS)]), ", ".join(str(q["id"]) for q in user[str(QUEUES)]), ] for user in users_list ] click.echo(tabulate(table, headers=["id", "username", "groups", "queues"]))
def delete_command(ctx: click.Context, id_: int) -> None: with RossumClient(context=ctx.obj) as rossum: workspace = rossum.get_workspace(id_) queues = rossum.get_queues(workspace=workspace["id"]) documents = {} for queue in queues: res, _ = rossum.get_paginated( "annotations", { "page_size": 50, "queue": queue["id"], "sideload": "documents" }, key="documents", ) documents.update({d["id"]: d["url"] for d in res}) rossum.delete({workspace["id"]: workspace["url"], **documents})
def change_command( ctx: click.Context, id_: int, queue_ids: Tuple[int], group: Optional[str], locale: Optional[str] ) -> None: if not any([queue_ids, group, locale]): return data: Dict[str, Any] = {} with RossumClient(context=ctx.obj) as rossum: if queue_ids: data[str(QUEUES)] = [rossum.get_queue(queue)["url"] for queue in queue_ids] if group is not None: data[str(GROUPS)] = [g["url"] for g in rossum.get_groups(group_name=group)] if locale is not None: ui_settings = rossum.get_user(id_)["ui_settings"] data["ui_settings"] = {**ui_settings, "locale": locale} rossum.patch(f"{USERS}/{id_}", data)
def create_command( ctx: click.Context, name: str, hook_type: str, queue_ids: Tuple[int, ...], active: bool, events: Tuple[str, ...], sideload: Tuple[str, ...], **kwargs, ) -> None: with RossumClient(context=ctx.obj) as rossum: if not queue_ids: queue_urls = [rossum.get_queue()["url"]] else: queue_urls = [] for id_ in queue_ids: queue_dict = rossum.get_queue(id_) if queue_dict: queue_urls.append(queue_dict["url"]) config = {**kwargs} config = cleanup_config(config) response = rossum.create_hook( name=name, hook_type=hook_type, queues=queue_urls, active=active, events=list(events), sideload=list(sideload), config=config, ) additional_fields = [ value for key, value in response["config"].items() if key not in ["code", "runtime", "insecure_ssl"] ] regular_fields = f"{response['id']}, {response['name']}, {response['queues']}, {response['events']}, {response['sideload']}" click.echo(regular_fields + ", " + f"{', '.join(map(str, additional_fields))}" if additional_fields != [] else regular_fields)
def change_command( ctx: click.Context, id_: int, queue_ids: Tuple[int, ...], name: Optional[str], hook_type: str, events: Tuple[str, ...], active: Optional[bool], sideload: Tuple[str, ...], **kwargs, ) -> None: config = {**kwargs} config = cleanup_config(config) if not any([queue_ids, name, active, events, sideload, config]): return data: Dict[str, Any] = {"config": {}} with RossumClient(context=ctx.obj) as rossum: if queue_ids: data["queues"] = [ rossum.get_queue(queue)["url"] for queue in queue_ids ] if name is not None: data["name"] = name if hook_type: data["type"] = hook_type if active is not None: data["active"] = active if events: data["events"] = list(events) if config: data["config"] = config if sideload: data["sideload"] = list(sideload) rossum.patch(f"hooks/{id_}", data)
def create_command( ctx: click.Context, name: str, schema_content: List[dict], email_prefix: Optional[str], bounce_email: Optional[str], workspace_id: Optional[int], connector_id: Optional[int], hook_id: Optional[Tuple[int, ...]], locale: Optional[str], ) -> None: if email_prefix is not None and bounce_email is None: raise click.ClickException( "Inbox cannot be created without specified bounce email.") with RossumClient(context=ctx.obj) as rossum: workspace_url = rossum.get_workspace(workspace_id)["url"] connector_url = (get_json( rossum.get(f"connectors/{connector_id}"))["url"] if connector_id is not None else None) hooks_urls = [] if hook_id: for hook in hook_id: hook_url = get_json(rossum.get(f"hooks/{hook}"))["url"] hooks_urls.append(hook_url) schema_dict = rossum.create_schema(f"{name} schema", schema_content) queue_dict = rossum.create_queue(name, workspace_url, schema_dict["url"], connector_url, hooks_urls, locale) inbox_dict = {"email": "no email-prefix specified"} if email_prefix is not None: inbox_dict = rossum.create_inbox(f"{name} inbox", email_prefix, bounce_email, queue_dict["url"]) click.echo(f"{queue_dict['id']}, {inbox_dict['email']}")
def list_command(ctx: click.Context, ) -> None: with RossumClient(context=ctx.obj) as rossum: queues = rossum.get_queues( (WORKSPACES, INBOXES, SCHEMAS, USERS, HOOKS)) table = [[ queue["id"], queue["name"], str(queue["workspace"].get("id", "")), queue["inbox"].get("email", ""), str(queue["schema"].get("id", "")), ", ".join(str(q.get("id", "")) for q in queue["users"]), queue["connector"], ", ".join(str(q.get("id", "")) for q in queue["hooks"]), ] for queue in queues] click.echo( tabulate( table, headers=[ "id", "name", "workspace", "inbox", "schema", "users", "connector", "hooks" ], ))
class TestRossumClient: api_client = RossumClient(None) @pytest.mark.usefixtures("mock_login_request") def test_get_organization_old_api(self, requests_mock): organization_json = {"test": "test"} user_url = f"{USERS_URL}/1" organization_url = f"{ORGANIZATIONS_URL}/1" requests_mock.get(f"{API_URL}/v1/auth/user", json={"url": user_url}) requests_mock.get(user_url, json={"organization": organization_url}) requests_mock.get(organization_url, json=organization_json) assert organization_json == self.api_client.get_organization() assert requests_mock.called @pytest.mark.usefixtures("mock_login_request") def test_upload_overwrite_filename(self, requests_mock, mock_file): original_filename = "empty_file.pdf" overwritten_filename = "Overwritten filename.pdf" api_response = {"results": [{"document": DOCUMENT_URL}]} requests_mock.post( UPLOAD_ENDPOINT, additional_matcher=partial(match_uploaded_data, original_filename), request_headers={"Authorization": f"Token {TOKEN}"}, json={"results": [{"document": DOCUMENT_URL}]}, status_code=201, ) assert api_response == self.api_client.upload_document(QUEUE_ID, mock_file) requests_mock.post( UPLOAD_ENDPOINT, additional_matcher=partial(match_uploaded_data, overwritten_filename), request_headers={"Authorization": f"Token {TOKEN}"}, json={"results": [{"document": DOCUMENT_URL}]}, status_code=201, ) assert api_response == self.api_client.upload_document( QUEUE_ID, mock_file, overwritten_filename ) @pytest.mark.usefixtures("mock_login_request") def test_upload_values(self, requests_mock, mock_file): values = {"upload:key_1": "value_1", "upload:key_2": "value_2"} api_response = { "document": DOCUMENT_URL, "annotation": ANNOTATION_URL, "results": [{"document": DOCUMENT_URL, "annotation": ANNOTATION_URL}], } requests_mock.post( UPLOAD_ENDPOINT, additional_matcher=partial(match_uploaded_values, values), request_headers={"Authorization": f"Token {TOKEN}"}, json=api_response, status_code=201, ) assert api_response == self.api_client.upload_document(QUEUE_ID, mock_file, values=values) @pytest.mark.usefixtures("mock_login_request") def test_set_metadata(self, requests_mock): metadata = {"key_1": 42, "key_2": "str_value", "nested_key": {"key_a": "value_a"}} api_response = { "document": DOCUMENT_URL, "id": DOCUMENT_ID, "queue": QUEUE_URL, "schema": SCHEMA_URL, "pages": [PAGE_URL], "modifier": None, "modified_at": None, "confirmed_at": None, "exported_at": None, "assigned_at": None, "status": "to_review", "rir_poll_id": "de8fb2e5877741bf97808eda", "messages": None, "url": ANNOTATION_URL, "content": f"{ANNOTATION_URL}/content", "time_spent": 0.0, "metadata": metadata, } requests_mock.patch( ANNOTATION_URL, additional_matcher=partial(match_uploaded_json, {"metadata": metadata}), request_headers={"Authorization": f"Token {TOKEN}"}, json=api_response, status_code=200, ) assert api_response == self.api_client.set_metadata(ANNOTATIONS, ANNOTATION_ID, metadata) @pytest.mark.usefixtures("mock_login_request") def test_create_workspace(self, requests_mock): name = "TestName" metadata = {"customer-id": "some-customer-id"} api_response = {"name": name, "organization": ORGANIZATION_URL, "metadata": metadata} requests_mock.post( WORKSPACES_URL, additional_matcher=partial( match_uploaded_json, {"name": name, "organization": ORGANIZATION_URL, "metadata": metadata}, ), request_headers={"Authorization": f"Token {TOKEN}"}, status_code=201, json=api_response, ) assert api_response == self.api_client.create_workspace(name, ORGANIZATION_URL, metadata)
def delete_command(ctx: click.Context, id_: str) -> None: with RossumClient(context=ctx.obj) as rossum: url = rossum.url rossum.delete(to_delete={f"{id_}": f"{url}/connectors/{id_}"}, item="connector")
def change_command(ctx: click.Context, password: str) -> None: with RossumClient(context=ctx.obj) as rossum: result = rossum.change_user_password(password) click.echo(result.get("detail")) click.echo('Run "rossum configure" to update existing credentials.')
def reset_command(ctx: click.Context, email: str) -> None: with RossumClient(context=ctx.obj) as rossum: result = rossum.reset_user_password(email) click.echo(result.get("detail"))
def delete_command(ctx: click.Context, id_: int) -> None: with RossumClient(context=ctx.obj) as rossum: queue = rossum.get_queue(id_) rossum.delete({queue["id"]: queue["url"]})
def create_command( ctx: click.Context, name: str, hook_type: str, queue_ids: Tuple[int, ...], active: bool, events: Tuple[str, ...], sideload: Tuple[str, ...], token_owner: Optional[int], run_after: List[str] = None, test: Optional[str] = None, **kwargs, ) -> None: with RossumClient(context=ctx.obj) as rossum: if not queue_ids: queue_urls = [rossum.get_queue()["url"]] else: queue_urls = [] for id_ in queue_ids: queue_dict = rossum.get_queue(id_) if queue_dict: queue_urls.append(queue_dict["url"]) config = {**kwargs} config = cleanup_config(config) token_owner_url = f"{rossum.url}/users/{token_owner}" if token_owner else None all_run_after_hooks = ( [f"{rossum.url}/hooks/{hook_id}" for hook_id in run_after] if run_after else []) try: loaded_test = json.loads(test) if test else {} except JSONDecodeError: click.echo( "Could not parse value for --test. Did you pass a valid JSON?") return response = rossum.create_hook( name=name, hook_type=hook_type, queues=queue_urls, active=active, events=list(events), sideload=list(sideload), config=config, token_owner=token_owner_url, run_after=all_run_after_hooks, test=loaded_test, ) additional_fields = [ value for key, value in response["config"].items() if key not in ["code", "runtime", "insecure_ssl"] ] regular_fields = f"{response['id']}, {response['name']}, {response['queues']}, {response['events']}, {response['sideload']}" click.echo(regular_fields + ", " + f"{', '.join(map(str, additional_fields))}" if additional_fields != [] else regular_fields)