55import tempfile
66from pathlib import Path
77from typing import Callable
8+ from typing import Dict
89from typing import Iterable
910from typing import List
1011from typing import Optional
@@ -2571,7 +2572,6 @@ def set_annotation_statuses(
25712572 raise AppException (response .errors )
25722573 else :
25732574 logger .info ("Annotation statuses of items changed" )
2574- return response .data
25752575
25762576 def download_annotations (
25772577 self ,
@@ -2666,7 +2666,8 @@ def create_custom_fields(self, project: NotEmptyStr, fields: dict):
26662666 --------------------------------------
26672667 field spec spec value
26682668 ============== ======================
2669- format “email” or “date”
2669+ format “email” (user@example.com) or “date” (YYYY-MM-DD)
2670+
26702671 enum list of strings
26712672 ============== ======================
26722673 ::
@@ -2724,40 +2725,42 @@ def get_custom_fields(self, project: NotEmptyStr):
27242725
27252726 Response Example:
27262727 ::
2727- {
2728- "study_date": {
2729- "type": "string",
2730- "format": "date"
2731- },
2732- "patient_id": {
2733- "type": "string"
2734- },
2735- "patient_sex": {
2736- "type": "string",
2737- "enum": [
2738- "male", "female"
2739- ]
2740- },
2741- "patient_age": {
2742- "type": "number"
2743- },
2744- "medical_specialist": {
2745- "type": "string",
2746- "format": "email"
2747- },
2748- "duration": {
2749- "type": "number",
2750- "minimum": 10
2751- }
2752- }
2728+ {
2729+ "study_date": {
2730+ "type": "string",
2731+ "format": "date"
2732+ },
2733+ "patient_id": {
2734+ "type": "string"
2735+ },
2736+ "patient_sex": {
2737+ "type": "string",
2738+ "enum": [
2739+ "male", "female"
2740+ ]
2741+ },
2742+ "patient_age": {
2743+ "type": "number"
2744+ },
2745+ "medical_specialist": {
2746+ "type": "string",
2747+ "format": "email"
2748+ },
2749+ "duration": {
2750+ "type": "number",
2751+ "minimum": 10
2752+ }
2753+ }
27532754 """
27542755 project_name , _ = extract_project_folder (project )
27552756 response = self .controller .get_custom_schema (project_name = project )
27562757 if response .errors :
27572758 raise AppException (response .errors )
27582759 return response .data
27592760
2760- def delete_custom_fields (self , project : NotEmptyStr , fields : list ):
2761+ def delete_custom_fields (
2762+ self , project : NotEmptyStr , fields : conlist (NotEmptyStr , min_items = 1 )
2763+ ):
27612764 """Remove custom fields from a project’s custom metadata schema.
27622765
27632766 :param project: project name (e.g., “project1”)
@@ -2771,39 +2774,143 @@ def delete_custom_fields(self, project: NotEmptyStr, fields: list):
27712774
27722775 Request Example:
27732776 ::
2774- client = SAClient()
2775- client.delete_custom_fields(
2776- project = "Medical Annotations",
2777- fields = ["duration", patient_age]
2778- )
2777+ client = SAClient()
2778+ client.delete_custom_fields(
2779+ project = "Medical Annotations",
2780+ fields = ["duration", patient_age]
2781+ )
27792782
27802783 Response Example:
27812784 ::
2782- {
2783- "study_date": {
2784- "type": "string",
2785- "format": "date"
2786- },
2787- "patient_id": {
2788- "type": "string"
2789- },
2790- "patient_sex": {
2791- "type": "string",
2792- "enum": [
2793- "male", "female"
2794- ]
2795- },
2796- "medical_specialist": {
2797- "type": "string",
2798- "format": "email"
2785+ {
2786+ "study_date": {
2787+ "type": "string",
2788+ "format": "date"
2789+ },
2790+ "patient_id": {
2791+ "type": "string"
2792+ },
2793+ "patient_sex": {
2794+ "type": "string",
2795+ "enum": [
2796+ "male", "female"
2797+ ]
2798+ },
2799+ "medical_specialist": {
2800+ "type": "string",
2801+ "format": "email"
2802+ }
27992803 }
2800- }
28012804
28022805 """
28032806 project_name , _ = extract_project_folder (project )
28042807 response = self .controller .delete_custom_schema (
2805- project_name = project , fields = fields
2808+ project_name = project_name , fields = fields
28062809 )
28072810 if response .errors :
28082811 raise AppException (response .errors )
28092812 return response .data
2813+
2814+ def upload_custom_values (
2815+ self , project : NotEmptyStr , items : conlist (Dict [str , dict ], min_items = 1 )
2816+ ):
2817+ """
2818+ Attach custom metadata to items.
2819+ SAClient.get_item_metadata(), SAClient.search_items(), SAClient.query() methods
2820+ will return the item metadata and custom metadata.
2821+
2822+ :param project: project name or folder path (e.g., “project1/folder1”)
2823+ :type project: str
2824+
2825+ :param items: list of name-data pairs.
2826+ The key of each dict indicates an existing item name and the value represents the custom metadata dict.
2827+ The values for the corresponding keys will be added to an item or will be overridden.
2828+ :type items: list of dicts
2829+
2830+ :return: the count of succeeded items and the list of failed item names.
2831+ :rtype: dict
2832+
2833+ Request Example:
2834+ ::
2835+ client = SAClient()
2836+
2837+ items_values = [
2838+ {
2839+ "image_1.png": {
2840+ "study_date": "2021-12-31",
2841+ "patient_id": "62078f8a756ddb2ca9fc9660",
2842+ "patient_sex": "female",
2843+ "medical_specialist": "robertboxer@ms.com"
2844+ }
2845+ },
2846+ {
2847+ "image_2.png": {
2848+ "study_date": "2021-12-31",
2849+ "patient_id": "62078f8a756ddb2ca9fc9661",
2850+ "patient_sex": "female",
2851+ "medical_specialist": "robertboxer@ms.com"
2852+ }
2853+ },
2854+ {
2855+ "image_3.png": {
2856+ "study_date": "2011-10-05T14:48:00.000Z",
2857+ "patient_": "62078f8a756ddb2ca9fc9660",
2858+ "patient_sex": "female",
2859+ "medical_specialist": "robertboxer"
2860+ }
2861+ }
2862+ ]
2863+
2864+ client.upload_custom_values(
2865+ project = "Medical Annotations",
2866+ items = items_values
2867+ )
2868+ Response Example:
2869+ ::
2870+ {
2871+ "successful_items_count": 2,
2872+ "failed_items_names": ["image_3.png"]
2873+ }
2874+ """
2875+
2876+ project_name , folder_name = extract_project_folder (project )
2877+ response = self .controller .upload_custom_values (
2878+ project_name = project_name , folder_name = folder_name , items = items
2879+ )
2880+ if response .errors :
2881+ raise AppException (response .errors )
2882+ return response .data
2883+
2884+ def delete_custom_values (
2885+ self , project : NotEmptyStr , items : conlist (Dict [str , List [str ]], min_items = 1 )
2886+ ):
2887+ """
2888+ Remove custom data from items
2889+
2890+ :param project: project name or folder path (e.g., “project1/folder1”)
2891+ :type project: str
2892+
2893+ :param items: list of name-custom data dicts.
2894+ The key of each dict element indicates an existing item in the project root or folder.
2895+ The value should be the list of fields to be removed from the given item.
2896+ Please note, that the function removes pointed metadata from a given item.
2897+ To delete metadata for all items you should delete it from the custom metadata schema.
2898+ To override values for existing fields, use SAClient.upload_custom_values()
2899+ :type items: list of dicts
2900+
2901+ Request Example:
2902+ ::
2903+ client.delete_custom_values(
2904+ project = "Medical Annotations",
2905+ items = [
2906+ {"image_1.png": ["study_date", "patient_sex"]},
2907+ {"image_2.png": ["study_date", "patient_sex"]}
2908+ ]
2909+ )
2910+ """
2911+ project_name , folder_name = extract_project_folder (project )
2912+ response = self .controller .delete_custom_values (
2913+ project_name = project_name , folder_name = folder_name , items = items
2914+ )
2915+ if response .errors :
2916+ raise AppException (response .errors )
0 commit comments