class Meta: model = WorkstationConfig fields = ( "title", "description", "image_context", "window_presets", "default_window_preset", "default_slab_thickness_mm", "default_slab_render_method", "default_orientation", "default_overlay_alpha", "overlay_luts", "default_overlay_lut", "default_image_interpolation", "default_overlay_interpolation", "overlay_segments", "key_bindings", "default_zoom_scale", "auto_jump_center_of_gravity", "link_images", "show_image_info_plugin", "show_display_plugin", "show_image_switcher_plugin", "show_algorithm_output_plugin", "show_overlay_plugin", "show_invert_tool", "show_flip_tool", "show_window_level_tool", "show_reset_tool", "show_overlay_selection_tool", "show_lut_selection_tool", "show_annotation_counter_tool", "enable_contrast_enhancement", ) widgets = { "overlay_segments": JSONEditorWidget( schema=OVERLAY_SEGMENTS_SCHEMA ), "key_bindings": JSONEditorWidget(schema=KEY_BINDINGS_SCHEMA), } help_texts = { "overlay_segments": ( model._meta.get_field("overlay_segments").help_text + ". If an categorical overlay is shown, it is possible to show toggles " "to change the visibility of the different overlay categories. To do " "so, configure the categories that should be displayed. Data from the" " algorithm's output.json can be added as an extra label to each " "toggle using jinja templating. " 'For example: [{ "voxel_value": 0, "name": "Level 0", "visible": ' 'false, "metric_template": "{{metrics.volumes[0]}} mm³"},]' ), "window_presets": model._meta.get_field("window_presets").help_text + ". Select multiple presets by holding CTRL or dragging your mouse", "overlay_luts": model._meta.get_field("overlay_luts").help_text + ". Select multiple presets by holding CTRL or dragging your mouse", "key_bindings": model._meta.get_field("key_bindings").help_text + ". A copy and paste JSON can be obtained from the viewer", }
class Meta(ReaderStudyCreateForm.Meta): fields = ( "title", "logo", "social_image", "description", "publications", "modalities", "structures", "organizations", "workstation", "workstation_config", "help_text_markdown", "shuffle_hanging_list", "is_educational", "public", "allow_answer_modification", "allow_case_navigation", "allow_show_all_annotations", "hanging_list", "case_text", ) widgets = { "hanging_list": JSONEditorWidget(schema=HANGING_LIST_SCHEMA), "case_text": JSONEditorWidget(schema=CASE_TEXT_SCHEMA), "help_text_markdown": MarkdownEditorWidget, "description": TextInput, "publications": Select2MultipleWidget, "modalities": Select2MultipleWidget, "structures": Select2MultipleWidget, "organizations": Select2MultipleWidget, } help_texts = { **READER_STUDY_HELP_TEXTS, "shuffle_hanging_list": ("If true, each reader will read the images in a unique " "order. The ordering for each user will be consistent over " "time. If false, the readers will all read the images in the " "order that you define in the hanging_list field."), "hanging_list": ("A list of hangings. " "The hanging defines which image (the hanging value) " "should be assigned to which image port or overlay. " "Options are: main, secondary, tertiary, quaternary, " "quinary, senary, septenary, octonary, nonary, denary. " "For instance: " '[{"main":"im1.mhd","main-overlay":"im1-overlay.mhd"}]'), "case_text": ("Free text that can be included for each case, where the key " "is the filename and the value is free text. You can use " "markdown formatting in the text. Not all images in the " "reader study are required. " 'e.g., {"a73512ee-1.2.276.0.542432.3.1.3.3546325986342": "This is *image 1*"}' ), }
class Meta: model = WorkstationConfig fields = ( "title", "description", "image_context", "window_presets", "default_window_preset", "default_slab_thickness_mm", "default_slab_render_method", "default_orientation", "default_overlay_alpha", "overlay_luts", "default_overlay_lut", "default_overlay_interpolation", "overlay_segments", "key_bindings", "default_zoom_scale", "show_image_info_plugin", "show_display_plugin", "show_invert_tool", "show_flip_tool", "show_window_level_tool", "show_reset_tool", "enable_contrast_enhancement", "client_rendered_sidebar", ) widgets = { "overlay_segments": JSONEditorWidget(schema=OVERLAY_SEGMENTS_SCHEMA), "key_bindings": JSONEditorWidget(schema=KEY_BINDINGS_SCHEMA), } help_texts = { "overlay_segments": ("If an categorical overlay is shown, it is possible to show toggles " "to change the visibility of the different overlay categories. To do " "so, configure the categories that should be displayed. Data from the" " algorithm's output.json can be added as an extra label to each " "toggle using jinja templating. " 'For example: [{ "voxel_value": 0, "name": "Level 0", "visible": ' 'false, "metric_template": "{{metrics.volumes[0]}} mm³"},]'), "image_context": "This tells the viewer how to show the images " "defined in the hanging list", "window_presets": "These are the window LUT presets the viewer can choose between. " "By default, none are selected. " "Select multiple presets by holding CTRL or dragging your mouse", }
class Meta(ReaderStudyCreateForm.Meta): fields = ( "title", "logo", "description", "workstation", "shuffle_hanging_list", "hanging_list", ) widgets = { "hanging_list": JSONEditorWidget(schema=HANGING_LIST_SCHEMA) } help_texts = { **READER_STUDY_HELP_TEXTS, "shuffle_hanging_list": ( "If true, each reader will read the images in a unique " "order. The ordering for each user will be consistent over " "time. If false, the readers will all read the images in the " "order that you define in the hanging_list field." ), "hanging_list": ( "A list of hangings. " "The hanging defines which image (the hanging value) " "should be assigned to which image port. " 'e.g., [{"main":"im1.mhd","secondary":"im2.mhd"}]' ), }
class Meta: model = HangingProtocol fields = ("title", "description", "json") widgets = {"json": JSONEditorWidget(schema=HANGING_PROTOCOL_SCHEMA)} help_texts = { "json": ("To display a single image in full size, define the " "protocol as follows: " '[{"viewport_name": "main", "x": 0,"y": 0,"w": 1,"h": 1,' '"fullsizable": true,"draggable": false,"selectable": true,' '"order": 0}]') }
class Meta: widgets = { "view_content": JSONEditorWidget(schema=VIEW_CONTENT_SCHEMA), } help_texts = { "view_content": ("Indicate which Component Interfaces need to be displayed in " 'which image port. E.g. {"main": ["interface1"]}. The first ' "item in the list of interfaces will be the main image in " "the image port. The first overlay type interface thereafter " "will be rendered as an overlay. For now, any other items " "will be ignored by the viewer.") }
class Meta: model = WorkstationConfig fields = ( "title", "description", "window_presets", "default_window_preset", "default_slab_thickness_mm", "default_slab_render_method", "default_orientation", "default_overlay_alpha", "default_overlay_lut", "default_overlay_interpolation", "overlay_segments", "key_bindings", "default_zoom_scale", "show_image_info_plugin", "show_display_plugin", "show_invert_tool", "show_flip_tool", "show_window_level_tool", "show_reset_tool", ) widgets = { "overlay_segments": JSONEditorWidget(schema=OVERLAY_SEGMENTS_SCHEMA), "key_bindings": JSONEditorWidget(schema=KEY_BINDINGS_SCHEMA), } help_texts = { "overlay_segments": ("If an categorical overlay is shown, it is possible to show toggles " "to change the visibility of the different overlay categories. To do " "so, configure the categories that should be displayed. Data from the" " algorithm's output.json can be added as an extra label to each " "toggle using jinja templating. " 'For example: [{ "voxel_value": 0, "name": "Level 0", "visible": ' 'false, "metric_template": "{{metrics.volumes[0]}} mm³"},]'), }
class Meta: model = Config fields = ( *submission_options, *scoring_options, *leaderboard_options, *result_detail_options, ) widgets = { "submission_page_html": SummernoteInplaceWidget(), "extra_results_columns": JSONEditorWidget(schema=EXTRA_RESULT_COLUMNS_SCHEMA), }
def __init__( self, *, kind: InterfaceKind.InterfaceKindChoices, schema: Dict, initial=None, user=None, required=None, help_text="", ): field_type = InterfaceKind.get_default_field(kind=kind) kwargs = {"required": required} if initial is not None: kwargs["initial"] = initial if kind in InterfaceKind.interface_type_image(): kwargs["widget"] = UserUploadMultipleWidget() kwargs["queryset"] = get_objects_for_user( user, "uploads.change_userupload", accept_global_perms=False ).filter(status=UserUpload.StatusChoices.COMPLETED) extra_help = IMAGE_UPLOAD_HELP_TEXT elif kind in InterfaceKind.interface_type_file(): kwargs["widget"] = UserUploadSingleWidget( allowed_file_types=InterfaceKind.get_file_mimetypes(kind=kind) ) kwargs["queryset"] = get_objects_for_user( user, "uploads.change_userupload", accept_global_perms=False ).filter(status=UserUpload.StatusChoices.COMPLETED) extra_help = f"{file_upload_text} .{kind.lower()}" elif kind in InterfaceKind.interface_type_json(): default_schema = { **INTERFACE_VALUE_SCHEMA, "anyOf": [{"$ref": f"#/definitions/{kind}"}], } if field_type == forms.JSONField: kwargs["widget"] = JSONEditorWidget(schema=default_schema) kwargs["validators"] = [ JSONValidator(schema=default_schema), JSONValidator(schema=schema), ] extra_help = "" else: raise RuntimeError(f"Unknown kind {kind}") self._field = field_type( help_text=_join_with_br(help_text, extra_help), **kwargs )
class Meta: model = Phase fields = ( *phase_options, *submission_options, *scoring_options, *leaderboard_options, *result_detail_options, ) widgets = { "submission_page_html": SummernoteInplaceWidget(), "extra_results_columns": JSONEditorWidget(schema=EXTRA_RESULT_COLUMNS_SCHEMA), "submissions_open_at": forms.DateTimeInput(format=("%Y-%m-%dT%H:%M"), attrs={"type": "datetime-local"}), "submissions_close_at": forms.DateTimeInput(format=("%Y-%m-%dT%H:%M"), attrs={"type": "datetime-local"}), }
def __init__( self, *, kind: InterfaceKind.InterfaceKindChoices, initial=None, user=None, help_text="", ): field_type = field_for_interface(kind) # bool can't be required kwargs = { "required": (kind != InterfaceKind.InterfaceKindChoices.BOOL), } extra_help = "" if initial is not None: kwargs["initial"] = initial if kind in InterfaceKind.interface_type_annotation(): kwargs["widget"] = JSONEditorWidget( schema=ANSWER_TYPE_SCHEMA["definitions"][kind]) if kind in InterfaceKind.interface_type_file(): kwargs["widget"] = uploader.AjaxUploadWidget(multifile=False, auto_commit=False) kwargs["validators"] = [ ExtensionValidator(allowed_extensions=(f".{kind.lower()}", )) ] extra_help = f"{file_upload_text} .{kind.lower()}" if kind in InterfaceKind.interface_type_image(): kwargs["widget"] = uploader.AjaxUploadWidget(multifile=True, auto_commit=False) extra_help = IMAGE_UPLOAD_HELP_TEXT self._field = field_type(help_text=_join_with_br( help_text, extra_help), **kwargs) if user: self._field.widget.user = user
class Meta(ReaderStudyCreateForm.Meta): fields = ( "title", "logo", "social_image", "description", "publications", "modalities", "structures", "organizations", "workstation", "workstation_config", "hanging_protocol", "view_content", "help_text_markdown", "shuffle_hanging_list", "is_educational", "public", "access_request_handling", "allow_answer_modification", "allow_case_navigation", "allow_show_all_annotations", "roll_over_answers_for_n_cases", "hanging_list", "case_text", ) widgets = { "hanging_list": JSONEditorWidget(schema=HANGING_LIST_SCHEMA), "case_text": JSONEditorWidget(schema=CASE_TEXT_SCHEMA), "help_text_markdown": MarkdownEditorWidget, "description": TextInput, "publications": Select2MultipleWidget, "modalities": Select2MultipleWidget, "structures": Select2MultipleWidget, "organizations": Select2MultipleWidget, } widgets.update(ViewContentMixin.Meta.widgets) help_texts = { **READER_STUDY_HELP_TEXTS, "shuffle_hanging_list": ("If true, each reader will read the cases in a unique " "order. The ordering for each user will be consistent over " "time. If false, the readers will all read the cases in the " "order that you define."), "hanging_list": ("A list of hangings. " "The hanging defines which image (the hanging value) " "should be assigned to which image port or overlay. " "Options are: main, secondary, tertiary, quaternary, " "quinary, senary, septenary, octonary, nonary, denary. " "For instance: " '[{"main":"im1.mhd","main-overlay":"im1-overlay.mhd"}]'), "case_text": ("Free text that can be included for each case, where the key " "is the filename and the value is free text. You can use " "markdown formatting in the text. Not all images in the " "reader study are required. " 'e.g., {"a73512ee-1.2.276.0.542432.3.1.3.3546325986342": "This is *image 1*"}' ), "hanging_protocol": format_lazy( ("The hanging protocol to use for this reader study. " "If a suitable protocol does not exist you can " '<a href="{}">create a new one</a>. For a list of existing ' 'hanging protocols, go <a href="{}">here</a>.'), reverse_lazy("hanging-protocols:create"), reverse_lazy("hanging-protocols:list"), ), } help_texts.update(ViewContentMixin.Meta.help_texts)