def get_by_id(cls, task_id: str) -> "Task": """Returns the task specified by the passed id (if your token authorizes you to see it)""" client = _get_generated_client(enforce_auth=True) response = task_info.sync(id=task_id, client=client) assert (response is not None ), f"Requesting task infos from {client.base_url} failed." return cls._from_generated_response(response)
def __init__( self, organization_id: Optional[str], tags: Optional[Union[str, Sequence[str]]], ) -> None: from webknossos.administration.user import User from webknossos.client._generated.api.default import dataset_list from webknossos.client.context import _get_generated_client from webknossos.dataset.dataset import Dataset client = _get_generated_client(enforce_auth=True) if organization_id is None: organization_id = User.get_current_user().organization_id if isinstance(tags, str): tags = [tags] response = dataset_list.sync(client=client) assert response is not None ds_names = [] for ds_info in response: tags_match = tags is None or any(tag in tags for tag in ds_info.tags) if ds_info.owning_organization == organization_id and tags_match: ds_names.append(ds_info.name) super().__init__( entries=dict(zip(ds_names, ds_names)), func=lambda name: Dataset.open_remote(name, organization_id), )
def get_annotation_infos(self) -> List[AnnotationInfo]: """Returns AnnotationInfo objects describing all task instances that have been started by annotators for this task""" client = _get_generated_client(enforce_auth=True) response = annotation_infos_by_task_id.sync(id=self.task_id, client=client) assert ( response is not None ), f"Requesting annotation infos for task from {client.base_url} failed." return [AnnotationInfo._from_generated_response(a) for a in response]
def get_by_name(cls, name: str) -> "Team": """Returns the Team specified by the passed name if your token authorizes you to see it.""" client = _get_generated_client(enforce_auth=True) response = team_list.sync(client=client) assert response is not None, "Could not fetch teams." for team in response: if team.name == name: return cls(team.id, team.name, team.organization) raise KeyError(f"Could not find team {name}.")
def create( cls, task_type_id: str, project_name: str, dataset_name: Union[str, RemoteDataset], needed_experience_domain: str, needed_experience_value: int, starting_position: Vec3Int, starting_rotation: Optional[Vec3Int] = Vec3Int(0, 0, 0), instances: int = 1, script_id: Optional[str] = None, bounding_box: Optional[BoundingBox] = None, ) -> List["Task"]: """Submits tasks in webKnossos based on a dataset, starting position + rotation, and returns the Task objects""" client = _get_generated_client(enforce_auth=True) url = f"{client.base_url}/api/tasks" if isinstance(dataset_name, RemoteDataset): dataset_name = dataset_name._dataset_name task_parameters = { "taskTypeId": task_type_id, "neededExperience": { "domain": needed_experience_domain, "value": needed_experience_value, }, "openInstances": instances, "projectName": project_name, "scriptId": script_id, "dataSet": dataset_name, "editPosition": starting_position, "editRotation": starting_rotation, "boundingBox": bounding_box.to_wkw_dict() if bounding_box is not None else None, } response = httpx.post( url=url, headers=client.get_headers(), cookies=client.get_cookies(), timeout=client.get_timeout(), json=[task_parameters], ) assert ( response.status_code == 200 ), f"Failed to create tasks: {response.status_code}: {response.text}" return cls._handle_task_creation_response(response)
def create_from_annotations( cls, task_type_id: str, project_name: str, base_annotations: List[Annotation], needed_experience_domain: str, needed_experience_value: int, instances: int = 1, script_id: Optional[str] = None, bounding_box: Optional[BoundingBox] = None, ) -> List["Task"]: """Submits tasks in webKnossos based on existing annotations, and returns the Task objects""" assert (len(base_annotations) > 0), "Must supply at least one base annotation to create tasks" client = _get_generated_client(enforce_auth=True) url = f"{client.base_url}/api/tasks/createFromFiles" task_parameters = { "taskTypeId": task_type_id, "neededExperience": { "domain": needed_experience_domain, "value": needed_experience_value, }, "openInstances": instances, "projectName": project_name, "scriptId": script_id, "boundingBox": bounding_box.to_wkw_dict() if bounding_box is not None else None, } form_data = {"formJSON": json.dumps(task_parameters)} files: Mapping[str, Tuple[str, Union[bytes, BinaryIO]]] = { f"{a.name}.zip": (f"{a.name}.zip", a._binary_zip()) for a in base_annotations } response = httpx.post( url=url, headers=client.get_headers(), cookies=client.get_cookies(), timeout=client.get_timeout(), data=form_data, files=files, ) assert ( response.status_code == 200 ), f"Failed to create tasks from files: {response.status_code}: {response.text}" return cls._handle_task_creation_response(response)
def get_logged_times(self) -> List["LoggedTime"]: """Get the logged times of this user. Returns a list of `LoggedTime` objects where one represents one month.""" client = _get_generated_client(enforce_auth=True) response = user_logged_time.sync(id=self.user_id, client=client) assert response is not None, f"Could not fetch logged time of {self}" return [ LoggedTime( duration_in_seconds=i.duration_in_seconds, month=i.payment_interval.month, year=i.payment_interval.year, ) for i in response.logged_time ]
def get_tasks(self, fetch_all: bool = False) -> List["Task"]: """Returns the tasks of this project. Note: will fetch only the first 1000 entries by default, warns if that means some are missing. set parameter pass fetch_all=True to use pagination to fetch all tasks iteratively with pagination.""" from webknossos.administration import Task PAGINATION_LIMIT = 1000 pagination_page = 0 client = _get_generated_client(enforce_auth=True) response_raw = task_infos_by_project_id.sync_detailed( self.project_id, limit=PAGINATION_LIMIT, page_number=pagination_page, include_total_count=True, client=client, ) total_count_raw = response_raw.headers.get("X-Total-Count") assert total_count_raw is not None, "X-Total-Count header missing from response" total_count = int(total_count_raw) response = response_raw.parsed assert response is not None, "Could not fetch task infos by project id." all_tasks = [Task._from_generated_response(t) for t in response] if total_count > PAGINATION_LIMIT: if fetch_all: while total_count > len(all_tasks): pagination_page += 1 response = task_infos_by_project_id.sync( self.project_id, limit=PAGINATION_LIMIT, page_number=pagination_page, include_total_count=False, client=client, ) assert (response is not None ), "Could not fetch task infos by project id." new_tasks = [ Task._from_generated_response(t) for t in response ] all_tasks.extend(new_tasks) else: warnings.warn( f"Fetched only {PAGINATION_LIMIT} of {total_count} tasks. Pass fetch_all=True to fetch all tasks iteratively (may be slow!)" ) return all_tasks
def test_dataset_info() -> None: with webknossos_context(url=DEFAULT_WEBKNOSSOS_URL): client = _get_generated_client() response = dataset_info.sync( organization_name="scalable_minds", data_set_name="l4dense_motta_et_al_demo", client=client, ) assert response is not None assert response.data_store.url == DATASTORE_URL assert response.display_name == "L4 Mouse Cortex Demo" assert sorted((layer.name, layer.category, layer.element_class) for layer in response.data_source.data_layers) == [ ("color", "color", "uint8"), ("predictions", "color", "uint24"), ("segmentation", "segmentation", "uint32"), ]
def upload(self) -> str: """Uploads the annotation to your current `webknossos_context`.""" from webknossos.client.context import _get_generated_client client = _get_generated_client(enforce_auth=True) url = f"{client.base_url}/api/annotations/upload" response = httpx.post( url=url, headers=client.get_headers(), cookies=client.get_cookies(), timeout=client.get_timeout(), data={"createGroupForEachFile": False}, files={ f"{self.name}.zip": (f"{self.name}.zip", self._binary_zip()), }, ) assert ( response.status_code == 200 ), f"Failed to upload annotation {self.name}: {response.status_code}: {response.text}" response_annotation_info = response.json()["annotation"] return f"{client.base_url}/annotations/{response_annotation_info['typ']}/{response_annotation_info['id']}"
def auth_client() -> Client: return _get_generated_client(enforce_auth=True)
def client() -> Client: return _get_generated_client()
def get_current_user(cls) -> "User": """Returns the current user from the authentication context.""" client = _get_generated_client(enforce_auth=True) response = current_user_info.sync(client=client) assert response is not None, "Could not fetch current user." return cls._from_generated_response(response)
def get_by_id(cls, id: str) -> "User": # pylint: disable=redefined-builtin """Returns the user specified by the passed id if your token authorizes you to see them.""" client = _get_generated_client(enforce_auth=True) response = user_info_by_id.sync(id, client=client) assert response is not None, "Could not fetch user by id." return cls._from_generated_response(response)
def get_all_managed_users(cls) -> List["User"]: """Returns all users of whom the current user is admin or team-manager.""" client = _get_generated_client(enforce_auth=True) response = user_list.sync(client=client) assert response is not None, "Could not fetch managed users." return [cls._from_generated_response(i) for i in response]
def get_by_name(cls, name: str) -> "Project": """Returns the user specified by the passed name if your token authorizes you to see it.""" client = _get_generated_client(enforce_auth=True) response = project_info_by_name.sync(name, client=client) assert response is not None, "Could not fetch project by name." return cls._from_generated_response(response)
def iterate_request_ids_with_responses() -> Iterable[Tuple[str, bytes]]: """Send requests to webKnossos and record the schema of their replies""" from webknossos.client._generated.api.default import ( annotation_info, annotation_infos_by_task_id, build_info, current_user_info, dataset_info, dataset_list, dataset_sharing_token, datastore_list, generate_token_for_data_store, project_info_by_id, project_info_by_name, task_info, task_infos_by_project_id, team_list, user_info_by_id, user_list, user_logged_time, ) from webknossos.client.context import _get_generated_client from webknossos.utils import snake_to_camel_case organization_id = "Organization_X" dataset_name = "e2006_knossos" task_id = "581367a82faeb37a008a5352" user_id = "570b9f4d2a7c0e4d008da6ef" project_id = "58135bfd2faeb3190181c057" project_name = "Test_Project" explorative_annotation_id = "58135c192faeb34c0081c05d" extract_200_response( httpx.post( url=f"{WK_URL}/data/triggers/checkInboxBlocking?token={WK_TOKEN}", )) response = httpx.get( url=f"{WK_URL}/api/datasets/{organization_id}/{dataset_name}", headers={"X-Auth-Token": f"{WK_TOKEN}"}, ) assert ( response.status_code == 200 and response.json()["isActive"] ), f"You need to copy or link any dataset to binaryData/{organization_id}/{dataset_name}." d = datetime.utcnow() unixtime = calendar.timegm(d.utctimetuple()) client = _get_generated_client(enforce_auth=True) yield ( "annotationInfo", extract_200_response( annotation_info.sync_detailed( id=explorative_annotation_id, client=client, timestamp=unixtime, )), ) yield ( "datasetInfo", extract_200_response( dataset_info.sync_detailed( organization_name=organization_id, data_set_name=dataset_name, client=client, )), ) yield ( "datasetList", extract_200_response(dataset_list.sync_detailed(client=client, )), ) yield ( "datasetSharingToken", extract_200_response( dataset_sharing_token.sync_detailed( organization_name=organization_id, data_set_name=dataset_name, client=client, )), ) yield ( "taskInfo", extract_200_response( task_info.sync_detailed( id=task_id, client=client, ), ), ) yield ( "userInfoById", extract_200_response( user_info_by_id.sync_detailed( id=user_id, client=client, ), ), ) yield ( "teamList", extract_200_response(team_list.sync_detailed(client=client, ), ), ) yield ( "projectInfoById", extract_200_response( project_info_by_id.sync_detailed( id=project_id, client=client, ), ), ) yield ( "projectInfoByName", extract_200_response( project_info_by_name.sync_detailed(name=project_name, client=client), ), ) yield ( "taskInfosByProjectId", extract_200_response( task_infos_by_project_id.sync_detailed( id=project_id, client=client, ), ), ) yield ( "annotationInfosByTaskId", extract_200_response( annotation_infos_by_task_id.sync_detailed(id=task_id, client=client), ), ) yield ( "userLoggedTime", extract_200_response( user_logged_time.sync_detailed( id=user_id, client=client, ), ), ) for api_endpoint in [ datastore_list, build_info, current_user_info, generate_token_for_data_store, user_list, ]: api_endpoint_name = api_endpoint.__name__.split(".")[-1] api_endpoint_name = snake_to_camel_case(api_endpoint_name) yield ( api_endpoint_name, extract_200_response(api_endpoint.sync_detailed(client=client)), )
def download( cls, annotation_id_or_url: str, annotation_type: Union[str, "AnnotationType", None] = None, webknossos_url: Optional[str] = None, ) -> "Annotation": """ * `annotation_id_or_url` may be an annotation id or a full URL to an annotation, e.g. `https://webknossos.org/annotations/6114d9410100009f0096c640` * `annotation_type` is no longer required and therefore deprecated and ignored * `webknossos_url` may be supplied if an annotation id was used and allows to specifiy in which webknossos instance to search for the annotation. It defaults to the url from your current `webknossos_context`, using https://webknossos.org as a fallback. """ from webknossos.client._generated.api.default import annotation_download from webknossos.client.context import ( _get_context, _get_generated_client, webknossos_context, ) match = re.match(_ANNOTATION_URL_REGEX, annotation_id_or_url) if match is not None: assert webknossos_url is None and annotation_type is None, ( "When Annotation.download() is be called with an annotation url, " + "e.g. Annotation.download('https://webknossos.org/annotations/6114d9410100009f0096c640'), " + "annotation_type and webknossos_url must not be set." ) annotation_id = match.group("annotation_id") webknossos_url = match.group("webknossos_url") else: annotation_id = annotation_id_or_url if annotation_type is not None: warnings.warn( "[DEPRECATION] `annotation_type` is deprecated for Annotation.download(), it should be omitted.", DeprecationWarning, ) if webknossos_url is not None and webknossos_url != _get_context().url: warnings.warn( f"The supplied url {webknossos_url} does not match your current context {_get_context().url}. " + "Using no token, only public annotations can be downloaded. " + "Please see https://docs.webknossos.org/api/webknossos/client/context.html to adapt the URL and token." ) context: ContextManager[None] = webknossos_context( webknossos_url, token=None ) else: context = nullcontext() with context: client = _get_generated_client() response = annotation_download.sync_detailed( id=annotation_id, client=client ) assert response.status_code == 200, response content_disposition_header = response.headers.get("content-disposition", "") _header_value, header_params = cgi.parse_header(content_disposition_header) filename = header_params.get("filename", "") if filename.endswith(".nml"): annotation, nml = Annotation._load_from_nml( filename[:-4], BytesIO(response.content) ) assert ( len(nml.volumes) == 0 ), "The downloaded NML contains volume tags, it should have downloaded a zip instead." return annotation else: assert filename.endswith( ".zip" ), f"Downloaded annoation should have the suffix .zip or .nml, but has filename {filename}" return Annotation._load_from_zip(BytesIO(response.content))