async def update_model( model_id: UUID, model_patch: ScientificModelPatch, token: HTTPAuthorizationCredentials = Depends(auth), ): # if payload contains a project_id, check permissions for that id if model_patch.project_id and not (await is_collab_member( model_patch.project_id, token.credentials) or await is_admin(token.credentials)): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail= f"This account is not a member of Collab #{model_patch.project_id}", ) # retrieve stored model model_project = ModelProject.from_uuid(str(model_id), kg_client, api="nexus") stored_model = ScientificModel.from_kg_object(model_project, kg_client) # if retrieved project_id is different to payload id, check permissions for that id if stored_model.project_id != model_patch.project_id and not ( await is_collab_member(stored_model.project_id, token.credentials) or await is_admin(token.credentials)): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail= f"Access to this model is restricted to members of Collab #{stored_model.project_id}", ) # if alias changed, check uniqueness of new alias if (model_patch.alias and model_patch.alias != stored_model.alias and model_alias_exists(model_patch.alias, kg_client)): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail= f"Another model with alias '{model_patch.alias}' already exists.", ) # todo: if model id provided in payload, check it matches the model_id parameter # todo: if model uri provided in payload, check it matches the id # here we are updating the pydantic model `stored_model`, then recreating the kg objects # from this. It might be more efficient to directly update `model_project`. # todo: profile both possible implementations update_data = model_patch.dict(exclude_unset=True) for field, value in update_data.items(): if field in ("author", "owner"): update_data[field] = [Person(**p) for p in update_data[field]] updated_model = stored_model.copy(update=update_data) kg_objects = updated_model.to_kg_objects() for obj in kg_objects: obj.save(kg_client) model_project = kg_objects[-1] assert isinstance(model_project, ModelProject) return ScientificModel.from_kg_object(model_project, kg_client)
async def _get_model_by_id_or_alias(model_id, token): try: model_id = UUID(model_id) model_project = ModelProject.from_uuid(str(model_id), kg_client, api="nexus") except ValueError: model_alias = str(model_id) model_project = ModelProject.from_alias(model_alias, kg_client, api="nexus") if not model_project: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Model with ID or alias '{model_id}' not found.", ) # todo: fairgraph should accept UUID object as well as str await _check_model_access(model_project, token) return model_project
def _are_model_instance_version_unique_kg(instance_json, kg_client): """ Check if versions of model instance are unique :param instance_json: datas of instance :type instance_json: dict :returns: response :rtype: boolean """ new_version_name = instance_json['version'] model_project = ModelProject.from_uuid(instance_json['model_id'], kg_client) if model_project.instances: all_instances_versions_name = [inst.resolve(kg_client).version for inst in as_list(model_project.instances)] if new_version_name in all_instances_versions_name: return False return True
async def delete_model_instance( model_id: UUID, model_instance_id: UUID, token: HTTPAuthorizationCredentials = Depends(auth)): # todo: handle non-existent UUID model_project = ModelProject.from_uuid(str(model_id), kg_client, api="nexus") if not (await is_collab_member(model_project.collab_id, token.credentials) or await is_admin(token.credentials)): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail= f"Access to this model is restricted to members of Collab #{model_project.collab_id}", ) await _delete_model_instance(model_instance_id, model_project)
async def delete_model(model_id: UUID, token: HTTPAuthorizationCredentials = Depends(auth)): # todo: handle non-existent UUID model_project = ModelProject.from_uuid(str(model_id), kg_client, api="nexus") if not (await is_collab_member(model_project.collab_id, token.credentials) or await is_admin(token.credentials)): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail= f"Access to this model is restricted to members of Collab #{model_project.collab_id}", ) model_project.delete(kg_client) for model_instance in as_list(model_project.instances): # todo: we should possibly also delete emodels, modelscripts, morphologies, # but need to check they're not shared with other instances model_instance.delete(kg_client)
def save(self): # todo: Create/update EModel, MEModel and Morphology where model_scope is "single cell" if self.obj is None: # create model_project = ModelProject.from_uuid(self.data["model_id"], self.client, api="nexus") script = ModelScript(name="ModelScript for {} @ {}".format( model_project.name, self.data["version"]), code_format=self.data.get("code_format"), code_location=self.data["source"], license=self.data.get("license")) script.save(self.client) if model_project.model_of and model_project.model_of.label == "single cell" and "morphology" in self.data: e_model = EModel(name="EModel for {} @ {}".format( model_project.name, self.data["version"]), brain_region=model_project.brain_region, species=model_project.species, model_of=None, main_script=None, release=None) e_model.save(self.client) morph = Morphology(name="Morphology for {} @ {}".format( model_project.name, self.data["version"]), cell_type=model_project.celltype, morphology_file=self.data["morphology"]) morph.save(self.client) minst = MEModel(name="ModelInstance for {} @ {}".format( model_project.name, self.data["version"]), description=self.data.get("description", ""), brain_region=model_project.brain_region, species=model_project.species, model_of=None, main_script=script, e_model=e_model, morphology=morph, version=self.data["version"], parameters=self.data.get("parameters"), timestamp=datetime.now(), release=None) else: minst = ModelInstance(name="ModelInstance for {} @ {}".format( model_project.name, self.data["version"]), description=self.data.get( "description", ""), brain_region=model_project.brain_region, species=model_project.species, model_of=None, main_script=script, version=self.data["version"], parameters=self.data.get("parameters"), timestamp=datetime.now(), release=None) minst.save(self.client) self.obj = minst if model_project.instances: if not isinstance(model_project.instances, list): model_project.instances = [model_project.instances] model_project.instances.append(minst) else: model_project.instances = [minst] model_project.save(self.client) else: # update instance_changed = False script_changed = False morphology_changed = False def resolve_obj(obj): if isinstance(obj, KGProxy): return obj.resolve(self.client, api="nexus") else: return obj if "name" in self.data: self.obj.name = self.data["name"] # todo: also update e_model and morphology instance_changed = True if "description" in self.data: self.obj.description = self.data.get("description", "") instance_changed = True if "version" in self.data: self.obj.version = self.data["version"] instance_changed = True if "parameters" in self.data: self.obj.parameters = self.data.get("parameters") instance_changed = True if "code_format" in self.data: self.obj.main_script = resolve_obj(self.obj.main_script) self.obj.main_script.code_format = self.data.get("code_format") script_changed = True if "source" in self.data: self.obj.main_script = resolve_obj(self.obj.main_script) self.obj.main_script.code_location = self.data["source"] script_changed = True if "license" in self.data: self.obj.main_script = resolve_obj(self.obj.main_script) self.obj.main_script.license = self.data.get("license") script_changed = True if "morphology" in self.data and self.data[ "morphology"] is not None: self.obj.morphology = resolve_obj(self.obj.morphology) self.obj.morphology.morphology_file = self.data["morphology"] morphology_changed = True logger.debug("Morphology changed: {}".format(morphology_changed)) logger.debug("Script changed: {}".format(script_changed)) logger.debug("Instance changed: {}".format(instance_changed)) if morphology_changed: self.obj.morphology.save(self.client) if script_changed: self.obj.main_script.save(self.client) if instance_changed: self.obj.save(self.client) return self.obj