From e27e9901ba5d76d940775c70ce8beadf4d1c8d06 Mon Sep 17 00:00:00 2001 From: Christian Schiffer <c.schiffer@fz-juelich.de> Date: Thu, 22 Aug 2024 13:48:07 +0200 Subject: [PATCH] Fixed saving logic --- atlas_server/src/app.py | 302 +++-- atlas_server/src/db.py | 178 +-- atlas_server/src/models.py | 84 +- microdraw/src/atlas_ui.js | 21 +- microdraw/src/microdraw.html | 2489 +++++++++++++++++++++++----------- 5 files changed, 2057 insertions(+), 1017 deletions(-) diff --git a/atlas_server/src/app.py b/atlas_server/src/app.py index 1b7da3f..352499d 100644 --- a/atlas_server/src/app.py +++ b/atlas_server/src/app.py @@ -4,69 +4,132 @@ from flask import Flask, request from flask_restx import Api, Resource, fields, abort from . import models -from .db import Database, ProjectNotFoundError, TaskNotFoundError, JobNotFoundError, ForbiddenOperationError -from .scheduler import submit_training_job, submit_prediction_job, get_job_status, cancel_job, JobStatusNotFoundError, JobNotFoundError, JobSubmissionError +from .db import ( + Database, + ProjectNotFoundError, + TaskNotFoundError, + JobNotFoundError, + ForbiddenOperationError, +) +from .scheduler import ( + submit_training_job, + submit_prediction_job, + get_job_status, + cancel_job, + JobStatusNotFoundError, + JobNotFoundError, + JobSubmissionError, +) app = Flask(__name__) # Setup flask with CORS, since we do requests to atlas controller -api = Api(app, - prefix="/api", - version="1.0", - title="ATLaS UI API", - description="API serving as backend for the ATLaS UI") +api = Api( + app, + prefix="/api", + version="1.0", + title="ATLaS UI API", + description="API serving as backend for the ATLaS UI", +) # ----------------------------------- # Namespace definitions # ----------------------------------- -project_namespace = api.namespace("projects", description="Operations to deal with projects") -annotation_namespace = api.namespace("annotations", description="Operations to deal with annotations") -predictions_namespace = api.namespace("predictions", description="Operations to deal with predictions") +project_namespace = api.namespace( + "projects", description="Operations to deal with projects" +) +annotation_namespace = api.namespace( + "annotations", description="Operations to deal with annotations" +) +predictions_namespace = api.namespace( + "predictions", description="Operations to deal with predictions" +) # ----------------------------------- # Model definitions # ----------------------------------- -annotation_model = api.model("AnnotationModel", - { - "section": fields.Integer(description="Section number the annotation corresponds to."), - "name": fields.String(description="Name of the annotation."), - "original_name": fields.String(description="Original name of the annotation before automatic renaming. May be None."), - "path": fields.String(description="Path of the annotation.") - }) - -task_model = api.model("TaskModel", - { - "task_id": fields.Integer(description="ID of the task."), - "name": fields.String(description="The name of the task."), - "annotations": fields.List(fields.Nested(annotation_model), description="Annotations to add to the task."), - "no_aux_labels": fields.Boolean(description="Whether to use auxiliary labels.", required=False), - "model": fields.String(description="Model to use.", required=False), - }) - -project_model = api.model("ProjectModel", - { - "project_id": fields.Integer(description="ID of the project."), - "name": fields.String(description="The name of the project."), - "brain": fields.Integer(description="Number of the brain the projects refers to."), - "owner": fields.String(description="Owner of the project."), - "tasks": fields.List(fields.Nested(task_model), description="List of tasks of the project.") - }) - -job_configuration_model = api.model("JobConfigurationModel", - { - "account": fields.String(description="Account to submit job with, e.g. jinm14.", required=True), - "time": fields.String(description="Time limit for job in format HH:MM:SS.", required=True), - "job_type": fields.String(description="Type of the job which is being submitted.", required=True), - "gres": fields.String(description="Generic resources to request for job, e.g. \"gpus:4\""), - "mail_type": fields.String(description="Mail type to use for e-mail notifications."), - "mail_user": fields.String(description="E-mail address to send notifications to."), - "num_nodes": fields.Integer(description="Number of nodes to allocate for the job."), - "partition": fields.String(description="Partition to allocate job on, e.g. \"batch\" or \"gpus\""), - "reservation": fields.String(description="Reservation to use for running the job."), - "model": fields.String(description="Model configuration to train."), - "kwargs": fields.String(description="Keyword arguments for configuration encoded as JSON"), - }) +annotation_model = api.model( + "AnnotationModel", + { + "section": fields.Integer( + description="Section number the annotation corresponds to." + ), + "name": fields.String(description="Name of the annotation."), + "original_name": fields.String( + description="Original name of the annotation before automatic renaming. May be None." + ), + "path": fields.String(description="Path of the annotation."), + }, +) + +task_model = api.model( + "TaskModel", + { + "task_id": fields.Integer(description="ID of the task."), + "name": fields.String(description="The name of the task."), + "annotations": fields.List( + fields.Nested(annotation_model), + description="Annotations to add to the task.", + ), + "no_aux_labels": fields.Boolean( + description="Whether to use auxiliary labels.", required=False + ), + "model": fields.String(description="Model to use.", required=False), + }, +) + +project_model = api.model( + "ProjectModel", + { + "project_id": fields.Integer(description="ID of the project."), + "name": fields.String(description="The name of the project."), + "brain": fields.Integer( + description="Number of the brain the projects refers to." + ), + "owner": fields.String(description="Owner of the project."), + "tasks": fields.List( + fields.Nested(task_model), description="List of tasks of the project." + ), + }, +) + +job_configuration_model = api.model( + "JobConfigurationModel", + { + "account": fields.String( + description="Account to submit job with, e.g. jinm14.", required=True + ), + "time": fields.String( + description="Time limit for job in format HH:MM:SS.", required=True + ), + "job_type": fields.String( + description="Type of the job which is being submitted.", required=True + ), + "gres": fields.String( + description='Generic resources to request for job, e.g. "gpus:4"' + ), + "mail_type": fields.String( + description="Mail type to use for e-mail notifications." + ), + "mail_user": fields.String( + description="E-mail address to send notifications to." + ), + "num_nodes": fields.Integer( + description="Number of nodes to allocate for the job." + ), + "partition": fields.String( + description='Partition to allocate job on, e.g. "batch" or "gpus"' + ), + "reservation": fields.String( + description="Reservation to use for running the job." + ), + "model": fields.String(description="Model configuration to train."), + "kwargs": fields.String( + description="Keyword arguments for configuration encoded as JSON" + ), + }, +) _project_schema = models.ProjectSchema() _task_schema = models.TaskSchema() @@ -78,6 +141,7 @@ _job_configuration_schema = models.JobConfigurationSchema() # Actual API definitions # ----------------------------------- + @project_namespace.route("/") class ProjectList(Resource): @project_namespace.doc("Get all projects") @@ -194,10 +258,11 @@ class Task(Resource): def get(self, project_id, task_id): try: with Database() as db: - task = db.get_task_by_id(project_id=project_id, - task_id=task_id) + task = db.get_task_by_id(project_id=project_id, task_id=task_id) except TaskNotFoundError: - abort(404, f"No task found for project id {project_id} and task id {task_id}") + abort( + 404, f"No task found for project id {project_id} and task id {task_id}" + ) return return _task_schema.dump(task) @@ -226,11 +291,15 @@ class Task(Resource): try: with Database() as db: - db.update_task_values(project_id=project_id, task_id=task_id, key_value_dict=data) + db.update_task_values( + project_id=project_id, task_id=task_id, key_value_dict=data + ) # Get the modified data to return it task = db.get_task_by_id(project_id=project_id, task_id=task_id) except TaskNotFoundError: - abort(404, f"No task found for project id {project_id} and task id {task_id}") + abort( + 404, f"No task found for project id {project_id} and task id {task_id}" + ) return except ForbiddenOperationError as ex: abort(403, ex.message) @@ -269,38 +338,51 @@ class TaskJobList(Resource): annotations = task.annotations # noinspection PyUnresolvedReferences job_type = job_configuration.job_type - if job_type.lower() in ("training", "train", "training_prediction", "train_predict", "train+predict"): + if job_type.lower() in ( + "training", + "train", + "training_prediction", + "train_predict", + "train+predict", + ): with_prediction = "predict" in job_type.lower() - job_id = submit_training_job(brain=brain, - annotations=annotations, - job_configuration=job_configuration, - project_id=project_id, - task_id=task_id, - with_prediction=with_prediction, - **kwargs) + job_id = submit_training_job( + brain=brain, + annotations=annotations, + job_configuration=job_configuration, + project_id=project_id, + task_id=task_id, + with_prediction=with_prediction, + **kwargs, + ) elif job_type in ("prediction", "predict"): - job_id = submit_prediction_job(brain=brain, - job_configuration=job_configuration, - project_id=project_id, - task_id=task_id) + job_id = submit_prediction_job( + brain=brain, + job_configuration=job_configuration, + project_id=project_id, + task_id=task_id, + ) else: abort(400, f"Invalid job type: {job_type}") return # Save job id into the task - job = models.Job(job_id=job_id, - job_configuration=job_configuration) + job = models.Job(job_id=job_id, job_configuration=job_configuration) job = _job_schema.dump(job) - db.update_task_values(project_id=project_id, - task_id=task_id, - key_value_dict={"jobs": job}, - operation="add") + db.update_task_values( + project_id=project_id, + task_id=task_id, + key_value_dict={"jobs": job}, + operation="add", + ) except ProjectNotFoundError: abort(404, f"No project found for id {project_id}") return except TaskNotFoundError: - abort(404, f"No task found for project id {project_id} and task id {task_id}") + abort( + 404, f"No task found for project id {project_id} and task id {task_id}" + ) return except JobSubmissionError as ex: abort(500, f"Error submitting job: {ex.message}") @@ -312,15 +394,20 @@ class TaskJobList(Resource): @project_namespace.route("/<int:project_id>/tasks/<int:task_id>/jobs/<int:job_id>") class TaskJob(Resource): - @project_namespace.doc("Get information about a specific job associated with a task") + @project_namespace.doc( + "Get information about a specific job associated with a task" + ) def get(self, project_id, task_id, job_id): try: with Database() as db: - job = db.get_job_by_id(project_id=project_id, - task_id=task_id, - job_id=job_id) + job = db.get_job_by_id( + project_id=project_id, task_id=task_id, job_id=job_id + ) except JobNotFoundError: - abort(404, f"No job found for project {project_id} - task {task_id} - project {project_id}") + abort( + 404, + f"No job found for project {project_id} - task {task_id} - project {project_id}", + ) return # Dump job to dict and add the job status from the batch system @@ -329,8 +416,10 @@ class TaskJob(Resource): result["status"] = get_job_status(job_id=job_id) except JobStatusNotFoundError: # If the job status could not be found, it could mean the job has finished to long ago - result["status"] = f"Could not determine job status for job with id {job_id}. " \ - f"This could mean that the job does not exist or finished too long ago." + result["status"] = ( + f"Could not determine job status for job with id {job_id}. " + f"This could mean that the job does not exist or finished too long ago." + ) return result @@ -344,16 +433,20 @@ class TaskJob(Resource): jobs = [job for job in task.jobs if job.job_id != job_id] jobs = _job_schema.dump(jobs, many=True) # Update task with changed jobs list - db.update_task_values(project_id=project_id, - task_id=task_id, - key_value_dict={"jobs": jobs}, - operation="set") + db.update_task_values( + project_id=project_id, + task_id=task_id, + key_value_dict={"jobs": jobs}, + operation="set", + ) except JobNotFoundError: abort(404, f"Could not cancel job {job_id}: Job not found.") return {"message": "success"} -@project_namespace.route("/<int:project_id>/tasks/<int:task_id>/tile_server_config/<base_file_b64>") +@project_namespace.route( + "/<int:project_id>/tasks/<int:task_id>/tile_server_config/<base_file_b64>" +) class TaskTileServerConfig(Resource): @project_namespace.doc("Create a tile server configuration for the task") @@ -362,7 +455,11 @@ class TaskTileServerConfig(Resource): import requests import base64 from atlaslib.files import get_section_number_from_string - from .config import TILE_SERVER_OUTPUT_DIR, ATLAS_CONTROLLER_URL, TILE_SERVER_HOST + from .config import ( + TILE_SERVER_OUTPUT_DIR, + ATLAS_CONTROLLER_URL, + TILE_SERVER_HOST, + ) # Download the base file base_file = base64.b64decode(base_file_b64).decode("utf-8") @@ -418,7 +515,11 @@ class TaskTileServerConfig(Resource): h, w = prediction["shape"] section = f"{get_section_number_from_string(path):04d}" section_overlays = overlays.get(section, {}) - url = "function(level, x, y){return '" + f"{TILE_SERVER_HOST}/get_tile?level=' + level + '&y=' + y + '&x=' + x + '&ts=256&img={path}&dataset=/{cmap_args}'" + ";}" + url = ( + "function(level, x, y){return '" + + f"{TILE_SERVER_HOST}/get_tile?level=' + level + '&y=' + y + '&x=' + x + '&ts=256&img={path}&dataset=/{cmap_args}'" + + ";}" + ) section_overlays[overlay_name] = { "getTileUrl": url, "height": h, @@ -462,8 +563,7 @@ class AnnotationImportExport(Resource): return try: - annotations = db.export_annotations(json_path=path, - user_name=user) + annotations = db.export_annotations(json_path=path, user_name=user) except Exception as ex: abort(500, f"Error while retrieving annotations: {ex}") return @@ -514,11 +614,15 @@ class AnnotationImportExport(Resource): overwrite = request.args.get("overwrite", False) for section, structures in annotations.items(): structures = parse_microdraw_json(structures) - annotations_for_section = Annotations(structures=structures, section=int(section)) - db.import_annotations(json_path=path, - user_name=user, - annotations=annotations_for_section, - overwrite=overwrite) + annotations_for_section = Annotations( + structures=structures, section=int(section) + ) + db.import_annotations( + json_path=path, + user_name=user, + annotations=annotations_for_section, + overwrite=overwrite, + ) except Exception as ex: abort(500, f"Error importing annotations into database: {ex}") return @@ -533,7 +637,9 @@ class AnnotationImportExport(Resource): @predictions_namespace.route("/<int:project_id>/tasks/<int:task_id>") class PredictionAnnotationExport(Resource): - @annotation_namespace.doc("Retrieve annotations based on predictions of a specific task") + @annotation_namespace.doc( + "Retrieve annotations based on predictions of a specific task" + ) def get(self, project_id, task_id): import os import requests @@ -543,7 +649,9 @@ class PredictionAnnotationExport(Resource): # Determine work_dir = f"atlas_ui_project{project_id}_task{task_id}" # Query predictions - result = requests.get(url=f"{ATLAS_CONTROLLER_URL}/predictions/{work_dir}?annotations=true") + result = requests.get( + url=f"{ATLAS_CONTROLLER_URL}/predictions/{work_dir}?annotations=true" + ) if result.status_code >= 400: abort(404, f"Failed to find predictions") return diff --git a/atlas_server/src/db.py b/atlas_server/src/db.py index 54c65a1..7fc1757 100644 --- a/atlas_server/src/db.py +++ b/atlas_server/src/db.py @@ -4,7 +4,13 @@ Access to Mondo database """ from pymongo import MongoClient, DESCENDING from . import models -from .config import DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD +from .config import ( + DATABASE_HOST, + DATABASE_PORT, + DATABASE_NAME, + DATABASE_USERNAME, + DATABASE_PASSWORD, +) from marshmallow.schema import EXCLUDE _project_schema = models.ProjectSchema(unknown=EXCLUDE) @@ -14,6 +20,7 @@ _job_schema = models.JobSchema(unknown=EXCLUDE) def _get_timestamp(): from time import time + return int(time()) @@ -63,10 +70,12 @@ class Database(object): def open(self): if self.client: raise RuntimeError("Cannot open connection: Connection already open.") - self.client = MongoClient(DATABASE_HOST, - username=DATABASE_USERNAME, - password=DATABASE_PASSWORD, - port=DATABASE_PORT) + self.client = MongoClient( + DATABASE_HOST, + username=DATABASE_USERNAME, + password=DATABASE_PASSWORD, + port=DATABASE_PORT, + ) return self def close(self): @@ -89,10 +98,6 @@ class Database(object): def projects(self): return self.database.projects - @property - def project_ids(self): - return self.database.project_ids - def _update_project_fields(self, project_id, key_value_dict, operation="set"): """ Update a single field @@ -113,10 +118,9 @@ class Database(object): raise ForbiddenOperationError(message) mongo_operation = operations[operation] - update_res = self.projects.update_one({"project_id": project_id}, - { - mongo_operation: key_value_dict - }) + update_res = self.projects.update_one( + {"project_id": project_id}, {mongo_operation: key_value_dict} + ) # Raise error if no projects were affected # This means the project id does not exist if operation == "delete": @@ -136,18 +140,21 @@ class Database(object): """ # Update modified data of project modified_date = _get_timestamp() - self.projects.update_one({"project_id": project_id}, - { - "$set": - { - "modified": str(modified_date), - }, - }) + self.projects.update_one( + {"project_id": project_id}, + { + "$set": { + "modified": str(modified_date), + }, + }, + ) return modified_date def insert_project(self, project): - # Find current maximum project id - max_existing = self.project_ids.find_one(sort=[("project_id", DESCENDING)]) + # Find current maximum project id. Important: Here, we search for __all__ projects, including deleted ones. + max_existing = self.projects.find_one( + {}, sort={"project_id": -1}, projection={"_id": False, "project_id": True} + ) if max_existing: new_id = max_existing["project_id"] + 1 else: @@ -165,15 +172,13 @@ class Database(object): document = _project_to_mongodb_item(project=project) # Insert into database self.projects.insert_one(document) - # Insert project id - self.project_ids.insert_one({"project_id": new_id, "next_task_id": 1, }) return document def update_project_values(self, project_id, key_value_dict): - self._update_project_fields(project_id=project_id, - key_value_dict=key_value_dict, - operation="set") + self._update_project_fields( + project_id=project_id, key_value_dict=key_value_dict, operation="set" + ) def get_project_by_id(self, project_id): """ @@ -181,24 +186,35 @@ class Database(object): Args: project_id (int): Id of the project. + include_deleted_tasks (bool): Include deleted tasks. Returns: atlas_server.models.Project """ - project = self.projects.find_one({"project_id": project_id}) + project = self.projects.find_one( + { + "project_id": project_id, + } + ) if not project: raise ProjectNotFoundError(project_id=project_id) return _project_schema.load(project, unknown=EXCLUDE) def get_projects_for_user(self, username): - projects = list(self.projects.find({"owner": username})) + projects = list( + self.projects.find( + { + "owner": username, + } + ) + ) # noinspection PyTypeChecker return _project_schema.load(projects, many=True, unknown=EXCLUDE) def delete_project(self, project_id): - deleted_res = self.projects.delete_one({"project_id": project_id}) - if deleted_res.deleted_count == 0: - raise ProjectNotFoundError(project_id=project_id) + self._update_project_fields( + project_id=project_id, key_value_dict={"deleted": True}, operation="set" + ) def _update_task_fields(self, project_id, task_id, key_value_dict, operation="set"): """ @@ -221,7 +237,13 @@ class Database(object): mongo_operation = operations[operation] # Modify the update dict to match the update syntax. We prefix "tasks.$." before each key update_dict = {f"tasks.$.{key}": value for key, value in key_value_dict.items()} - update_res = self.projects.update_one({"project_id": project_id, "tasks.task_id": task_id, }, {mongo_operation: update_dict}) + update_res = self.projects.update_one( + { + "project_id": project_id, + "tasks.task_id": task_id, + }, + {mongo_operation: update_dict}, + ) # Raise error if no projects were affected # This means the project id does not exist affected_count = update_res.modified_count @@ -232,12 +254,14 @@ class Database(object): self._update_task_modified_date(project_id=project_id, task_id=task_id) def _delete_task(self, project_id, task_id): - selection_dict = {"project_id": project_id, "tasks.task_id": task_id} - delete_dict = {"$pull": {"tasks": {"task_id": task_id}}} - update_res = self.projects.update_one(selection_dict, delete_dict) - affected_count = update_res.modified_count - if affected_count == 0: - raise TaskNotFoundError(project_id=project_id, task_id=task_id) + self._update_task_fields( + project_id=project_id, + task_id=task_id, + # "Delete" task by setting them to deleted + key_value_dict={ + "deleted": True, + }, + ) def _update_task_modified_date(self, project_id, task_id): """ @@ -245,13 +269,17 @@ class Database(object): """ # Update modified data of project modified_date = _get_timestamp() - self.projects.update_one({"project_id": project_id, "tasks.task_id": task_id, }, - { - "$set": - { - "task.$.modified": str(modified_date), - }, - }) + self.projects.update_one( + { + "project_id": project_id, + "tasks.task_id": task_id, + }, + { + "$set": { + "task.$.modified": str(modified_date), + }, + }, + ) return modified_date # noinspection PyUnresolvedReferences @@ -260,17 +288,11 @@ class Database(object): project = self.get_project_by_id(project_id=project_id) if project is None: raise ProjectNotFoundError(project_id) - project_ids = self.project_ids.find_one({"project_id": project_id}) - if not project_ids: - # Fallback for projects which have no entry in project_ids - if not project.tasks: - new_task_id = 1 - else: - new_task_id = max(task.task_id for task in project.tasks) + 1 - project_ids.insert_one({"project_id": project_id, "next_task_id": new_task_id}) + task_ids = [task.task_id for task in project.tasks] + if task_ids: + new_task_id = max(task_ids) + 1 else: - new_task_id = project_ids["next_task_id"] - + new_task_id = 1 task.task_id = new_task_id # Add created and modified info to task task.created = _get_timestamp() @@ -278,55 +300,51 @@ class Database(object): # Serialize task document = _task_schema.dump(task) # Insert task into the projects task list - self._update_project_fields(project_id=project_id, - key_value_dict={ - "tasks": document - }, - operation="add") - # Update next task id - self.project_ids.update_one({"project_id": project_id}, {"$set": {"next_task_id": new_task_id + 1}}) + self._update_project_fields( + project_id=project_id, key_value_dict={"tasks": document}, operation="add" + ) + return document def get_task_by_id(self, project_id, task_id): res = self.projects.find_one( { "project_id": project_id, - "tasks.task_id": task_id + "tasks.task_id": task_id, }, # Projection, only get the specific task { "tasks.$": 1, - } + }, ) if not res: raise TaskNotFoundError(project_id=project_id, task_id=task_id) return _task_schema.load(res["tasks"][0]) def update_task_values(self, project_id, task_id, key_value_dict, operation="set"): - self._update_task_fields(project_id=project_id, - task_id=task_id, - key_value_dict=key_value_dict, - operation=operation) + self._update_task_fields( + project_id=project_id, + task_id=task_id, + key_value_dict=key_value_dict, + operation=operation, + ) def delete_task(self, project_id, task_id): - # delete_res = self.projects. - self._delete_task(project_id=project_id, - task_id=task_id) + self._delete_task(project_id=project_id, task_id=task_id) def get_job_by_id(self, project_id, task_id, job_id): # We cannot do double nested updates with mongoDB, so we get the task # and search for the task in the result set try: - task = self.get_task_by_id(project_id=project_id, - task_id=task_id) + task = self.get_task_by_id(project_id=project_id, task_id=task_id) except TaskNotFoundError: - raise JobNotFoundError(project_id=project_id, - task_id=task_id, - job_id=job_id) + raise JobNotFoundError( + project_id=project_id, task_id=task_id, job_id=job_id + ) # Search for the job # noinspection PyUnresolvedReferences job = [job for job in task.jobs if job.job_id == job_id] if not job: - raise JobNotFoundError(project_id=project_id, - task_id=task_id, - job_id=job_id) + raise JobNotFoundError( + project_id=project_id, task_id=task_id, job_id=job_id + ) diff --git a/atlas_server/src/models.py b/atlas_server/src/models.py index 265fa20..aaff649 100644 --- a/atlas_server/src/models.py +++ b/atlas_server/src/models.py @@ -18,7 +18,6 @@ class JobConfigurationSchema(Schema): model = fields.String(allow_none=True) kwargs = fields.String(allow_none=True) - # noinspection PyUnusedLocal @post_load def create_job_configuration(self, data, **kwargs): @@ -57,6 +56,7 @@ class TaskSchema(Schema): jobs = fields.List(fields.Nested(JobSchema)) no_aux_labels = fields.Boolean(allow_none=True) model = fields.String(allow_none=True) + deleted = fields.Boolean(allow_none=True) # noinspection PyUnusedLocal @post_load @@ -72,6 +72,7 @@ class ProjectSchema(Schema): tasks = fields.List(fields.Nested(TaskSchema)) created = fields.Integer() modified = fields.Integer() + deleted = fields.Boolean(allow_none=True) # noinspection PyUnusedLocalname @post_load @@ -80,15 +81,18 @@ class ProjectSchema(Schema): class Project(object): - def __init__(self, - project_id=None, - name=None, - brain=None, - owner=None, - tasks=None, - jobs=None, - created=None, - modified=None): + def __init__( + self, + project_id=None, + name=None, + brain=None, + owner=None, + tasks=None, + jobs=None, + created=None, + modified=None, + deleted=None, + ): self.project_id = project_id self.name = name self.brain = brain @@ -107,21 +111,25 @@ class Project(object): self.created = created self.modified = modified + self.deleted = deleted def __repr__(self): return f"Project(project_id={self.project_id}, name={self.name}, owner={self.owner})" class Task(object): - def __init__(self, - task_id=None, - name=None, - created=None, - modified=None, - annotations=None, - jobs=None, - no_aux_labels=None, - model=None): + def __init__( + self, + task_id=None, + name=None, + created=None, + modified=None, + annotations=None, + jobs=None, + no_aux_labels=None, + model=None, + deleted=None, + ): self.task_id = task_id self.name = name self.annotations = annotations @@ -143,17 +151,14 @@ class Task(object): self.created = created self.modified = modified + self.deleted = deleted def __repr__(self): return f"Task(task_id={self.task_id}, name={self.name})" class Annotation(object): - def __init__(self, - section=None, - original_name=None, - name=None, - path=None): + def __init__(self, section=None, original_name=None, name=None, path=None): self.section = section self.name = name self.path = path @@ -163,10 +168,7 @@ class Annotation(object): class Job(object): - def __init__(self, - job_id=None, - status=None, - job_configuration=None): + def __init__(self, job_id=None, status=None, job_configuration=None): self.job_id = job_id self.status = status self.job_configuration = job_configuration @@ -176,18 +178,20 @@ class Job(object): class JobConfiguration(object): - def __init__(self, - account=None, - time=None, - job_type=None, - gres=None, - mail_type=None, - mail_user=None, - num_nodes=None, - partition=None, - reservation=None, - model=None, - kwargs=None): + def __init__( + self, + account=None, + time=None, + job_type=None, + gres=None, + mail_type=None, + mail_user=None, + num_nodes=None, + partition=None, + reservation=None, + model=None, + kwargs=None, + ): self.account = account self.time = time self.job_type = job_type diff --git a/microdraw/src/atlas_ui.js b/microdraw/src/atlas_ui.js index 81fb361..18d6414 100644 --- a/microdraw/src/atlas_ui.js +++ b/microdraw/src/atlas_ui.js @@ -170,6 +170,12 @@ var app = new Vue({ } return annotations; }, + existing_projects: function () { + // Only existing projects + return this.projects.filter((item) => { + return !item.deleted; + }); + }, sorted_tasks: function () { if (this.selected_project === null) { // No tasks @@ -180,7 +186,9 @@ var app = new Vue({ a.name.localeCompare(b.name), ); } - return this.selected_project.tasks; + return this.selected_project.tasks.filter((item) => { + return !item.deleted; + }); }, }, methods: { @@ -204,7 +212,7 @@ var app = new Vue({ success: function (data) { _this.projects = data; if (_this.projects.length > 0) { - _this.selected_project = _this.projects[0]; + _this.selected_project = _this.existing_projects[0]; } post_message(`Got ${_this.projects.length} projects from server.`); }, @@ -260,6 +268,11 @@ var app = new Vue({ // To edit a project, we set it as selected project this.selected_project = project; }, + getProjectTasks: function (project) { + return project.tasks.filter((item) => { + return !item.deleted; + }); + }, saveProject: function (project) { if (project.created === undefined) { // This is a new project, so we add it to the project list @@ -323,8 +336,8 @@ var app = new Vue({ }); } }, - deleteProject: function (project_index) { - let project = this.projects[project_index]; + deleteProject: function (project) { + let project_index = this.projects.findIndex((item) => item === project); // Confirm delete let confirm_delete = confirm( diff --git a/microdraw/src/microdraw.html b/microdraw/src/microdraw.html index 88b6cff..1bbcf02 100755 --- a/microdraw/src/microdraw.html +++ b/microdraw/src/microdraw.html @@ -1,336 +1,724 @@ <html> + <head> + <title>MicroDraw</title> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,user-scalable=no" /> + <link + rel="stylesheet" + href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" + integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" + crossorigin="anonymous" + /> + <link rel="stylesheet" href="css/microdraw.css" type="text/css" /> + <link rel="stylesheet" href="css/atlas_ui.css" type="text/css" /> + <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" /> + </head> - <head> - <title>MicroDraw</title> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width,user-scalable=no" /> - <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> - <link rel="stylesheet" href="css/microdraw.css" type="text/css" /> - <link rel="stylesheet" href="css/atlas_ui.css" type="text/css" /> - <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" /> - </head> - - <body> - <!-- Load javascript --> - <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script> - <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> - <div id="atlas_app"> - <div id="menuButton"> - <img class="button" id="openMenu" title="Menu" src="img/openMenu.svg" /> - </div> - - <!-- Toolbar --> - <div id="menuBar" class="table"> - - <div class="md-row"> - <div id="login"></div> - <img class="button" id="closeMenu" title="Close Menu" src="img/closeMenu.svg" /> - </div> - - <div class="md-row"> - <div id="myNavigator"></div> - </div> - - <div class="md-row"> - <div class="cell" style="text-align:center"> - <div class="buttonGroup"> - <img class="button" id="zoom" title="Navigator" src="img/navigate.svg" /> - <img class="button" id="home" title="Home" src="img/home.svg" /> - <img class="button" id="zoom-in" title="Zoom in" src="img/plus.svg" /> - <img class="button" id="zoom-out" title="Zoom out" src="img/minus.svg" /> - <img class="button" id="rotate-right" title="Rotate right" src="img/plus.svg" /> - <img class="button" id="rotate-left" title="Rotate left" src="img/minus.svg" /> - - <div> - <img class="button" id="prev" title="Previous slice" src="img/prev.svg" /> - <input id="slice-name" list="slice-names" style="width:80px" /> - <img class="button" id="next" title="Next slice" src="img/next.svg" /> - </div> - <input type="range" id="slider" class="sliderClass" /> - </div> - <div class="buttonGroup"> - <img class="button" id="select" title="Select" src="img/select.svg" /> - <img class="button" id="draw" title="Draw" src="img/draw.svg" /> - <img class="button" id="draw-polygon" title="Draw polygon" src="img/draw-polygon.svg" /> - <img class="button" id="simplify" title="Simplify" src="img/simplify.svg" /> - <img class="button" id="addpoint" title="Add point" src="img/addPoint.svg" /> - <img class="button" id="delpoint" title="Delete point" src="img/deletePoint.svg" /> - <img class="button" id="addregion" title="Union of regions" src="img/union.svg" /> - <img class="button" id="delregion" title="Subtraction of regions" src="img/subtract.svg" /> - <img class="button" id="splitregion" title="Split region" src="img/split.svg" /> - <img class="button" id="rotate" title="Rotate region" src="img/rotate.svg" /> - <img class="button" id="flip" title="Flip region" src="img/flip.svg" /> - <img class="button" id="snappoint" title="Snap two points on top of each other" - src="img/rotate.svg" /> - <img class="button" id="handle" title="Toggle handles" src="img/handle.svg" /> - <img class="button" id="text" title="Add text" src="img/text.svg" /> - <img class="button" id="point" title="Add point" src="img/addCross.svg" /> - </div> - <div class="buttonGroup"> - <img class="button" id="save" title="Save annotations" src="img/save.svg" /> - <img class="button" id="copy" title="Copy path" src="img/copy.svg" /> - <img class="button" id="paste" title="Paste path" src="img/paste.svg" /> - <img class="button" id="delete" title="Delete region" src="img/delete.svg" /> - <img class="button" id="load" title="Load annotations from db" src="img/load.svg" /> - </div> - - <div class="buttonGroup"> - <select id="overlays"> - <option value="none">No Overlay</option> - </select> - <input type="range" class="sliderClass" id="alpha-overlay" - title="Change the transparency of the overlay" /> - </div> - <div id="mouseCoordinates" class="small-text"> - x: 0, y: 0 - </div> - <div id="rgbValues" class="small-text"> - RGB: (000,000,000) - </div> - </div> - - </div> - - <div class="md-row"> - <div class="btn-group" role="group" aria-label="basic example"> - <button v-on:click="initializeAtlas" type="button" class="btn btn-primary" data-toggle="modal" - data-target="#atlas_ui_projects_modal"> - ATLaS UI - </button> - <button type="button" class="btn btn-secondary" data-toggle="modal" - v-on:click="initializeAnnotationImport" data-target="#atlas_ui_imports_modal"> - Import - </button> - </div> - </div> - - <div class="md-row"> - <div class="cell"> - <br />Regions <img class="eye" id="eye_all" title="Toggle all regions" src="img/eyeOpened.svg"> - <div id="regionList"></div> - </div> - </div> - </div> - - <div id="colorSelector"> - stroke color - <select id="selectStrokeColor" onChange="onSelectStrokeColor();"> - <option value="0">black</option> - <option value="1">white</option> - <option value="2">red</option> - <option value="3">green</option> - <option value="4">blue</option> - <option value="5">yellow</option> - </select> - <br> - <br> - stroke width - <input type="button" id="strokeWidthDec" value="-" onClick="onStrokeWidthDec();"><input type="button" - id="strokeWidthInc" value="+" onClick="onStrokeWidthInc();"> - <br> - <br> - fill color - <input type="color" id="fillColorPicker" value="#ff0000" onChange="onFillColorPicker(this.value);"> - <br> - <br> - α<input type="range" min="0" max="100" id="alphaSlider" onInput="onAlphaSlider(this.value);"><input - id="alphaFill" onInput="onAlphaInput(this.value);"> - <br> - <br> - <input type="button" id="okStrokeColor" value="ok" onClick="setRegionColor();"> - </div> - <!-- Region Picker --> - <div id="regionPicker"> - </div> - <!-- OpenSeadragon viewer --> - <div id="openseadragon1" style="width:100%"> - </div> - <!-- alert/info box after saving --> - <div id="saveDialog"></div> - <!-- naat logo --> - <a id="logo" href="http://neuroanatomy.github.io"> - <img src="img/naat.svg" style="width:2.5em" /> - </a> - <!-- ATLaS UI dialogue --> - <div class="modal fade" id="atlas_ui_projects_modal" tabindex="-1" role="dialog" - aria-labelledby="exampleModalLabel" aria-hidden="true"> - <div class="modal-dialog modal-lg" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">ATLaS UI</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - - <!-- Body of dialogue --> - <div class="modal-body"> - <!-- List of projects --> - <div class="form-group"> - <label for="atlas_project_list">Projects</label> - <ol id="atlas_project_list" - class="list-group atlas_project_list border border-dark rounded bg-light"> - <li v-for="(project, index) in projects" class="list-group-item list-group-item-action"> - <div class="form-row"> - <!-- <label class="col-1 col-form-label">Name</label> --> - <div class="col-1"> - <input v-model="project.project_id" class="form-control" type="text" readonly /> - </div> - <div class="col-4"> - <input v-model="project.name" class="form-control" type="text" readonly /> - </div> - <label class="col-1 col-form-label">Brain</label> - <div class="col-1"> - <input v-model="project.brain" type="text" class="form-control" readonly> - </div> - <label class="col-1 col-form-label">Tasks</label> - <div class="col-1"> - <input v-model="project.tasks.length" type="text" class="form-control" - readonly> - </div> - <div class="col-3"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-primary" data-toggle="modal" - data-target="#atlas_ui_tasks_modal" v-on:click="editProject(project)">Edit</button> - <div class="btn-group" role="group"> - <button type="button" class="btn btn-danger" - v-on:click="deleteProject(index)">Delete</button> - </div> - </div> - </div> - </div> - </li> - </ol> - </div> - - <!-- Button group --> - <div class="btn-group" role="group"> - <button v-on:click="newProject" type="button" class="btn btn-primary" data-toggle="modal" - data-target="#atlas_ui_tasks_modal">New project...</button> - </div> - <!-- Log messages --> - <!-- <div class="overflow-auto" id="messages"></div> --> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> - </div> - </div> - </div> - </div> - <div class="modal fade" id="atlas_ui_tasks_modal" tabindex="-1" role="dialog" - aria-labelledby="exampleModalLabel" aria-hidden="true" v-if="selected_project !== null"> - <div class="modal-dialog modal-xl" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel"> - {{selected_project.project_id}} - {{selected_project.name}} - </h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close" v-on:click="closeProject()"> - <span aria-hidden="true">×</span> - </button> - </div> - - <!-- Body of dialogue --> - <div class="modal-body"> - <div class="form-group"> - <!-- Project information --> - <div class="form-group"> - <label for="atlas_ui_project_form_name">Name</label> - <input v-model="selected_project.name" type="text" class="form-control" - id="atlas_ui_project_form_name"> - </div> - <div class="form-group"> - <label for="atlas_ui_project_form_brain">Brain</label> - <input v-model="selected_project.brain" type="text" class="form-control" - id="atlas_ui_project_form_brain"> - </div> - - - <!-- List of tasks --> - <label for="atlas_task_list">Tasks</label> - <ol id="atlas_task_list" - class="list-group atlas_task_list border border-dark rounded bg-light" - v-if="selected_project !== null"> - <li v-for="(task, index) in sorted_tasks" - class="list-group-item list-group-item-action"> - <div class="form-row"> - <input v-model="task.task_id" class="col-1 form-control" type="text" readonly /> - <div class="col-2"> - <input v-model="task.name" class="form-control" type="text" readonly /> - </div> - <label class="col-2 col-form-label text-right">Annotations</label> - <div class="col-1"> - <input v-model="task.annotations.length" class="form-control" type="text" - readonly /> - </div> - <label class="col-1 col-form-label text-right">Jobs</label> - <div class="col-1"> - <input v-model="task.jobs.length" class="form-control" type="text" - readonly /> - </div> - <div class="col-4"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-primary" data-toggle="modal" - data-target="#atlas_ui_annotations_modal" - v-on:click="editTask(task)">Edit</button> - <button type="button" class="btn btn-info" data-toggle="modal" - data-target="#atlas_ui_jobs_modal" v-on:click="showJobsForTask(selected_project, task)">Jobs</button> - <button type="button" class="btn btn-default" v-on:click="getResultLink(selected_project, task)"> - <img src="static/results.svg" width="20" height="20"/> - </button> - <button type="button" class="btn btn-danger" v-on:click="deleteTask(selected_project, task)">Delete</button> - - </div> - </div> - </div> - </li> - </ol> - </div> - - - <!-- Button group --> - <div class="btn-group" role="group"> - <button v-on:click="newTask" type="button" class="btn btn-primary" data-toggle="modal" - data-target="#atlas_ui_annotations_modal">New task...</button> - <button type="button" class="btn btn-success" data-toggle="modal" data-target="#atlas_ui_task_wizard_modal" - v-on:click="initializeTaskWizard">Task wizard</button> - </div> - <div class="btn-group" role="group"> - <button type="button" class="btn btn-info" v-on:click="submitAllJobsForProject(selected_project, submitTrainingJob)">Train all</button> - <button type="button" class="btn btn-info" v-on:click="submitAllJobsForProject(selected_project, submitPredictionJob)">Predict all</button> - <button type="button" class="btn btn-info" v-on:click="submitAllJobsForProject(selected_project, submitTrainingPredictionJob)">Train+Predict all</button> - <button type="button" class="btn btn-danger" v-on:click="cancelAllJobsForProject(selected_project)">Cancel all</button> - </div> - - <div class="form-group" id="atlas_ui_task_options"> - <input type="checkbox" v-model="sort_tasks_by_name" id="atlas_ui_task_sort_by_name"/> - <label for="atlas_ui_task_sort_by_name">Sort by name</label><p> - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-primary" data-dismiss="modal" - v-on:click="saveProject(selected_project)">Save</button> - <button type="button" class="btn btn-secondary" data-dismiss="modal" v-on:click="closeProject()">Close</button> - </div> - </div> - </div> - </div> - - <div class="modal fade" id="atlas_ui_annotations_modal" tabindex="-1" role="dialog" - aria-labelledby="exampleModalLabel" aria-hidden="true" v-if="selected_task !== null"> - <div class="modal-dialog modal-lg" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel"> - {{selected_task.task_id}} - {{selected_project.name}} - {{selected_task.name}}</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close" v-on:click="closeTask()"> - <span aria-hidden="true">×</span> - </button> - </div> + <body> + <!-- Load javascript --> + <script + src="https://code.jquery.com/jquery-3.3.1.slim.min.js" + integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" + crossorigin="anonymous" + ></script> + <script + src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" + integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" + crossorigin="anonymous" + ></script> + <script + src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" + integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" + crossorigin="anonymous" + ></script> + <div id="atlas_app"> + <div id="menuButton"> + <img class="button" id="openMenu" title="Menu" src="img/openMenu.svg" /> + </div> + + <!-- Toolbar --> + <div id="menuBar" class="table"> + <div class="md-row"> + <div id="login"></div> + <img + class="button" + id="closeMenu" + title="Close Menu" + src="img/closeMenu.svg" + /> + </div> + + <div class="md-row"> + <div id="myNavigator"></div> + </div> + + <div class="md-row"> + <div class="cell" style="text-align: center"> + <div class="buttonGroup"> + <img + class="button" + id="zoom" + title="Navigator" + src="img/navigate.svg" + /> + <img class="button" id="home" title="Home" src="img/home.svg" /> + <img + class="button" + id="zoom-in" + title="Zoom in" + src="img/plus.svg" + /> + <img + class="button" + id="zoom-out" + title="Zoom out" + src="img/minus.svg" + /> + <img + class="button" + id="rotate-right" + title="Rotate right" + src="img/plus.svg" + /> + <img + class="button" + id="rotate-left" + title="Rotate left" + src="img/minus.svg" + /> + + <div> + <img + class="button" + id="prev" + title="Previous slice" + src="img/prev.svg" + /> + <input id="slice-name" list="slice-names" style="width: 80px" /> + <img + class="button" + id="next" + title="Next slice" + src="img/next.svg" + /> + </div> + <input type="range" id="slider" class="sliderClass" /> + </div> + <div class="buttonGroup"> + <img + class="button" + id="select" + title="Select" + src="img/select.svg" + /> + <img class="button" id="draw" title="Draw" src="img/draw.svg" /> + <img + class="button" + id="draw-polygon" + title="Draw polygon" + src="img/draw-polygon.svg" + /> + <img + class="button" + id="simplify" + title="Simplify" + src="img/simplify.svg" + /> + <img + class="button" + id="addpoint" + title="Add point" + src="img/addPoint.svg" + /> + <img + class="button" + id="delpoint" + title="Delete point" + src="img/deletePoint.svg" + /> + <img + class="button" + id="addregion" + title="Union of regions" + src="img/union.svg" + /> + <img + class="button" + id="delregion" + title="Subtraction of regions" + src="img/subtract.svg" + /> + <img + class="button" + id="splitregion" + title="Split region" + src="img/split.svg" + /> + <img + class="button" + id="rotate" + title="Rotate region" + src="img/rotate.svg" + /> + <img + class="button" + id="flip" + title="Flip region" + src="img/flip.svg" + /> + <img + class="button" + id="snappoint" + title="Snap two points on top of each other" + src="img/rotate.svg" + /> + <img + class="button" + id="handle" + title="Toggle handles" + src="img/handle.svg" + /> + <img + class="button" + id="text" + title="Add text" + src="img/text.svg" + /> + <img + class="button" + id="point" + title="Add point" + src="img/addCross.svg" + /> + </div> + <div class="buttonGroup"> + <img + class="button" + id="save" + title="Save annotations" + src="img/save.svg" + /> + <img + class="button" + id="copy" + title="Copy path" + src="img/copy.svg" + /> + <img + class="button" + id="paste" + title="Paste path" + src="img/paste.svg" + /> + <img + class="button" + id="delete" + title="Delete region" + src="img/delete.svg" + /> + <img + class="button" + id="load" + title="Load annotations from db" + src="img/load.svg" + /> + </div> + + <div class="buttonGroup"> + <select id="overlays"> + <option value="none">No Overlay</option> + </select> + <input + type="range" + class="sliderClass" + id="alpha-overlay" + title="Change the transparency of the overlay" + /> + </div> + <div id="mouseCoordinates" class="small-text">x: 0, y: 0</div> + <div id="rgbValues" class="small-text">RGB: (000,000,000)</div> + </div> + </div> + + <div class="md-row"> + <div class="btn-group" role="group" aria-label="basic example"> + <button + v-on:click="initializeAtlas" + type="button" + class="btn btn-primary" + data-toggle="modal" + data-target="#atlas_ui_projects_modal" + > + ATLaS UI + </button> + <button + type="button" + class="btn btn-secondary" + data-toggle="modal" + v-on:click="initializeAnnotationImport" + data-target="#atlas_ui_imports_modal" + > + Import + </button> + </div> + </div> + + <div class="md-row"> + <div class="cell"> + <br />Regions + <img + class="eye" + id="eye_all" + title="Toggle all regions" + src="img/eyeOpened.svg" + /> + <div id="regionList"></div> + </div> + </div> + </div> + + <div id="colorSelector"> + stroke color + <select id="selectStrokeColor" onChange="onSelectStrokeColor();"> + <option value="0">black</option> + <option value="1">white</option> + <option value="2">red</option> + <option value="3">green</option> + <option value="4">blue</option> + <option value="5">yellow</option> + </select> + <br /> + <br /> + stroke width + <input + type="button" + id="strokeWidthDec" + value="-" + onClick="onStrokeWidthDec();" + /><input + type="button" + id="strokeWidthInc" + value="+" + onClick="onStrokeWidthInc();" + /> + <br /> + <br /> + fill color + <input + type="color" + id="fillColorPicker" + value="#ff0000" + onChange="onFillColorPicker(this.value);" + /> + <br /> + <br /> + α<input + type="range" + min="0" + max="100" + id="alphaSlider" + onInput="onAlphaSlider(this.value);" + /><input id="alphaFill" onInput="onAlphaInput(this.value);" /> + <br /> + <br /> + <input + type="button" + id="okStrokeColor" + value="ok" + onClick="setRegionColor();" + /> + </div> + <!-- Region Picker --> + <div id="regionPicker"></div> + <!-- OpenSeadragon viewer --> + <div id="openseadragon1" style="width: 100%"></div> + <!-- alert/info box after saving --> + <div id="saveDialog"></div> + <!-- naat logo --> + <a id="logo" href="http://neuroanatomy.github.io"> + <img src="img/naat.svg" style="width: 2.5em" /> + </a> + <!-- ATLaS UI dialogue --> + <div + class="modal fade" + id="atlas_ui_projects_modal" + tabindex="-1" + role="dialog" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + > + <div class="modal-dialog modal-lg" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel">ATLaS UI</h5> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > + <span aria-hidden="true">×</span> + </button> + </div> + + <!-- Body of dialogue --> + <div class="modal-body"> + <!-- List of projects --> + <div class="form-group"> + <label for="atlas_project_list">Projects</label> + <ol + id="atlas_project_list" + class="list-group atlas_project_list border border-dark rounded bg-light" + > + <li + v-for="(project, index) in existing_projects" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <!-- <label class="col-1 col-form-label">Name</label> --> + <div class="col-1"> + <input + v-model="project.project_id" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-4"> + <input + v-model="project.name" + class="form-control" + type="text" + readonly + /> + </div> + <label class="col-1 col-form-label">Brain</label> + <div class="col-1"> + <input + v-model="project.brain" + type="text" + class="form-control" + readonly + /> + </div> + <label class="col-1 col-form-label">Tasks</label> + <div class="col-1"> + <input + v-model="getProjectTasks(project).length" + type="text" + class="form-control" + readonly + /> + </div> + <div class="col-3"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-primary" + data-toggle="modal" + data-target="#atlas_ui_tasks_modal" + v-on:click="editProject(project)" + > + Edit + </button> + <div class="btn-group" role="group"> + <button + type="button" + class="btn btn-danger" + v-on:click="deleteProject(project)" + > + Delete + </button> + </div> + </div> + </div> + </div> + </li> + </ol> + </div> + + <!-- Button group --> + <div class="btn-group" role="group"> + <button + v-on:click="newProject" + type="button" + class="btn btn-primary" + data-toggle="modal" + data-target="#atlas_ui_tasks_modal" + > + New project... + </button> + </div> + <!-- Log messages --> + <!-- <div class="overflow-auto" id="messages"></div> --> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + > + Close + </button> + </div> + </div> + </div> + </div> + <div + class="modal fade" + id="atlas_ui_tasks_modal" + tabindex="-1" + role="dialog" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + v-if="selected_project !== null" + > + <div class="modal-dialog modal-xl" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel"> + {{selected_project.project_id}} - {{selected_project.name}} + </h5> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + v-on:click="closeProject()" + > + <span aria-hidden="true">×</span> + </button> + </div> + + <!-- Body of dialogue --> + <div class="modal-body"> + <div class="form-group"> + <!-- Project information --> + <div class="form-group"> + <label for="atlas_ui_project_form_name">Name</label> + <input + v-model="selected_project.name" + type="text" + class="form-control" + id="atlas_ui_project_form_name" + /> + </div> + <div class="form-group"> + <label for="atlas_ui_project_form_brain">Brain</label> + <input + v-model="selected_project.brain" + type="text" + class="form-control" + id="atlas_ui_project_form_brain" + /> + </div> + + <!-- List of tasks --> + <label for="atlas_task_list">Tasks</label> + <ol + id="atlas_task_list" + class="list-group atlas_task_list border border-dark rounded bg-light" + v-if="selected_project !== null" + > + <li + v-for="(task, index) in sorted_tasks" + class="list-group-item list-group-item-action" + > + <div class="form-row" v-if="!task.deleted"> + <input + v-model="task.task_id" + class="col-1 form-control" + type="text" + readonly + /> + <div class="col-2"> + <input + v-model="task.name" + class="form-control" + type="text" + readonly + /> + </div> + <label class="col-2 col-form-label text-right" + >Annotations</label + > + <div class="col-1"> + <input + v-model="task.annotations.length" + class="form-control" + type="text" + readonly + /> + </div> + <label class="col-1 col-form-label text-right" + >Jobs</label + > + <div class="col-1"> + <input + v-model="task.jobs.length" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-4"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-primary" + data-toggle="modal" + data-target="#atlas_ui_annotations_modal" + v-on:click="editTask(task)" + > + Edit + </button> + <button + type="button" + class="btn btn-info" + data-toggle="modal" + data-target="#atlas_ui_jobs_modal" + v-on:click="showJobsForTask(selected_project, task)" + > + Jobs + </button> + <button + type="button" + class="btn btn-default" + v-on:click="getResultLink(selected_project, task)" + > + <img + src="static/results.svg" + width="20" + height="20" + /> + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="deleteTask(selected_project, task)" + > + Delete + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + + <!-- Button group --> + <div class="btn-group" role="group"> + <button + v-on:click="newTask" + type="button" + class="btn btn-primary" + data-toggle="modal" + data-target="#atlas_ui_annotations_modal" + > + New task... + </button> + <button + type="button" + class="btn btn-success" + data-toggle="modal" + data-target="#atlas_ui_task_wizard_modal" + v-on:click="initializeTaskWizard" + > + Task wizard + </button> + </div> + <div class="btn-group" role="group"> + <button + type="button" + class="btn btn-info" + v-on:click="submitAllJobsForProject(selected_project, submitTrainingJob)" + > + Train all + </button> + <button + type="button" + class="btn btn-info" + v-on:click="submitAllJobsForProject(selected_project, submitPredictionJob)" + > + Predict all + </button> + <button + type="button" + class="btn btn-info" + v-on:click="submitAllJobsForProject(selected_project, submitTrainingPredictionJob)" + > + Train+Predict all + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="cancelAllJobsForProject(selected_project)" + > + Cancel all + </button> + </div> + + <div class="form-group" id="atlas_ui_task_options"> + <input + type="checkbox" + v-model="sort_tasks_by_name" + id="atlas_ui_task_sort_by_name" + /> + <label for="atlas_ui_task_sort_by_name">Sort by name</label> + <p></p> + </div> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-primary" + data-dismiss="modal" + v-on:click="saveProject(selected_project)" + > + Save + </button> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + v-on:click="closeProject()" + > + Close + </button> + </div> + </div> + </div> + </div> + + <div + class="modal fade" + id="atlas_ui_annotations_modal" + tabindex="-1" + role="dialog" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + v-if="selected_task !== null" + > + <div class="modal-dialog modal-lg" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel"> + {{selected_task.task_id}} - {{selected_project.name}} - + {{selected_task.name}} + </h5> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + v-on:click="closeTask()" + > + <span aria-hidden="true">×</span> + </button> + </div> <!-- Body of dialogue --> <div class="modal-body"> @@ -338,161 +726,317 @@ <!-- Project information --> <div class="form-group"> <label for="atlas_ui_task_form_name">Name</label> - <input v-model="selected_task.name" type="text" class="form-control" - id="atlas_ui_task_form_name"> - <label for="atlas_ui_task_form_filter">Annotation filter</label> - <input v-model="annotation_filter" type="text" class="form-control" - id="atlas_ui_task_form_filter"> + <input + v-model="selected_task.name" + type="text" + class="form-control" + id="atlas_ui_task_form_name" + /> + <label for="atlas_ui_task_form_filter" + >Annotation filter</label + > + <input + v-model="annotation_filter" + type="text" + class="form-control" + id="atlas_ui_task_form_filter" + /> <label for="atlas_ui_task_form_model">Model</label> - <select class="form-control" id="atlas_ui_task_form_model" v-model="selected_task.model"> - <option v-for="model in model_options"> - {{ model }} - </option> + <select + class="form-control" + id="atlas_ui_task_form_model" + v-model="selected_task.model" + > + <option v-for="model in model_options">{{ model }}</option> </select> - <input type="checkbox" v-model="enable_auto_rename" id="atlas_ui_task_form_auto_rename"/> - <label for="atlas_ui_task_form_auto_rename">Enable auto-rename</label> - - <input type="checkbox" v-model="selected_task.no_aux_labels" id="atlas_ui_task_form_aux_labels"/> - <label for="atlas_ui_task_form_aux_labels">No auxiliary labels</label> - </div> - - <!-- List of annotations --> - <label for="atlas_annotation_list">Annotations</label> - <ol id="atlas_annotation_list_selected" - class="list-group atlas_annotation_list border border-dark rounded bg-light" - v-if="selected_task !== null"> - <li v-for="(annotation, index) in selected_task.annotations" - class="list-group-item list-group-item-action"> - <div class="form-row"> - <label class="col-1 col-form-label text-right">Name</label> - <div class="col-3"> - <input v-model="annotation.name" class="form-control" type="text" - readonly /> - </div> - <label class="col-1 col-form-label text-right">Section</label> - <div class="col-2"> - <input v-model="annotation.section" class="form-control" type="text" - readonly /> - </div> - <div class="col-5"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-danger" - v-on:click="removeAnnotationFromTask(index)">Remove</button> - </div> - </div> - </div> - </li> - </ol> - - <label for="atlas_annotation_list">Available annotations</label> - <ol id="atlas_annotation_list_available" - class="list-group atlas_annotation_list border border-dark rounded bg-light" - v-if="selected_task !== null"> - <li v-for="(annotation, index) in available_annotations" v-if="filterAnnotation(annotation_filter, annotation.name)" class="list-group-item list-group-item-action"> - <div class="form-row"> - <label class="col-1 col-form-label">Name</label> - <div class="col-3"> - <input v-model="annotation.name" class="form-control" type="text" /> - </div> - <label class="col-1 col-form-label">Section</label> - <div class="col-2"> - <input v-model="annotation.section" class="form-control" type="text" - readonly /> - </div> - <div class="col-5"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-primary" - v-on:click="addAnnotationToTask(index)">Add</button> - </div> - </div> - </div> - </li> - </ol> - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-primary" data-dismiss="modal" - v-on:click="saveTask(selected_project, selected_task)">Save</button> - <button type="button" class="btn btn-secondary" data-dismiss="modal" v-on:click="closeTask()">Close</button> - </div> - </div> - </div> - </div> - - <div class="modal fade" id="atlas_ui_task_wizard_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" role="dialog"> - <div class="modal-dialog modal-xl" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">Task wizard</h5> - <!-- <div class="text-center" v-if="task_wizard.loading"> --> - <!-- <div class="spinner-border spinner-border-sm m-1" role="status"> --> - <!-- <span class="sr-only">Loading...</span> --> - <!-- </div> --> - <!-- </div> --> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - - <!-- Body of dialogue --> - <div class="modal-body"> - <div class="container col-12"> - <div class="row"> - - <!-- Sections --> - <div class="col-3 form"> - <div class="form-group"> - <label for="atlas_task_wizard_section_list">Sections</label> - <ol id="atlas_task_wizard_section_list" - class="list-group atlas_task_wizard_section_list border border-dark rounded bg-light"> - <li v-for="(section_item, index) in task_wizard.section_data" class="list-group-item list-group-item-action"> - <div class="form-row"> - <button type="button" class="btn col-12" v-bind:class="{'btn-primary': section_item.selected, 'btn-danger': ! section_item.selected }" v-on:click="taskWizardSectionToggle(section_item)"> - {{ section_item.section }} ({{ section_item.annotations.length }} annotations) - </button> - </div> - </li> - </ol> - </div> - <div class="btn-group" role="group" id="atlas_ui_task_wizard_section_controls"> - <button type="button" class="btn btn-primary" v-on:click="taskWizardSectionSelect(true)" :disabled="task_wizard.section_data.length == 0">Enable all</button> - <button type="button" class="btn btn-danger" v-on:click="taskWizardSectionSelect(false)" :disabled="task_wizard.section_data.length == 0">Disable all</button> - </div> - <div class="btn-group" role="group" id="atlas_ui_task_wizard_section_controls2"> - <button type="button" class="btn btn-success" v-on:click="taskWizardAutoPair" data-toggle="modal" data-target="#atlas_ui_task_wizard_import_modal">Import</button> - </div> - </div> - - <!-- Annotations --> - <div class="col-4 form"> - <div class="form-group"> - <label for="atlas_task_wizard_annotation_list">Annotations</label> - <ol id="atlas_task_wizard_annotation_list" class="atlas_task_wizard_annotation_list list-group border border-dark rounded bg-light"> - <li v-for="(annotation_item, index) in task_wizard_annotations" class="list-group-item list-group-item-action"> - <div class="form-row"> - <label class="col-2"> - {{ annotation_item.section_item.section }} - </label> - <div class="col-8"> - <input v-model="annotation_item.annotation.name" class="form-control" type="text"/> - </div> - <div class="col-2"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-primary" v-on:click="taskWizardAddAnnotation(task_wizard.selected_task, annotation_item)">+</button> - </div> - </div> - </div> - </li> - </ol> - </div> - <div class="form-group"> - <label for="atlas_ui_task_wizard_filter">Annotation filter (supports RegExp)</label> - <input v-model="annotation_filter" type="text" class="form-control" id="atlas_ui_task_wizard_filter"> - </div> - </div> + <input + type="checkbox" + v-model="enable_auto_rename" + id="atlas_ui_task_form_auto_rename" + /> + <label for="atlas_ui_task_form_auto_rename" + >Enable auto-rename</label + > + + <input + type="checkbox" + v-model="selected_task.no_aux_labels" + id="atlas_ui_task_form_aux_labels" + /> + <label for="atlas_ui_task_form_aux_labels" + >No auxiliary labels</label + > + </div> + + <!-- List of annotations --> + <label for="atlas_annotation_list">Annotations</label> + <ol + id="atlas_annotation_list_selected" + class="list-group atlas_annotation_list border border-dark rounded bg-light" + v-if="selected_task !== null" + > + <li + v-for="(annotation, index) in selected_task.annotations" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <label class="col-1 col-form-label text-right" + >Name</label + > + <div class="col-3"> + <input + v-model="annotation.name" + class="form-control" + type="text" + readonly + /> + </div> + <label class="col-1 col-form-label text-right" + >Section</label + > + <div class="col-2"> + <input + v-model="annotation.section" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-5"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-danger" + v-on:click="removeAnnotationFromTask(index)" + > + Remove + </button> + </div> + </div> + </div> + </li> + </ol> + + <label for="atlas_annotation_list">Available annotations</label> + <ol + id="atlas_annotation_list_available" + class="list-group atlas_annotation_list border border-dark rounded bg-light" + v-if="selected_task !== null" + > + <li + v-for="(annotation, index) in available_annotations" + v-if="filterAnnotation(annotation_filter, annotation.name)" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <label class="col-1 col-form-label">Name</label> + <div class="col-3"> + <input + v-model="annotation.name" + class="form-control" + type="text" + /> + </div> + <label class="col-1 col-form-label">Section</label> + <div class="col-2"> + <input + v-model="annotation.section" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-5"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="addAnnotationToTask(index)" + > + Add + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-primary" + data-dismiss="modal" + v-on:click="saveTask(selected_project, selected_task)" + > + Save + </button> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + v-on:click="closeTask()" + > + Close + </button> + </div> + </div> + </div> + </div> + + <div + class="modal fade" + id="atlas_ui_task_wizard_modal" + tabindex="-1" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + role="dialog" + > + <div class="modal-dialog modal-xl" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel">Task wizard</h5> + <!-- <div class="text-center" v-if="task_wizard.loading"> --> + <!-- <div class="spinner-border spinner-border-sm m-1" role="status"> --> + <!-- <span class="sr-only">Loading...</span> --> + <!-- </div> --> + <!-- </div> --> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > + <span aria-hidden="true">×</span> + </button> + </div> + + <!-- Body of dialogue --> + <div class="modal-body"> + <div class="container col-12"> + <div class="row"> + <!-- Sections --> + <div class="col-3 form"> + <div class="form-group"> + <label for="atlas_task_wizard_section_list" + >Sections</label + > + <ol + id="atlas_task_wizard_section_list" + class="list-group atlas_task_wizard_section_list border border-dark rounded bg-light" + > + <li + v-for="(section_item, index) in task_wizard.section_data" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <button + type="button" + class="btn col-12" + v-bind:class="{'btn-primary': section_item.selected, 'btn-danger': ! section_item.selected }" + v-on:click="taskWizardSectionToggle(section_item)" + > + {{ section_item.section }} ({{ + section_item.annotations.length }} annotations) + </button> + </div> + </li> + </ol> + </div> + <div + class="btn-group" + role="group" + id="atlas_ui_task_wizard_section_controls" + > + <button + type="button" + class="btn btn-primary" + v-on:click="taskWizardSectionSelect(true)" + :disabled="task_wizard.section_data.length == 0" + > + Enable all + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="taskWizardSectionSelect(false)" + :disabled="task_wizard.section_data.length == 0" + > + Disable all + </button> + </div> + <div + class="btn-group" + role="group" + id="atlas_ui_task_wizard_section_controls2" + > + <button + type="button" + class="btn btn-success" + v-on:click="taskWizardAutoPair" + data-toggle="modal" + data-target="#atlas_ui_task_wizard_import_modal" + > + Import + </button> + </div> + </div> + + <!-- Annotations --> + <div class="col-4 form"> + <div class="form-group"> + <label for="atlas_task_wizard_annotation_list" + >Annotations</label + > + <ol + id="atlas_task_wizard_annotation_list" + class="atlas_task_wizard_annotation_list list-group border border-dark rounded bg-light" + > + <li + v-for="(annotation_item, index) in task_wizard_annotations" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <label class="col-2"> + {{ annotation_item.section_item.section }} + </label> + <div class="col-8"> + <input + v-model="annotation_item.annotation.name" + class="form-control" + type="text" + /> + </div> + <div class="col-2"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="taskWizardAddAnnotation(task_wizard.selected_task, annotation_item)" + > + + + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + <div class="form-group"> + <label for="atlas_ui_task_wizard_filter" + >Annotation filter (supports RegExp)</label + > + <input + v-model="annotation_filter" + type="text" + class="form-control" + id="atlas_ui_task_wizard_filter" + /> + </div> + </div> <!-- Tasks --> <div class="col-5"> @@ -500,334 +1044,687 @@ <div class="form"> <div class="form-group"> <label for="atlas_ui_task_wizard_task_name">Name</label> - <input v-model="task_wizard.selected_task.name" type="text" class="form-control" id="atlas_ui_task_wizard_task_name"> + <input + v-model="task_wizard.selected_task.name" + type="text" + class="form-control" + id="atlas_ui_task_wizard_task_name" + /> </div> <div class="form-group"> <div> - <input type="checkbox" v-model="enable_auto_rename" id="atlas_ui_task_wizard_auto_rename"/> - <label for="atlasui_ui_task_wizard_auto_rename">Enable auto-rename</label> - <input type="checkbox" v-model="task_wizard.selected_task.no_aux_labels" id="atlas_ui_task_form_aux_labels"/> - <label for="atlas_ui_task_form_aux_labels">No auxiliary labels</label> + <input + type="checkbox" + v-model="enable_auto_rename" + id="atlas_ui_task_wizard_auto_rename" + /> + <label for="atlasui_ui_task_wizard_auto_rename" + >Enable auto-rename</label + > + <input + type="checkbox" + v-model="task_wizard.selected_task.no_aux_labels" + id="atlas_ui_task_form_aux_labels" + /> + <label for="atlas_ui_task_form_aux_labels" + >No auxiliary labels</label + > </div> <div> - <input type="checkbox" v-model="task_wizard.section_naming" id="atlas_ui_task_form_section_naming"/> - <label for="atlas_ui_task_form_section_naming">Name by sections</label> + <input + type="checkbox" + v-model="task_wizard.section_naming" + id="atlas_ui_task_form_section_naming" + /> + <label for="atlas_ui_task_form_section_naming" + >Name by sections</label + > </div> <div class="form-group row"> - <label class="col-sm-2 col-form-label" for="atlas_ui_task_wizard_model">Model</label> - <select class="form-control col-sm-10" id="atlas_ui_task_wizard_model" v-model="task_wizard.model"> + <label + class="col-sm-2 col-form-label" + for="atlas_ui_task_wizard_model" + >Model</label + > + <select + class="form-control col-sm-10" + id="atlas_ui_task_wizard_model" + v-model="task_wizard.model" + > <option v-for="model in model_options"> - {{ model }} + {{ model }} </option> </select> </div> </div> - <div class="form-group"> - <!-- List of annotations --> - <label for="atlas_annotation_list_selected">Annotations</label> - <ol id="atlas_annotation_list_selected" class="list-group atlas_task_wizard_small_list border border-dark rounded bg-light"> - <li v-for="(annotation, index) in task_wizard.selected_task.annotations" class="list-group-item list-group-item-action"> - <div class="form-row"> - <label class="col-2"> - {{ annotation.section }} - </label> - <div class="col-8"> - <input v-model="annotation.name" class="form-control" type="text"/> - </div> - <div class="col-2"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-danger" v-on:click="taskWizardRemoveAnnotation(task_wizard.selected_task, index)">-</button> - </div> - </div> - </div> - </li> - </ol> - </div> - - <div class="form-group"> - <div class="btn-group" role="group"> - <button type="button" class="btn btn-primary" v-on:click="taskWizardAddTask(task_wizard.selected_task)" :disabled="task_wizard.selected_task.annotations.length == 0">Save task</button> - <button type="button" class="btn btn-success" v-on:click="taskWizardAddAll(task_wizard.selected_task)" :disabled="task_wizard_annotations.length == 0">Add annotations</button> - <button type="button" class="btn btn-danger" v-on:click="taskWizardRemoveAll(task_wizard.selected_task)" :disabled="task_wizard.selected_task.annotations.length == 0">Remove annotations</button> - </div> - </div> - - <div class="form-group"> - <!-- List of annotations --> - <label for="atlas_ui_task_wizard_task_list">Tasks</label> - <ol id="atlas_ui_task_wizard_task_list" class="list-group atlas_task_wizard_small_list border border-dark rounded bg-light"> - <li v-for="(task, index) in task_wizard.tasks" class="list-group-item list-group-item-action"> - <div class="form-row"> - <div class="col-9"> - <input v-model="task.name" class="form-control" type="text" readonly/> - </div> - <div class="col-3"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-primary" v-on:click="taskWizardEditTask(index)">Edit</button> - <button type="button" class="btn btn-danger" v-on:click="taskWizardRemoveTask(index)">-</button> - </div> - </div> - </div> - </li> - </ol> - </div> - </div> - </div> - - </div> - </div> - - <!-- Loading overlay --> - <div class="loader_overlay" v-bind:class="[task_wizard.loading ? 'show' : '']"></div> - <div class="spanner" v-bind:class="[task_wizard.loading ? 'show' : '']"> - <div class="loader"></div> - <p>Loading annotations...</p> - </div> - - </div> - - <div class="modal-footer"> - <button type="button" class="btn btn-primary" data-dismiss="modal" v-on:click="taskWizardSave()">Save</button> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> - </div> - </div> - </div> - </div> - - <div class="modal fade" id="atlas_ui_task_wizard_import_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" role="dialog"> - <div class="modal-dialog modal-xl" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">Import</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - - <!-- Body of dialogue --> - <div class="modal-body"> - <div class="container col-12"> - <div class="row"> - <textarea class="form-control" id="atlas_ui_task_wizard_import_text" v-model="task_wizard.import_text" placeholder="Enter sections to import. One task per line."></textarea> - </div> - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-primary" v-on:click="taskWizardImport()">Import</button> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> - </div> - </div> - </div> - </div> - - <div class="modal fade" id="atlas_ui_jobs_modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" - aria-hidden="true" v-if="selected_task !== null"> - <div class="modal-dialog modal-lg" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel"> - {{selected_project.name}} - {{selected_task.name}}</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close" v-on:click="closeTask()"> - <span aria-hidden="true">×</span> - </button> - </div> - - <!-- Body of dialogue --> - <div class="modal-body"> - <div class="form-group"> - <label for="atlas_job_list">Jobs</label> - <ol id="atlas_job_list" - class="list-group atlas_job_list border border-dark rounded bg-light" - v-if="selected_task !== null"> - <li v-for="(job, index) in selected_task.jobs" - class="list-group-item list-group-item-action"> - <div class="form-row"> - <label class="col-1 col-form-label text-right">ID</label> - <div class="col-2"> - <input v-model="job.job_id" class="form-control" type="text" readonly /> - </div> - <!-- <label class="col-1 col-form-label text-right">Type</label> --> - <div class="col-2"> - <input v-model="job.job_configuration.job_type" class="form-control" - type="text" readonly /> - </div> - <label class="col-1 col-form-label text-right">Status</label> - <div class="col-3"> - <input v-model="job.status" class="form-control" - type="text" readonly /> - </div> - <div class="col-3"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-info" v-on:click="getJobStatus(selected_project, selected_task, job)">Status</button> - <button type="button" class="btn btn-danger" - v-on:click="cancelJob(selected_project, selected_task, index)">Cancel</button> - </div> - </div> - </div> - </li> - </ol> - </div> - <!-- Button group --> - <div class="btn-group" role="group"> - <button type="button" class="btn btn-primary" v-on:click="submitTrainingJob(selected_project, selected_task)">Start training</button> - <button type="button" class="btn btn-primary" v-on:click="submitPredictionJob(selected_project, selected_task)">Start prediction</button> - <button type="button" class="btn btn-primary" v-on:click="submitTrainingPredictionJob(selected_project, selected_task)">Start training & prediction</button> - <button type="button" class="btn btn-secondary" v-on:click="getJobStatusForAll(selected_project, selected_task)">Update all</button> - <!-- <button type="button" class="btn btn-danger" + <div class="form-group"> + <!-- List of annotations --> + <label for="atlas_annotation_list_selected" + >Annotations</label + > + <ol + id="atlas_annotation_list_selected" + class="list-group atlas_task_wizard_small_list border border-dark rounded bg-light" + > + <li + v-for="(annotation, index) in task_wizard.selected_task.annotations" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <label class="col-2"> + {{ annotation.section }} + </label> + <div class="col-8"> + <input + v-model="annotation.name" + class="form-control" + type="text" + /> + </div> + <div class="col-2"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-danger" + v-on:click="taskWizardRemoveAnnotation(task_wizard.selected_task, index)" + > + - + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + + <div class="form-group"> + <div class="btn-group" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="taskWizardAddTask(task_wizard.selected_task)" + :disabled="task_wizard.selected_task.annotations.length == 0" + > + Save task + </button> + <button + type="button" + class="btn btn-success" + v-on:click="taskWizardAddAll(task_wizard.selected_task)" + :disabled="task_wizard_annotations.length == 0" + > + Add annotations + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="taskWizardRemoveAll(task_wizard.selected_task)" + :disabled="task_wizard.selected_task.annotations.length == 0" + > + Remove annotations + </button> + </div> + </div> + + <div class="form-group"> + <!-- List of annotations --> + <label for="atlas_ui_task_wizard_task_list" + >Tasks</label + > + <ol + id="atlas_ui_task_wizard_task_list" + class="list-group atlas_task_wizard_small_list border border-dark rounded bg-light" + > + <li + v-for="(task, index) in task_wizard.tasks" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <div class="col-9"> + <input + v-model="task.name" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-3"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="taskWizardEditTask(index)" + > + Edit + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="taskWizardRemoveTask(index)" + > + - + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + </div> + </div> + </div> + </div> + + <!-- Loading overlay --> + <div + class="loader_overlay" + v-bind:class="[task_wizard.loading ? 'show' : '']" + ></div> + <div + class="spanner" + v-bind:class="[task_wizard.loading ? 'show' : '']" + > + <div class="loader"></div> + <p>Loading annotations...</p> + </div> + </div> + + <div class="modal-footer"> + <button + type="button" + class="btn btn-primary" + data-dismiss="modal" + v-on:click="taskWizardSave()" + > + Save + </button> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + > + Close + </button> + </div> + </div> + </div> + </div> + + <div + class="modal fade" + id="atlas_ui_task_wizard_import_modal" + tabindex="-1" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + role="dialog" + > + <div class="modal-dialog modal-xl" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel">Import</h5> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > + <span aria-hidden="true">×</span> + </button> + </div> + + <!-- Body of dialogue --> + <div class="modal-body"> + <div class="container col-12"> + <div class="row"> + <textarea + class="form-control" + id="atlas_ui_task_wizard_import_text" + v-model="task_wizard.import_text" + placeholder="Enter sections to import. One task per line." + ></textarea> + </div> + </div> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-primary" + v-on:click="taskWizardImport()" + > + Import + </button> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + > + Close + </button> + </div> + </div> + </div> + </div> + + <div + class="modal fade" + id="atlas_ui_jobs_modal" + tabindex="-1" + role="dialog" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + v-if="selected_task !== null" + > + <div class="modal-dialog modal-lg" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel"> + {{selected_project.name}} - {{selected_task.name}} + </h5> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + v-on:click="closeTask()" + > + <span aria-hidden="true">×</span> + </button> + </div> + + <!-- Body of dialogue --> + <div class="modal-body"> + <div class="form-group"> + <label for="atlas_job_list">Jobs</label> + <ol + id="atlas_job_list" + class="list-group atlas_job_list border border-dark rounded bg-light" + v-if="selected_task !== null" + > + <li + v-for="(job, index) in selected_task.jobs" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <label class="col-1 col-form-label text-right">ID</label> + <div class="col-2"> + <input + v-model="job.job_id" + class="form-control" + type="text" + readonly + /> + </div> + <!-- <label class="col-1 col-form-label text-right">Type</label> --> + <div class="col-2"> + <input + v-model="job.job_configuration.job_type" + class="form-control" + type="text" + readonly + /> + </div> + <label class="col-1 col-form-label text-right" + >Status</label + > + <div class="col-3"> + <input + v-model="job.status" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-3"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-info" + v-on:click="getJobStatus(selected_project, selected_task, job)" + > + Status + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="cancelJob(selected_project, selected_task, index)" + > + Cancel + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + <!-- Button group --> + <div class="btn-group" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="submitTrainingJob(selected_project, selected_task)" + > + Start training + </button> + <button + type="button" + class="btn btn-primary" + v-on:click="submitPredictionJob(selected_project, selected_task)" + > + Start prediction + </button> + <button + type="button" + class="btn btn-primary" + v-on:click="submitTrainingPredictionJob(selected_project, selected_task)" + > + Start training & prediction + </button> + <button + type="button" + class="btn btn-secondary" + v-on:click="getJobStatusForAll(selected_project, selected_task)" + > + Update all + </button> + <!-- <button type="button" class="btn btn-danger" v-on:click="cancelAllJobs(selected_project, selected_task)">Cancel all</button> --> - </div> - </div> - <div class="modal-footer"> - <!-- <button type="button" class="btn btn-primary" data-dismiss="modal" + </div> + </div> + <div class="modal-footer"> + <!-- <button type="button" class="btn btn-primary" data-dismiss="modal" v-on:click="saveTask(selected_project, selected_task)">Save</button> --> - <button type="button" class="btn btn-secondary" data-dismiss="modal" v-on:click="closeTask()">Close</button> - </div> - </div> - </div> - </div> - - <div class="modal fade" id="atlas_ui_imports_modal" tabindex="-1" role="dialog" - aria-labelledby="exampleModalLabel" aria-hidden="true"> - <div class="modal-dialog modal-lg" role="document"> - <div class="modal-content"> - <!-- Header of dialogue --> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">Import annotations</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - - <!-- Body of dialogue --> - <div class="modal-body"> - <div class="form"> - <div class="form-row"> - <label class="col-form-label col-2">Source host</label> - <div class="col-4"> - <input v-model="annotation_import.src_host" class="form-control" type="text" - data-toggle="tooltip" data-placement="top" title="Host of the machine to retrieve annotations from. If you do not know what this is, leave the default value untouched."> - </div> - <div class="col-4"> - <input type="checkbox" v-model="annotation_import.project_import" id="atlas_ui_import_form_project_import" - data-toggle="tooltip" data-placement="top" title="Switch between import from user and import from predictions of specific task."> - <label for="atlas_ui_import_form_project_import">Import predictions</label> - </div> - </div> - <div class="form-row"> - <label class="col-form-label col-2" v-if="annotation_import.project_import">Project ID</label> - <label class="col-form-label col-2" v-else>Source user</label> - <div class="col-4"> - <input type="number" min="0" v-model="annotation_import.src_project" type="text" class="form-control" v-if="annotation_import.project_import" - data-toggle="tooltip" data-placement="top" title="ID of project to import predictions from."> - <input v-model="annotation_import.src_user" type="text" class="form-control" v-else - data-toggle="tooltip" data-placement="top" title="Microdraw user on source host to retrieve annotations from."> - </div> - <label class="col-form-label col-2">Destination user</label> - <div class="col-4"> - <input v-model="annotation_import.dst_user" type="text" class="form-control" - data-toggle="tooltip" data-placement="top" title="Microdraw user to import annotations to."> - </div> - </div> - - <div class="form-row"> - <label class="col-form-label col-2" v-if="annotation_import.project_import">Task ID</label> - <label class="col-form-label col-2" v-else>Source path</label> - - <div class="col-4"> - <input type="number" min="0" v-model="annotation_import.src_task" type="text" class="form-control" v-if="annotation_import.project_import" - data-toggle="tooltip" data-placement="top" title="ID of task to import predictions from."> - <input v-model="annotation_import.src_path" type="text" class="form-control" v-else - data-toggle="tooltip" data-placement="top" title="Microdraw path to import annotations from."> - </div> - <label class="col-form-label col-2">Destination path</label> - <div class="col-4"> - <input v-model="annotation_import.dst_path" type="text" class="form-control" - data-toggle="tooltip" data-placement="top" title="Microdraw path to import annotations to."> - </div> - <div class="col-1"> - </div> - </div> - <br> - <div class="form-row"> - <div class="btn-group float-right" role="group"> - <button type="button" class="btn btn-primary" v-on:click="getAnnotations" :disabled="annotation_import.loading_state" - data-toggle="tooltip" data-placement="top" title="Retrieve annotations from source host. Enter source host, user and path first."> - Retrieve source annotations - </button> - </div> - </div> - <br> - - <!-- List of annotations --> - <ol id="atlas_import_list" - class="list-group atlas_task_list border border-dark rounded bg-light"> - <li v-for="ann_item in annotation_import.annotation_items" - class="list-group-item list-group-item-action"> - <div class="form-row"> - <label class="col-1 col-form-label text-right">Section</label> - <div class="col-2"> - <input v-model="ann_item.section" class="form-control" type="text" readonly /> - </div> - <label class="col-2 col-form-label text-right">Annotations</label> - <div class="col-2"> - <input v-model="ann_item.annotations.Regions.length" class="form-control" type="text" readonly /> - </div> - <div class="col-2"> - </div> - <div class="col-3"> - <div class="btn-group" role="group"> - <button type="button" class="btn btn-primary" v-on:click="importAnnotation(ann_item, false)" :disabled="ann_item.loading_state" - data-toggle="tooltip" data-placement="top" title="Add annotations without overwriting existing annotations.">Add</button> - <button type="button" class="btn btn-danger" v-on:click="importAnnotation(ann_item, true)" :disabled="ann_item.loading_state" - data-toggle="tooltip" data-placement="top" title="Overwrite existing annotations for this section.">Overwrite</button> - </div> - </div> - </div> - </li> - </ol> - </div> - <br> - - <!-- Button group --> - <div class="btn-group" role="group"> - <button v-on:click="importAll(false)" type="button" class="btn btn-primary" - data-toggle="tooltip" data-placement="top" title="Add all annotations without overwriting existing annotations."> - Add all - </button> - <button v-on:click="importAll(true)" type="button" class="btn btn-danger" - data-toggle="tooltip" data-placement="top" title="Overwrite all existing annotations."> - Overwrite all - </button> - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> - </div> - </div> - </div> - </div> - - <!-- Discard changes dialogue --> - </div> - <!-- development version, includes helpful console warnings --> - <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> - <!-- <script src="https://unpkg.com/vue-contextmenu"></script> --> - <script src="lib/paper-full-0.9.25.min.js"></script> - <script src="lib/openseadragon/openseadragon.js"></script> - <script src="lib/openseadragon-viewerinputhook.min.js"></script> - <script src="lib/OpenSeadragonScalebar/openseadragon-scalebar.js"></script> - <script src="lib/openseadragonrgb/dist/openseadragonrgb.js"></script> - <script src="lib/jquery-1.11.0.min.js"></script> - <script src="lib/mylogin/login.js"></script> - <script src="neurolex-ontology.js"></script> - <script src="base.js"></script> - <script src="cross.js"></script> - <script src="configuration.js"></script> - <script src="microdraw.js"></script> - <script src="atlas_ui.js"></script> - </body> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + v-on:click="closeTask()" + > + Close + </button> + </div> + </div> + </div> + </div> + + <div + class="modal fade" + id="atlas_ui_imports_modal" + tabindex="-1" + role="dialog" + aria-labelledby="exampleModalLabel" + aria-hidden="true" + > + <div class="modal-dialog modal-lg" role="document"> + <div class="modal-content"> + <!-- Header of dialogue --> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel"> + Import annotations + </h5> + <button + type="button" + class="close" + data-dismiss="modal" + aria-label="Close" + > + <span aria-hidden="true">×</span> + </button> + </div> + + <!-- Body of dialogue --> + <div class="modal-body"> + <div class="form"> + <div class="form-row"> + <label class="col-form-label col-2">Source host</label> + <div class="col-4"> + <input + v-model="annotation_import.src_host" + class="form-control" + type="text" + data-toggle="tooltip" + data-placement="top" + title="Host of the machine to retrieve annotations from. If you do not know what this is, leave the default value untouched." + /> + </div> + <div class="col-4"> + <input + type="checkbox" + v-model="annotation_import.project_import" + id="atlas_ui_import_form_project_import" + data-toggle="tooltip" + data-placement="top" + title="Switch between import from user and import from predictions of specific task." + /> + <label for="atlas_ui_import_form_project_import" + >Import predictions</label + > + </div> + </div> + <div class="form-row"> + <label + class="col-form-label col-2" + v-if="annotation_import.project_import" + >Project ID</label + > + <label class="col-form-label col-2" v-else>Source user</label> + <div class="col-4"> + <input + type="number" + min="0" + v-model="annotation_import.src_project" + type="text" + class="form-control" + v-if="annotation_import.project_import" + data-toggle="tooltip" + data-placement="top" + title="ID of project to import predictions from." + /> + <input + v-model="annotation_import.src_user" + type="text" + class="form-control" + v-else + data-toggle="tooltip" + data-placement="top" + title="Microdraw user on source host to retrieve annotations from." + /> + </div> + <label class="col-form-label col-2">Destination user</label> + <div class="col-4"> + <input + v-model="annotation_import.dst_user" + type="text" + class="form-control" + data-toggle="tooltip" + data-placement="top" + title="Microdraw user to import annotations to." + /> + </div> + </div> + + <div class="form-row"> + <label + class="col-form-label col-2" + v-if="annotation_import.project_import" + >Task ID</label + > + <label class="col-form-label col-2" v-else>Source path</label> + + <div class="col-4"> + <input + type="number" + min="0" + v-model="annotation_import.src_task" + type="text" + class="form-control" + v-if="annotation_import.project_import" + data-toggle="tooltip" + data-placement="top" + title="ID of task to import predictions from." + /> + <input + v-model="annotation_import.src_path" + type="text" + class="form-control" + v-else + data-toggle="tooltip" + data-placement="top" + title="Microdraw path to import annotations from." + /> + </div> + <label class="col-form-label col-2">Destination path</label> + <div class="col-4"> + <input + v-model="annotation_import.dst_path" + type="text" + class="form-control" + data-toggle="tooltip" + data-placement="top" + title="Microdraw path to import annotations to." + /> + </div> + <div class="col-1"></div> + </div> + <br /> + <div class="form-row"> + <div class="btn-group float-right" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="getAnnotations" + :disabled="annotation_import.loading_state" + data-toggle="tooltip" + data-placement="top" + title="Retrieve annotations from source host. Enter source host, user and path first." + > + Retrieve source annotations + </button> + </div> + </div> + <br /> + + <!-- List of annotations --> + <ol + id="atlas_import_list" + class="list-group atlas_task_list border border-dark rounded bg-light" + > + <li + v-for="ann_item in annotation_import.annotation_items" + class="list-group-item list-group-item-action" + > + <div class="form-row"> + <label class="col-1 col-form-label text-right" + >Section</label + > + <div class="col-2"> + <input + v-model="ann_item.section" + class="form-control" + type="text" + readonly + /> + </div> + <label class="col-2 col-form-label text-right" + >Annotations</label + > + <div class="col-2"> + <input + v-model="ann_item.annotations.Regions.length" + class="form-control" + type="text" + readonly + /> + </div> + <div class="col-2"></div> + <div class="col-3"> + <div class="btn-group" role="group"> + <button + type="button" + class="btn btn-primary" + v-on:click="importAnnotation(ann_item, false)" + :disabled="ann_item.loading_state" + data-toggle="tooltip" + data-placement="top" + title="Add annotations without overwriting existing annotations." + > + Add + </button> + <button + type="button" + class="btn btn-danger" + v-on:click="importAnnotation(ann_item, true)" + :disabled="ann_item.loading_state" + data-toggle="tooltip" + data-placement="top" + title="Overwrite existing annotations for this section." + > + Overwrite + </button> + </div> + </div> + </div> + </li> + </ol> + </div> + <br /> + + <!-- Button group --> + <div class="btn-group" role="group"> + <button + v-on:click="importAll(false)" + type="button" + class="btn btn-primary" + data-toggle="tooltip" + data-placement="top" + title="Add all annotations without overwriting existing annotations." + > + Add all + </button> + <button + v-on:click="importAll(true)" + type="button" + class="btn btn-danger" + data-toggle="tooltip" + data-placement="top" + title="Overwrite all existing annotations." + > + Overwrite all + </button> + </div> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-secondary" + data-dismiss="modal" + > + Close + </button> + </div> + </div> + </div> + </div> + <!-- Discard changes dialogue --> + </div> + <!-- development version, includes helpful console warnings --> + <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> + <!-- <script src="https://unpkg.com/vue-contextmenu"></script> --> + <script src="lib/paper-full-0.9.25.min.js"></script> + <script src="lib/openseadragon/openseadragon.js"></script> + <script src="lib/openseadragon-viewerinputhook.min.js"></script> + <script src="lib/OpenSeadragonScalebar/openseadragon-scalebar.js"></script> + <script src="lib/openseadragonrgb/dist/openseadragonrgb.js"></script> + <script src="lib/jquery-1.11.0.min.js"></script> + <script src="lib/mylogin/login.js"></script> + <script src="neurolex-ontology.js"></script> + <script src="base.js"></script> + <script src="cross.js"></script> + <script src="configuration.js"></script> + <script src="microdraw.js"></script> + <script src="atlas_ui.js"></script> + </body> </html> -- GitLab