"""_____________________________________________________________________

:PROJECT: LARAsuite

*lara_django_samples high_level_interface *

:details: lara_django_samples high_level_interface.
         - generated with 'lara-django-dev generate_high_level_interface lara_django_samples
:authors: mark doerr <mark.doerr@uni-greifswald.de>

.. note:: -
.. todo:: -
________________________________________________________________________
"""

import json
import asyncio
import logging
import grpc
from typing import Union

from google.protobuf.struct_pb2 import Struct
from google.protobuf.json_format import MessageToDict

import lara_django_base_grpc.v1.lara_django_base_pb2 as lara_django_base_pb2
import lara_django_base_grpc.v1.lara_django_base_pb2_grpc as lara_django_base_pb2_grpc

import lara_django_samples_grpc.v1.lara_django_samples_pb2 as lara_django_samples_pb2
import lara_django_samples_grpc.v1.lara_django_samples_pb2_grpc as lara_django_samples_pb2_grpc

import  lara_django_base_grpc.lara_django_base_data_model as base_dm
import  lara_django_base_grpc.lara_django_base_interface as base_if
from  lara_django_base_grpc.abstract_interface import LARAInterface, GRPCChannelSingleton, import_model_instance_from_file, export_model_instance_to_file, upload_file, download_file, update_file, upload_image, update_image, download_image, id_cache

import  lara_django_samples_grpc.lara_django_samples_data_model as samples_dm

logger = logging.getLogger(__name__)



#_________________  ExtraData  ______________________


class ExtraDataInterface:
    def __init__(self, channel: grpc.Channel = None, 
                       default_namespace_uri : str = None,
                       default_namespace_id : str = None,
                       namespace_if = None ) -> None:
        self._default_namespace_uri = default_namespace_uri
        self._default_namespace_id = default_namespace_id

        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.namespace_if = namespace_if if namespace_if is not None else base_if.NamespaceInterface(channel)

        # self.extradata_class_client = (
        #     lara_django_samples_pb2_grpc.ExtraDataclassByIRIControllerStub(channel)
        # )

        self.extradata_client = lara_django_samples_pb2_grpc.ExtraDataControllerStub(channel)
        
        self.item_id = None
        self.update_item_id = "extradata_id"
        self.stub = self.extradata_client

        self.file_download_info = lara_django_samples_pb2.FileDownloadInfo
        self.file_upload_chunk = lara_django_samples_pb2.FileUploadChunk


        # self.extradata_full_client = lara_django_samples_pb2_grpc.ExtraDataFullControllerStub(
        #     channel
        # )

    @property
    def default_namespace_uri(self):
        return self._default_namespace_uri

    @property
    def default_namespace_id(self):
        return self._default_namespace_id

    @classmethod
    async def build_if(cls, channel: grpc.Channel = None, default_namespace_uri: str = None):
    
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel
    
        namespace_if = base_if.NamespaceInterface(channel)
        try:
            default_namespace_id = await namespace_if.retrieve_id(namespace_uri=default_namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
        
        return cls(channel=channel, default_namespace_uri=default_namespace_uri,  
                   default_namespace_id=default_namespace_id, namespace_if=namespace_if)
    
    async def set_default_namespace_uri(self, namespace_uri: str = None):
        if namespace_uri is not None:
            self._default_namespace_uri = namespace_uri.strip()
        try:
            self._default_namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
    
    @import_model_instance_from_file()
    @upload_file  # needed for file upload
    async def create(self,  extradata:  list[samples_dm.ExtraData] = None,  
                            ids_only: bool = False,
                            namespace_uri: str = None) -> Union[list[str], list[samples_dm.ExtraData]]:
        """ create a list of extradata instances,
               returns a list of extradata data model objects or ids_only

        :param extradata: list of extradata data model objects
        :param ids_only: return only the ids of the created instances
        :return: list of extradata data model objects or ids_only
        """
        extradataclass_id = None

        namespace_id = None
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            namespace_id = self._default_namespace_id
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        if namespace_id is None:
            try:
                namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
            except Exception as e:
                logger.error(f"Error retrieving namespace_id: {e}")

        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if extradata_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.extradata_class_client.Retrieve(
        #         lara_django_samples_pb2.ExtraDataclassRetrieveRequest(iri=extradata_class_iri)
        #     )
        #     extradataclass_id = res.extradataclass_id
        # except Exception as e:
        #     logger.error(f"Error retrieving extradataclass_id: {e}")
        for extradata in extradata:
            if extradata.namespace == "" or extradata.namespace == "default":
                    extradata.namespace = namespace_id
            # extradata.extradata_class = extradataclass_id # uncomment if a model class is used

        try:
            create_coroutines = ( self.extradata_client.Create(lara_django_samples_pb2.ExtraDataRequest(**extradata.model_dump())) for extradata in extradata )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [ MessageToDict(item, preserving_proto_field_name=True).get("extradata_id") for item in res ]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating extradata: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        namespace_uri: str = None,
        extradata_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.ExtraData]:
        """ retrieve a list of extradata instances by extradata_id, name or filter_dict,
               returns a list of extradata data model objects,
               if raw is True, the raw protobuf objects are returned.
               If namespace_uri is "default", the default namespace_uri is used.
        """
        if namespace_uri == "default":
            if self._default_namespace_uri is not None:
                namespace_uri = self._default_namespace_uri
            else:
                raise ValueError("No default namespace URI is given.")
        
        results_list = []
        if  extradata_id is not None:
            try:
                res =  await self.extradata_client.Retrieve(
                    lara_django_samples_pb2.ExtraDataRetrieveRequest(extradata_id=extradata_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.ExtraData(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving extradata_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        if namespace_uri is not None:
            filter_dict["namespace__uri"] = namespace_uri
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.extradata_client.List(
                    lara_django_samples_pb2.ExtraDataListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # :TODO: a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.ExtraData(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving extradata name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          namespace_uri : str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the extradata_id for a given name,
               name_display or iri
        """

        results_list = await self.retrieve(name=name, 
                        namespace_uri=namespace_uri, filter_dict=filter_dict, raw=True)

        if len(results_list) == 1 :
            return results_list[0].extradata_id
        else:
            raise ValueError(f"Error retrieving extradata_id - no or too many results found.")
    
    @download_file  # needed for file download
    async def get_by_id(self, extradata_id: str = None, id_only: bool = False) -> samples_dm.ExtraData:
        """ retrieve a extradata data model by extradata_id """
        try:
            res = await self.extradata_client.Retrieve(
                lara_django_samples_pb2.ExtraDataRetrieveRequest(extradata_id=extradata_id)
            )
            item = samples_dm.ExtraData(**MessageToDict(res, preserving_proto_field_name=True))
            self.item_id = item.extradata_id
            return item
        except Exception as e:
            logger.error(f"Error retrieving extradata_id: {e}")
    
    @download_file  # needed for file download
    async def get_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                id_only: bool = False) -> samples_dm.ExtraData | str:
        """ retrieve a extradata data model by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            res = await self.extradata_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.ExtraDataRetrieveByNameNSRequest(name=name, namespace_uri=namespace_uri)
            )
            item = MessageToDict(res, preserving_proto_field_name=True)
            self.item_id = item["extradata_id"]
            if id_only:
                return self.item_id
            else:
                return samples_dm.ExtraData(**item)
        except Exception as e:
            logger.error(f"Error retrieving extradata name: {e}")
   
    
    @download_file  # needed for file download
    async def get_file(self, extradata_id : str = None, id_only : bool = True ) -> bytes | None:
        """ retrieve a file by data_id """
        self.item_id = extradata_id
        
    
    async def get_file_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                filename_target : str = None,
                                as_byte_str : bool = None ) -> bytes | None:
        """ retrieve a file by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            # logger.warning(f"no namespace_uri provided, using default namespace: {namespace_uri}")
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            extradata_id = await self.get_by_name(name=name, namespace_uri=namespace_uri, id_only=True)
            return await self.get_file(extradata_id=extradata_id, filename_target=filename_target, as_byte_str=as_byte_str, get_file=True)
        except Exception as e:
            logger.error(f"Error retrieving extradata name: {e}")
        
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all extradatas """
        if filter_dict is None:
            res = await self.extradata_client.List(lara_django_samples_pb2.ExtraDataListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.extradata_client.List(
                lara_django_samples_pb2.ExtraDataListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.extradata_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all extradatas """
        if self.extradata_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.extradata_full_client.List(
                lara_django_samples_pb2.ExtraDataFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.extradata_full_client.List(
                lara_django_samples_pb2.ExtraDataFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    @update_file  # needed for file update
    async def update(self, extradata : samples_dm.ExtraData = None) -> None:
        """ update a extradata model instance """
        try:
            res = await self.extradata_client.Update(
                lara_django_samples_pb2.ExtraDataRequest(**extradata.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating extradata_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a extradata by extradata_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.extradata_client.Destroy(lara_django_samples_pb2.ExtraDataDestroyRequest(extradata_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting extradata_id: {e}")

#_________________  SampleLayout  ______________________


class SampleLayoutInterface:
    def __init__(self, channel: grpc.Channel = None, 
                       default_namespace_uri : str = None,
                       default_namespace_id : str = None,
                       namespace_if = None ) -> None:
        self._default_namespace_uri = default_namespace_uri
        self._default_namespace_id = default_namespace_id

        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.namespace_if = namespace_if if namespace_if is not None else base_if.NamespaceInterface(channel)

        # self.samplelayout_class_client = (
        #     lara_django_samples_pb2_grpc.SampleLayoutclassByIRIControllerStub(channel)
        # )

        self.samplelayout_client = lara_django_samples_pb2_grpc.SampleLayoutControllerStub(channel)
        
        self.item_id = None
        self.update_item_id = "samplelayout_id"
        self.stub = self.samplelayout_client

        self.file_download_info = lara_django_samples_pb2.FileDownloadInfo
        self.file_upload_chunk = lara_django_samples_pb2.FileUploadChunk


        # self.samplelayout_full_client = lara_django_samples_pb2_grpc.SampleLayoutFullControllerStub(
        #     channel
        # )

    @property
    def default_namespace_uri(self):
        return self._default_namespace_uri

    @property
    def default_namespace_id(self):
        return self._default_namespace_id

    @classmethod
    async def build_if(cls, channel: grpc.Channel = None, default_namespace_uri: str = None):
    
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel
    
        namespace_if = base_if.NamespaceInterface(channel)
        try:
            default_namespace_id = await namespace_if.retrieve_id(namespace_uri=default_namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
        
        return cls(channel=channel, default_namespace_uri=default_namespace_uri,  
                   default_namespace_id=default_namespace_id, namespace_if=namespace_if)
    
    async def set_default_namespace_uri(self, namespace_uri: str = None):
        if namespace_uri is not None:
            self._default_namespace_uri = namespace_uri.strip()
        try:
            self._default_namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
    
    @import_model_instance_from_file()
    @upload_file  # needed for file upload
    async def create(self,  samplelayouts:  list[samples_dm.SampleLayout] = None,  
                            ids_only: bool = False,
                            namespace_uri: str = None) -> Union[list[str], list[samples_dm.SampleLayout]]:
        """ create a list of samplelayout instances,
               returns a list of samplelayout data model objects or ids_only

        :param samplelayouts: list of samplelayout data model objects
        :param ids_only: return only the ids of the created instances
        :return: list of samplelayout data model objects or ids_only
        """
        samplelayoutclass_id = None

        namespace_id = None
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            namespace_id = self._default_namespace_id
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        if namespace_id is None:
            try:
                namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
            except Exception as e:
                logger.error(f"Error retrieving namespace_id: {e}")

        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if samplelayout_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.samplelayout_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleLayoutclassRetrieveRequest(iri=samplelayout_class_iri)
        #     )
        #     samplelayoutclass_id = res.samplelayoutclass_id
        # except Exception as e:
        #     logger.error(f"Error retrieving samplelayoutclass_id: {e}")
        for samplelayout in samplelayouts:
            if samplelayout.namespace == "" or samplelayout.namespace == "default":
                    samplelayout.namespace = namespace_id

            
            # samplelayout.samplelayout_class = samplelayoutclass_id # uncomment if a model class is used

        try:
            create_coroutines = ( self.samplelayout_client.Create(lara_django_samples_pb2.SampleLayoutRequest(**samplelayout.model_dump())) for samplelayout in samplelayouts )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [ MessageToDict(item, preserving_proto_field_name=True).get("samplelayout_id") for item in res ]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating samplelayout: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        namespace_uri: str = None,
        samplelayout_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleLayout]:
        """ retrieve a list of samplelayout instances by samplelayout_id, name or filter_dict,
               returns a list of samplelayout data model objects,
               if raw is True, the raw protobuf objects are returned.
               If namespace_uri is "default", the default namespace_uri is used.
        """
        if namespace_uri == "default":
            if self._default_namespace_uri is not None:
                namespace_uri = self._default_namespace_uri
            else:
                raise ValueError("No default namespace URI is given.")
        
        results_list = []
        if  samplelayout_id is not None:
            try:
                res =  await self.samplelayout_client.Retrieve(
                    lara_django_samples_pb2.SampleLayoutRetrieveRequest(samplelayout_id=samplelayout_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleLayout(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving samplelayout_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        if namespace_uri is not None:
            filter_dict["namespace__uri"] = namespace_uri
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.samplelayout_client.List(
                    lara_django_samples_pb2.SampleLayoutListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # :TODO: a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.SampleLayout(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving samplelayout name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          namespace_uri : str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the samplelayout_id for a given name,
               name_display or iri
        """

        results_list = await self.retrieve(name=name, 
                        namespace_uri=namespace_uri, filter_dict=filter_dict, raw=True)

        if len(results_list) == 1 :
            return results_list[0].samplelayout_id
        else:
            raise ValueError(f"Error retrieving samplelayout_id - no or too many results found.")
    
    @download_file  # needed for file download
    async def get_by_id(self, samplelayout_id: str = None, id_only: bool = False) -> samples_dm.SampleLayout:
        """ retrieve a samplelayout data model by samplelayout_id """
        try:
            res = await self.samplelayout_client.Retrieve(
                lara_django_samples_pb2.SampleLayoutRetrieveRequest(samplelayout_id=samplelayout_id)
            )
            item = samples_dm.SampleLayout(**MessageToDict(res, preserving_proto_field_name=True))
            self.item_id = item.samplelayout_id
            return item
        except Exception as e:
            logger.error(f"Error retrieving samplelayout_id: {e}")
    
    @download_file  # needed for file download
    async def get_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                id_only: bool = False) -> samples_dm.SampleLayout | str:
        """ retrieve a samplelayout data model by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            res = await self.samplelayout_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.SampleLayoutRetrieveByNameNSRequest(name=name, namespace_uri=namespace_uri)
            )
            item = MessageToDict(res, preserving_proto_field_name=True)
            self.item_id = item["samplelayout_id"]
            if id_only:
                return self.item_id
            else:
                return samples_dm.SampleLayout(**item)
        except Exception as e:
            logger.error(f"Error retrieving samplelayout name: {e}")
   
    
    @download_file  # needed for file download
    async def get_file(self, samplelayout_id : str = None, id_only : bool = True ) -> bytes | None:
        """ retrieve a file by data_id """
        self.item_id = samplelayout_id
        
    
    async def get_file_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                filename_target : str = None,
                                as_byte_str : bool = None ) -> bytes | None:
        """ retrieve a file by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            # logger.warning(f"no namespace_uri provided, using default namespace: {namespace_uri}")
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            samplelayout_id = await self.get_by_name(name=name, namespace_uri=namespace_uri, id_only=True)
            return await self.get_file(samplelayout_id=samplelayout_id, filename_target=filename_target, as_byte_str=as_byte_str, get_file=True)
        except Exception as e:
            logger.error(f"Error retrieving samplelayout name: {e}")
        
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all samplelayouts """
        if filter_dict is None:
            res = await self.samplelayout_client.List(lara_django_samples_pb2.SampleLayoutListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.samplelayout_client.List(
                lara_django_samples_pb2.SampleLayoutListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.samplelayout_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all samplelayouts """
        if self.samplelayout_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.samplelayout_full_client.List(
                lara_django_samples_pb2.SampleLayoutFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.samplelayout_full_client.List(
                lara_django_samples_pb2.SampleLayoutFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    @update_file  # needed for file update
    async def update(self, samplelayout : samples_dm.SampleLayout = None) -> None:
        """ update a samplelayout model instance """
        try:
            res = await self.samplelayout_client.Update(
                lara_django_samples_pb2.SampleLayoutRequest(**samplelayout.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating samplelayout_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a samplelayout by samplelayout_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.samplelayout_client.Destroy(lara_django_samples_pb2.SampleLayoutDestroyRequest(samplelayout_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting samplelayout_id: {e}")

#_________________  SampleClass  ______________________


class SampleClassInterface:
    def __init__(self, channel: grpc.Channel = None) -> None:
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.sampleclass_client = lara_django_samples_pb2_grpc.SampleClassControllerStub(channel)
        
    
    @import_model_instance_from_file()
    async def create(self,  sampleclasses:  list[samples_dm.SampleClass] = None,  ids_only: bool = False) -> Union[list[str], list[samples_dm.SampleClass]]:
        try:
            create_coroutines = ( self.sampleclass_client.Create(lara_django_samples_pb2.SampleClassRequest(**sampleclass.model_dump())) for sampleclass in sampleclasses )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.sampleclass_id for item in res]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating sampleclass: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        sampleclass_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleClass]:
        """ retrieve a list of sampleclass instances by sampleclass_id, name or filter_dict,
               returns a list of sampleclass data model objects,
               if raw is True, the raw protobuf objects are returned
        """
        results_list = []
        if  sampleclass_id is not None:
            try:
                res =  await self.sampleclass_client.Retrieve(
                    lara_django_samples_pb2.SampleClassRetrieveRequest(sampleclass_id=sampleclass_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleClass(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving sampleclass_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.sampleclass_client.List(
                    lara_django_samples_pb2.SampleClassListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.SampleClass(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving sampleclass name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the sampleclass_id for a given name,
               (name_display) or iri
        """
        
        results_list = await self.retrieve(name=name, filter_dict=filter_dict, raw=True)
        if len(results_list) == 1 :
            return results_list[0].sampleclass_id
        else:
            raise ValueError(f"Error retrieving sampleclass_id - no or too many results found.")
    
    async def get_by_id(self, sampleclass_id: str = None) -> samples_dm.SampleClass:
        """ retrieve a sampleclass data model by sampleclass_id """
        try:
            res = await self.sampleclass_client.Retrieve(
                lara_django_samples_pb2.SampleClassRetrieveRequest(sampleclass_id=sampleclass_id)
            )
            return samples_dm.SampleClass(**MessageToDict(res, preserving_proto_field_name=True))
        except Exception as e:
            logger.error(f"Error retrieving sampleclass_id: {e}")
    
    async def get_by_name(self, name: str = None, id_only: bool = False) -> samples_dm.SampleClass | str:
        """ retrieve a sampleclass data model by name """
        try:
            res = await self.sampleclass_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.SampleClassRetrieveByNameNSRequest(name=name, namespace_uri="")
            )
            if id_only:
                return MessageToDict(res, preserving_proto_field_name=True)["sampleclass_id"]
            else:
                return samples_dm.SampleClass(**MessageToDict(res, preserving_proto_field_name=True))
        except Exception as e:
            logger.error(f"Error retrieving sampleclass name: {e}")
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all sampleclasss """
        if filter_dict is None:
            res = await self.sampleclass_client.List(lara_django_samples_pb2.SampleClassListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.sampleclass_client.List(
                lara_django_samples_pb2.SampleClassListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.sampleclass_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all sampleclasss """
        if self.sampleclass_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.sampleclass_full_client.List(
                lara_django_samples_pb2.SampleClassFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.sampleclass_full_client.List(
                lara_django_samples_pb2.SampleClassFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, sampleclass : samples_dm.SampleClass = None) -> None:
        """ update a sampleclass model instance """
        try:
            res = await self.sampleclass_client.Update(
                lara_django_samples_pb2.SampleClassRequest(**sampleclass.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating sampleclass_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a sampleclass by sampleclass_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.sampleclass_client.Destroy(lara_django_samples_pb2.SampleClassDestroyRequest(sampleclass_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting sampleclass_id: {e}")

#_________________  Sample  ______________________


class SampleInterface:
    def __init__(self, channel: grpc.Channel = None, 
                       default_namespace_uri : str = None,
                       default_namespace_id : str = None,
                       namespace_if = None ) -> None:
        self._default_namespace_uri = default_namespace_uri
        self._default_namespace_id = default_namespace_id

        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.namespace_if = namespace_if if namespace_if is not None else base_if.NamespaceInterface(channel)

        # self.sample_class_client = (
        #     lara_django_samples_pb2_grpc.SampleclassByIRIControllerStub(channel)
        # )

        self.sample_client = lara_django_samples_pb2_grpc.SampleControllerStub(channel)
        

        # self.sample_full_client = lara_django_samples_pb2_grpc.SampleFullControllerStub(
        #     channel
        # )

    @property
    def default_namespace_uri(self):
        return self._default_namespace_uri

    @property
    def default_namespace_id(self):
        return self._default_namespace_id

    @classmethod
    async def build_if(cls, channel: grpc.Channel = None, default_namespace_uri: str = None):
    
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel
    
        namespace_if = base_if.NamespaceInterface(channel)
        try:
            default_namespace_id = await namespace_if.retrieve_id(namespace_uri=default_namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
        
        return cls(channel=channel, default_namespace_uri=default_namespace_uri,  
                   default_namespace_id=default_namespace_id, namespace_if=namespace_if)
    
    async def set_default_namespace_uri(self, namespace_uri: str = None):
        if namespace_uri is not None:
            self._default_namespace_uri = namespace_uri.strip()
        try:
            self._default_namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
    
    @import_model_instance_from_file()
    async def create(self,  samples:  list[samples_dm.Sample] = None,  
                            ids_only: bool = False,
                            namespace_uri: str = None) -> Union[list[str], list[samples_dm.Sample]]:
        """ create a list of sample instances,
               returns a list of sample data model objects or ids_only

        :param samples: list of sample data model objects
        :param ids_only: return only the ids of the created instances
        :return: list of sample data model objects or ids_only
        """
        sampleclass_id = None

        namespace_id = None
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            namespace_id = self._default_namespace_id
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        if namespace_id is None:
            try:
                namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
            except Exception as e:
                logger.error(f"Error retrieving namespace_id: {e}")

        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if sample_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.sample_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleclassRetrieveRequest(iri=sample_class_iri)
        #     )
        #     sampleclass_id = res.sampleclass_id
        # except Exception as e:
        #     logger.error(f"Error retrieving sampleclass_id: {e}")
        for sample in samples:
            if sample.namespace == "" or sample.namespace == "default":
                    sample.namespace = namespace_id
            # sample.sample_class = sampleclass_id # uncomment if a model class is used

        try:
            create_coroutines = ( self.sample_client.Create(lara_django_samples_pb2.SampleRequest(**sample.model_dump())) for sample in samples )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [ MessageToDict(item, preserving_proto_field_name=True).get("sample_id") for item in res ]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating sample: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        namespace_uri: str = None,
        sample_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.Sample]:
        """ retrieve a list of sample instances by sample_id, name or filter_dict,
               returns a list of sample data model objects,
               if raw is True, the raw protobuf objects are returned.
               If namespace_uri is "default", the default namespace_uri is used.
        """
        if namespace_uri == "default":
            if self._default_namespace_uri is not None:
                namespace_uri = self._default_namespace_uri
            else:
                raise ValueError("No default namespace URI is given.")
        
        results_list = []
        if  sample_id is not None:
            try:
                res =  await self.sample_client.Retrieve(
                    lara_django_samples_pb2.SampleRetrieveRequest(sample_id=sample_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.Sample(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving sample_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        if namespace_uri is not None:
            filter_dict["namespace__uri"] = namespace_uri
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.sample_client.List(
                    lara_django_samples_pb2.SampleListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # :TODO: a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.Sample(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving sample name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          namespace_uri : str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the sample_id for a given name,
               name_display or iri
        """

        results_list = await self.retrieve(name=name, 
                        namespace_uri=namespace_uri, filter_dict=filter_dict, raw=True)

        if len(results_list) == 1 :
            return results_list[0].sample_id
        else:
            raise ValueError(f"Error retrieving sample_id - no or too many results found.")
    
    async def get_by_id(self, sample_id: str = None, id_only: bool = False) -> samples_dm.Sample:
        """ retrieve a sample data model by sample_id """
        try:
            res = await self.sample_client.Retrieve(
                lara_django_samples_pb2.SampleRetrieveRequest(sample_id=sample_id)
            )
            item = samples_dm.Sample(**MessageToDict(res, preserving_proto_field_name=True))
            self.item_id = item.sample_id
            return item
        except Exception as e:
            logger.error(f"Error retrieving sample_id: {e}")
    
    async def get_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                id_only: bool = False) -> samples_dm.Sample | str:
        """ retrieve a sample data model by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            res = await self.sample_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.SampleRetrieveByNameNSRequest(name=name, namespace_uri=namespace_uri)
            )
            item = MessageToDict(res, preserving_proto_field_name=True)
            self.item_id = item["sample_id"]
            if id_only:
                return self.item_id
            else:
                return samples_dm.Sample(**item)
        except Exception as e:
            logger.error(f"Error retrieving sample name: {e}")
   
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all samples """
        if filter_dict is None:
            res = await self.sample_client.List(lara_django_samples_pb2.SampleListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.sample_client.List(
                lara_django_samples_pb2.SampleListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.sample_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all samples """
        if self.sample_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.sample_full_client.List(
                lara_django_samples_pb2.SampleFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.sample_full_client.List(
                lara_django_samples_pb2.SampleFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, sample : samples_dm.Sample = None) -> None:
        """ update a sample model instance """
        try:
            res = await self.sample_client.Update(
                lara_django_samples_pb2.SampleRequest(**sample.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating sample_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a sample by sample_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.sample_client.Destroy(lara_django_samples_pb2.SampleDestroyRequest(sample_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting sample_id: {e}")

#_________________  SamplesSubset  ______________________


class SamplesSubsetInterface:
    def __init__(self, channel: grpc.Channel = None, 
                       default_namespace_uri : str = None,
                       default_namespace_id : str = None,
                       namespace_if = None ) -> None:
        self._default_namespace_uri = default_namespace_uri
        self._default_namespace_id = default_namespace_id

        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.namespace_if = namespace_if if namespace_if is not None else base_if.NamespaceInterface(channel)

        # self.samplessubset_class_client = (
        #     lara_django_samples_pb2_grpc.SamplesSubsetclassByIRIControllerStub(channel)
        # )

        self.samplessubset_client = lara_django_samples_pb2_grpc.SamplesSubsetControllerStub(channel)
        

        # self.samplessubset_full_client = lara_django_samples_pb2_grpc.SamplesSubsetFullControllerStub(
        #     channel
        # )

    @property
    def default_namespace_uri(self):
        return self._default_namespace_uri

    @property
    def default_namespace_id(self):
        return self._default_namespace_id

    @classmethod
    async def build_if(cls, channel: grpc.Channel = None, default_namespace_uri: str = None):
    
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel
    
        namespace_if = base_if.NamespaceInterface(channel)
        try:
            default_namespace_id = await namespace_if.retrieve_id(namespace_uri=default_namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
        
        return cls(channel=channel, default_namespace_uri=default_namespace_uri,  
                   default_namespace_id=default_namespace_id, namespace_if=namespace_if)
    
    async def set_default_namespace_uri(self, namespace_uri: str = None):
        if namespace_uri is not None:
            self._default_namespace_uri = namespace_uri.strip()
        try:
            self._default_namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
    
    @import_model_instance_from_file()
    async def create(self,  samplessubsets:  list[samples_dm.SamplesSubset] = None,  
                            ids_only: bool = False,
                            namespace_uri: str = None) -> Union[list[str], list[samples_dm.SamplesSubset]]:
        """ create a list of samplessubset instances,
               returns a list of samplessubset data model objects or ids_only

        :param samplessubsets: list of samplessubset data model objects
        :param ids_only: return only the ids of the created instances
        :return: list of samplessubset data model objects or ids_only
        """
        samplessubsetclass_id = None

        namespace_id = None
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            namespace_id = self._default_namespace_id
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        if namespace_id is None:
            try:
                namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
            except Exception as e:
                logger.error(f"Error retrieving namespace_id: {e}")

        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if samplessubset_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.samplessubset_class_client.Retrieve(
        #         lara_django_samples_pb2.SamplesSubsetclassRetrieveRequest(iri=samplessubset_class_iri)
        #     )
        #     samplessubsetclass_id = res.samplessubsetclass_id
        # except Exception as e:
        #     logger.error(f"Error retrieving samplessubsetclass_id: {e}")
        for samplessubset in samplessubsets:
            if samplessubset.namespace == "" or samplessubset.namespace == "default":
                    samplessubset.namespace = namespace_id
            # samplessubset.samplessubset_class = samplessubsetclass_id # uncomment if a model class is used

        try:
            create_coroutines = ( self.samplessubset_client.Create(lara_django_samples_pb2.SamplesSubsetRequest(**samplessubset.model_dump())) for samplessubset in samplessubsets )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [ MessageToDict(item, preserving_proto_field_name=True).get("samplessubset_id") for item in res ]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating samplessubset: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        namespace_uri: str = None,
        samplessubset_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SamplesSubset]:
        """ retrieve a list of samplessubset instances by samplessubset_id, name or filter_dict,
               returns a list of samplessubset data model objects,
               if raw is True, the raw protobuf objects are returned.
               If namespace_uri is "default", the default namespace_uri is used.
        """
        if namespace_uri == "default":
            if self._default_namespace_uri is not None:
                namespace_uri = self._default_namespace_uri
            else:
                raise ValueError("No default namespace URI is given.")
        
        results_list = []
        if  samplessubset_id is not None:
            try:
                res =  await self.samplessubset_client.Retrieve(
                    lara_django_samples_pb2.SamplesSubsetRetrieveRequest(samplessubset_id=samplessubset_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SamplesSubset(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving samplessubset_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        if namespace_uri is not None:
            filter_dict["namespace__uri"] = namespace_uri
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.samplessubset_client.List(
                    lara_django_samples_pb2.SamplesSubsetListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # :TODO: a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.SamplesSubset(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving samplessubset name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          namespace_uri : str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the samplessubset_id for a given name,
               name_display or iri
        """

        results_list = await self.retrieve(name=name, 
                        namespace_uri=namespace_uri, filter_dict=filter_dict, raw=True)

        if len(results_list) == 1 :
            return results_list[0].samplessubset_id
        else:
            raise ValueError(f"Error retrieving samplessubset_id - no or too many results found.")
    
    async def get_by_id(self, samplessubset_id: str = None, id_only: bool = False) -> samples_dm.SamplesSubset:
        """ retrieve a samplessubset data model by samplessubset_id """
        try:
            res = await self.samplessubset_client.Retrieve(
                lara_django_samples_pb2.SamplesSubsetRetrieveRequest(samplessubset_id=samplessubset_id)
            )
            item = samples_dm.SamplesSubset(**MessageToDict(res, preserving_proto_field_name=True))
            self.item_id = item.samplessubset_id
            return item
        except Exception as e:
            logger.error(f"Error retrieving samplessubset_id: {e}")
    
    async def get_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                id_only: bool = False) -> samples_dm.SamplesSubset | str:
        """ retrieve a samplessubset data model by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            res = await self.samplessubset_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.SamplesSubsetRetrieveByNameNSRequest(name=name, namespace_uri=namespace_uri)
            )
            item = MessageToDict(res, preserving_proto_field_name=True)
            self.item_id = item["samplessubset_id"]
            if id_only:
                return self.item_id
            else:
                return samples_dm.SamplesSubset(**item)
        except Exception as e:
            logger.error(f"Error retrieving samplessubset name: {e}")
   
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all samplessubsets """
        if filter_dict is None:
            res = await self.samplessubset_client.List(lara_django_samples_pb2.SamplesSubsetListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.samplessubset_client.List(
                lara_django_samples_pb2.SamplesSubsetListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.samplessubset_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all samplessubsets """
        if self.samplessubset_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.samplessubset_full_client.List(
                lara_django_samples_pb2.SamplesSubsetFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.samplessubset_full_client.List(
                lara_django_samples_pb2.SamplesSubsetFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, samplessubset : samples_dm.SamplesSubset = None) -> None:
        """ update a samplessubset model instance """
        try:
            res = await self.samplessubset_client.Update(
                lara_django_samples_pb2.SamplesSubsetRequest(**samplessubset.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating samplessubset_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a samplessubset by samplessubset_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.samplessubset_client.Destroy(lara_django_samples_pb2.SamplesSubsetDestroyRequest(samplessubset_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting samplessubset_id: {e}")

#_________________  OrderedSamplesSubset  ______________________


class OrderedSamplesSubsetInterface:
    def __init__(self, channel: grpc.Channel = None) -> None:
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.orderedsamplessubset_client = lara_django_samples_pb2_grpc.OrderedSamplesSubsetControllerStub(channel)
        
    
    @import_model_instance_from_file()
    async def create(self,  orderedsamplessubsets:  list[samples_dm.OrderedSamplesSubset] = None,  ids_only: bool = False) -> Union[list[str], list[samples_dm.OrderedSamplesSubset]]:
        try:
            create_coroutines = ( self.orderedsamplessubset_client.Create(lara_django_samples_pb2.OrderedSamplesSubsetRequest(**orderedsamplessubset.model_dump())) for orderedsamplessubset in orderedsamplessubsets )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.orderedsamplessubset_id for item in res]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating orderedsamplessubset: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        orderedsamplessubset_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.OrderedSamplesSubset]:
        """ retrieve a list of orderedsamplessubset instances by orderedsamplessubset_id, name or filter_dict,
               returns a list of orderedsamplessubset data model objects,
               if raw is True, the raw protobuf objects are returned
        """
        results_list = []
        if  orderedsamplessubset_id is not None:
            try:
                res =  await self.orderedsamplessubset_client.Retrieve(
                    lara_django_samples_pb2.OrderedSamplesSubsetRetrieveRequest(orderedsamplessubset_id=orderedsamplessubset_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.OrderedSamplesSubset(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving orderedsamplessubset_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.orderedsamplessubset_client.List(
                    lara_django_samples_pb2.OrderedSamplesSubsetListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.OrderedSamplesSubset(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving orderedsamplessubset name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the orderedsamplessubset_id for a given name,
               (name_display) or iri
        """
        
        results_list = await self.retrieve(name=name, filter_dict=filter_dict, raw=True)
        if len(results_list) == 1 :
            return results_list[0].orderedsamplessubset_id
        else:
            raise ValueError(f"Error retrieving orderedsamplessubset_id - no or too many results found.")
    
    async def get_by_id(self, orderedsamplessubset_id: str = None) -> samples_dm.OrderedSamplesSubset:
        """ retrieve a orderedsamplessubset data model by orderedsamplessubset_id """
        try:
            res = await self.orderedsamplessubset_client.Retrieve(
                lara_django_samples_pb2.OrderedSamplesSubsetRetrieveRequest(orderedsamplessubset_id=orderedsamplessubset_id)
            )
            return samples_dm.OrderedSamplesSubset(**MessageToDict(res, preserving_proto_field_name=True))
        except Exception as e:
            logger.error(f"Error retrieving orderedsamplessubset_id: {e}")
    
    async def get_by_name(self, name: str = None, id_only: bool = False) -> samples_dm.OrderedSamplesSubset | str:
        """ retrieve a orderedsamplessubset data model by name """
        try:
            res = await self.orderedsamplessubset_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.OrderedSamplesSubsetRetrieveByNameNSRequest(name=name, namespace_uri="")
            )
            if id_only:
                return MessageToDict(res, preserving_proto_field_name=True)["orderedsamplessubset_id"]
            else:
                return samples_dm.OrderedSamplesSubset(**MessageToDict(res, preserving_proto_field_name=True))
        except Exception as e:
            logger.error(f"Error retrieving orderedsamplessubset name: {e}")
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all orderedsamplessubsets """
        if filter_dict is None:
            res = await self.orderedsamplessubset_client.List(lara_django_samples_pb2.OrderedSamplesSubsetListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.orderedsamplessubset_client.List(
                lara_django_samples_pb2.OrderedSamplesSubsetListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.orderedsamplessubset_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all orderedsamplessubsets """
        if self.orderedsamplessubset_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.orderedsamplessubset_full_client.List(
                lara_django_samples_pb2.OrderedSamplesSubsetFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.orderedsamplessubset_full_client.List(
                lara_django_samples_pb2.OrderedSamplesSubsetFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, orderedsamplessubset : samples_dm.OrderedSamplesSubset = None) -> None:
        """ update a orderedsamplessubset model instance """
        try:
            res = await self.orderedsamplessubset_client.Update(
                lara_django_samples_pb2.OrderedSamplesSubsetRequest(**orderedsamplessubset.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating orderedsamplessubset_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a orderedsamplessubset by orderedsamplessubset_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.orderedsamplessubset_client.Destroy(lara_django_samples_pb2.OrderedSamplesSubsetDestroyRequest(orderedsamplessubset_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting orderedsamplessubset_id: {e}")

#_________________  SampleSet  ______________________


class SampleSetInterface:
    def __init__(self, channel: grpc.Channel = None, 
                       default_namespace_uri : str = None,
                       default_namespace_id : str = None,
                       namespace_if = None ) -> None:
        self._default_namespace_uri = default_namespace_uri
        self._default_namespace_id = default_namespace_id

        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.namespace_if = namespace_if if namespace_if is not None else base_if.NamespaceInterface(channel)

        # self.sampleset_class_client = (
        #     lara_django_samples_pb2_grpc.SampleSetclassByIRIControllerStub(channel)
        # )

        self.sampleset_client = lara_django_samples_pb2_grpc.SampleSetControllerStub(channel)
        

        # self.sampleset_full_client = lara_django_samples_pb2_grpc.SampleSetFullControllerStub(
        #     channel
        # )

    @property
    def default_namespace_uri(self):
        return self._default_namespace_uri

    @property
    def default_namespace_id(self):
        return self._default_namespace_id

    @classmethod
    async def build_if(cls, channel: grpc.Channel = None, default_namespace_uri: str = None):
    
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel
    
        namespace_if = base_if.NamespaceInterface(channel)
        try:
            default_namespace_id = await namespace_if.retrieve_id(namespace_uri=default_namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
        
        return cls(channel=channel, default_namespace_uri=default_namespace_uri,  
                   default_namespace_id=default_namespace_id, namespace_if=namespace_if)
    
    async def set_default_namespace_uri(self, namespace_uri: str = None):
        if namespace_uri is not None:
            self._default_namespace_uri = namespace_uri.strip()
        try:
            self._default_namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
    
    @import_model_instance_from_file()
    async def create(self,  samplesets:  list[samples_dm.SampleSet] = None,  
                            ids_only: bool = False,
                            namespace_uri: str = None) -> Union[list[str], list[samples_dm.SampleSet]]:
        """ create a list of sampleset instances,
               returns a list of sampleset data model objects or ids_only

        :param samplesets: list of sampleset data model objects
        :param ids_only: return only the ids of the created instances
        :return: list of sampleset data model objects or ids_only
        """
        samplesetclass_id = None

        namespace_id = None
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            namespace_id = self._default_namespace_id
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        if namespace_id is None:
            try:
                namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
            except Exception as e:
                logger.error(f"Error retrieving namespace_id: {e}")

        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if sampleset_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.sampleset_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleSetclassRetrieveRequest(iri=sampleset_class_iri)
        #     )
        #     samplesetclass_id = res.samplesetclass_id
        # except Exception as e:
        #     logger.error(f"Error retrieving samplesetclass_id: {e}")
        for sampleset in samplesets:
            if sampleset.namespace == "" or sampleset.namespace == "default":
                    sampleset.namespace = namespace_id
            # sampleset.sampleset_class = samplesetclass_id # uncomment if a model class is used

        try:
            create_coroutines = ( self.sampleset_client.Create(lara_django_samples_pb2.SampleSetRequest(**sampleset.model_dump())) for sampleset in samplesets )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [ MessageToDict(item, preserving_proto_field_name=True).get("sampleset_id") for item in res ]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating sampleset: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        namespace_uri: str = None,
        sampleset_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleSet]:
        """ retrieve a list of sampleset instances by sampleset_id, name or filter_dict,
               returns a list of sampleset data model objects,
               if raw is True, the raw protobuf objects are returned.
               If namespace_uri is "default", the default namespace_uri is used.
        """
        if namespace_uri == "default":
            if self._default_namespace_uri is not None:
                namespace_uri = self._default_namespace_uri
            else:
                raise ValueError("No default namespace URI is given.")
        
        results_list = []
        if  sampleset_id is not None:
            try:
                res =  await self.sampleset_client.Retrieve(
                    lara_django_samples_pb2.SampleSetRetrieveRequest(sampleset_id=sampleset_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleSet(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving sampleset_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        if namespace_uri is not None:
            filter_dict["namespace__uri"] = namespace_uri
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.sampleset_client.List(
                    lara_django_samples_pb2.SampleSetListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # :TODO: a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.SampleSet(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving sampleset name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          namespace_uri : str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the sampleset_id for a given name,
               name_display or iri
        """

        results_list = await self.retrieve(name=name, 
                        namespace_uri=namespace_uri, filter_dict=filter_dict, raw=True)

        if len(results_list) == 1 :
            return results_list[0].sampleset_id
        else:
            raise ValueError(f"Error retrieving sampleset_id - no or too many results found.")
    
    async def get_by_id(self, sampleset_id: str = None, id_only: bool = False) -> samples_dm.SampleSet:
        """ retrieve a sampleset data model by sampleset_id """
        try:
            res = await self.sampleset_client.Retrieve(
                lara_django_samples_pb2.SampleSetRetrieveRequest(sampleset_id=sampleset_id)
            )
            item = samples_dm.SampleSet(**MessageToDict(res, preserving_proto_field_name=True))
            self.item_id = item.sampleset_id
            return item
        except Exception as e:
            logger.error(f"Error retrieving sampleset_id: {e}")
    
    async def get_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                id_only: bool = False) -> samples_dm.SampleSet | str:
        """ retrieve a sampleset data model by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            res = await self.sampleset_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.SampleSetRetrieveByNameNSRequest(name=name, namespace_uri=namespace_uri)
            )
            item = MessageToDict(res, preserving_proto_field_name=True)
            self.item_id = item["sampleset_id"]
            if id_only:
                return self.item_id
            else:
                return samples_dm.SampleSet(**item)
        except Exception as e:
            logger.error(f"Error retrieving sampleset name: {e}")
   
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all samplesets """
        if filter_dict is None:
            res = await self.sampleset_client.List(lara_django_samples_pb2.SampleSetListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.sampleset_client.List(
                lara_django_samples_pb2.SampleSetListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.sampleset_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all samplesets """
        if self.sampleset_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.sampleset_full_client.List(
                lara_django_samples_pb2.SampleSetFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.sampleset_full_client.List(
                lara_django_samples_pb2.SampleSetFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, sampleset : samples_dm.SampleSet = None) -> None:
        """ update a sampleset model instance """
        try:
            res = await self.sampleset_client.Update(
                lara_django_samples_pb2.SampleSetRequest(**sampleset.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating sampleset_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a sampleset by sampleset_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.sampleset_client.Destroy(lara_django_samples_pb2.SampleSetDestroyRequest(sampleset_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting sampleset_id: {e}")

#_________________  SampleSetsSubset  ______________________


class SampleSetsSubsetInterface:
    def __init__(self, channel: grpc.Channel = None, 
                       default_namespace_uri : str = None,
                       default_namespace_id : str = None,
                       namespace_if = None ) -> None:
        self._default_namespace_uri = default_namespace_uri
        self._default_namespace_id = default_namespace_id

        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.namespace_if = namespace_if if namespace_if is not None else base_if.NamespaceInterface(channel)

        # self.samplesetssubset_class_client = (
        #     lara_django_samples_pb2_grpc.SampleSetsSubsetclassByIRIControllerStub(channel)
        # )

        self.samplesetssubset_client = lara_django_samples_pb2_grpc.SampleSetsSubsetControllerStub(channel)
        

        # self.samplesetssubset_full_client = lara_django_samples_pb2_grpc.SampleSetsSubsetFullControllerStub(
        #     channel
        # )

    @property
    def default_namespace_uri(self):
        return self._default_namespace_uri

    @property
    def default_namespace_id(self):
        return self._default_namespace_id

    @classmethod
    async def build_if(cls, channel: grpc.Channel = None, default_namespace_uri: str = None):
    
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel
    
        namespace_if = base_if.NamespaceInterface(channel)
        try:
            default_namespace_id = await namespace_if.retrieve_id(namespace_uri=default_namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
        
        return cls(channel=channel, default_namespace_uri=default_namespace_uri,  
                   default_namespace_id=default_namespace_id, namespace_if=namespace_if)
    
    async def set_default_namespace_uri(self, namespace_uri: str = None):
        if namespace_uri is not None:
            self._default_namespace_uri = namespace_uri.strip()
        try:
            self._default_namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logger.error(f"Error retrieving namespace_id: {e}")
    
    @import_model_instance_from_file()
    async def create(self,  samplesetssubsets:  list[samples_dm.SampleSetsSubset] = None,  
                            ids_only: bool = False,
                            namespace_uri: str = None) -> Union[list[str], list[samples_dm.SampleSetsSubset]]:
        """ create a list of samplesetssubset instances,
               returns a list of samplesetssubset data model objects or ids_only

        :param samplesetssubsets: list of samplesetssubset data model objects
        :param ids_only: return only the ids of the created instances
        :return: list of samplesetssubset data model objects or ids_only
        """
        samplesetssubsetclass_id = None

        namespace_id = None
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
            namespace_id = self._default_namespace_id
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        if namespace_id is None:
            try:
                namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
            except Exception as e:
                logger.error(f"Error retrieving namespace_id: {e}")

        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if samplesetssubset_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.samplesetssubset_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleSetsSubsetclassRetrieveRequest(iri=samplesetssubset_class_iri)
        #     )
        #     samplesetssubsetclass_id = res.samplesetssubsetclass_id
        # except Exception as e:
        #     logger.error(f"Error retrieving samplesetssubsetclass_id: {e}")
        for samplesetssubset in samplesetssubsets:
            if samplesetssubset.namespace == "" or samplesetssubset.namespace == "default":
                    samplesetssubset.namespace = namespace_id
            # samplesetssubset.samplesetssubset_class = samplesetssubsetclass_id # uncomment if a model class is used

        try:
            create_coroutines = ( self.samplesetssubset_client.Create(lara_django_samples_pb2.SampleSetsSubsetRequest(**samplesetssubset.model_dump())) for samplesetssubset in samplesetssubsets )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [ MessageToDict(item, preserving_proto_field_name=True).get("samplesetssubset_id") for item in res ]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating samplesetssubset: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        namespace_uri: str = None,
        samplesetssubset_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleSetsSubset]:
        """ retrieve a list of samplesetssubset instances by samplesetssubset_id, name or filter_dict,
               returns a list of samplesetssubset data model objects,
               if raw is True, the raw protobuf objects are returned.
               If namespace_uri is "default", the default namespace_uri is used.
        """
        if namespace_uri == "default":
            if self._default_namespace_uri is not None:
                namespace_uri = self._default_namespace_uri
            else:
                raise ValueError("No default namespace URI is given.")
        
        results_list = []
        if  samplesetssubset_id is not None:
            try:
                res =  await self.samplesetssubset_client.Retrieve(
                    lara_django_samples_pb2.SampleSetsSubsetRetrieveRequest(samplesetssubset_id=samplesetssubset_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleSetsSubset(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving samplesetssubset_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        if namespace_uri is not None:
            filter_dict["namespace__uri"] = namespace_uri
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.samplesetssubset_client.List(
                    lara_django_samples_pb2.SampleSetsSubsetListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # :TODO: a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.SampleSetsSubset(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving samplesetssubset name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          namespace_uri : str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the samplesetssubset_id for a given name,
               name_display or iri
        """

        results_list = await self.retrieve(name=name, 
                        namespace_uri=namespace_uri, filter_dict=filter_dict, raw=True)

        if len(results_list) == 1 :
            return results_list[0].samplesetssubset_id
        else:
            raise ValueError(f"Error retrieving samplesetssubset_id - no or too many results found.")
    
    async def get_by_id(self, samplesetssubset_id: str = None, id_only: bool = False) -> samples_dm.SampleSetsSubset:
        """ retrieve a samplesetssubset data model by samplesetssubset_id """
        try:
            res = await self.samplesetssubset_client.Retrieve(
                lara_django_samples_pb2.SampleSetsSubsetRetrieveRequest(samplesetssubset_id=samplesetssubset_id)
            )
            item = samples_dm.SampleSetsSubset(**MessageToDict(res, preserving_proto_field_name=True))
            self.item_id = item.samplesetssubset_id
            return item
        except Exception as e:
            logger.error(f"Error retrieving samplesetssubset_id: {e}")
    
    async def get_by_name(self, name: str = None,
                                namespace_uri : str = None,
                                id_only: bool = False) -> samples_dm.SampleSetsSubset | str:
        """ retrieve a samplesetssubset data model by name """
        if namespace_uri is None and self._default_namespace_uri is not None:
            namespace_uri = self._default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            res = await self.samplesetssubset_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.SampleSetsSubsetRetrieveByNameNSRequest(name=name, namespace_uri=namespace_uri)
            )
            item = MessageToDict(res, preserving_proto_field_name=True)
            self.item_id = item["samplesetssubset_id"]
            if id_only:
                return self.item_id
            else:
                return samples_dm.SampleSetsSubset(**item)
        except Exception as e:
            logger.error(f"Error retrieving samplesetssubset name: {e}")
   
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all samplesetssubsets """
        if filter_dict is None:
            res = await self.samplesetssubset_client.List(lara_django_samples_pb2.SampleSetsSubsetListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.samplesetssubset_client.List(
                lara_django_samples_pb2.SampleSetsSubsetListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.samplesetssubset_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all samplesetssubsets """
        if self.samplesetssubset_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.samplesetssubset_full_client.List(
                lara_django_samples_pb2.SampleSetsSubsetFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.samplesetssubset_full_client.List(
                lara_django_samples_pb2.SampleSetsSubsetFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, samplesetssubset : samples_dm.SampleSetsSubset = None) -> None:
        """ update a samplesetssubset model instance """
        try:
            res = await self.samplesetssubset_client.Update(
                lara_django_samples_pb2.SampleSetsSubsetRequest(**samplesetssubset.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating samplesetssubset_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a samplesetssubset by samplesetssubset_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.samplesetssubset_client.Destroy(lara_django_samples_pb2.SampleSetsSubsetDestroyRequest(samplesetssubset_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting samplesetssubset_id: {e}")

#_________________  OrderedSamplesetsSubset  ______________________


class OrderedSamplesetsSubsetInterface:
    def __init__(self, channel: grpc.Channel = None) -> None:
        if channel is None:
            cs = GRPCChannelSingleton()
            channel = cs.channel

        self.orderedsamplesetssubset_client = lara_django_samples_pb2_grpc.OrderedSamplesetsSubsetControllerStub(channel)
        
    
    @import_model_instance_from_file()
    async def create(self,  orderedsamplesetssubsets:  list[samples_dm.OrderedSamplesetsSubset] = None,  ids_only: bool = False) -> Union[list[str], list[samples_dm.OrderedSamplesetsSubset]]:
        try:
            create_coroutines = ( self.orderedsamplesetssubset_client.Create(lara_django_samples_pb2.OrderedSamplesetsSubsetRequest(**orderedsamplesetssubset.model_dump())) for orderedsamplesetssubset in orderedsamplesetssubsets )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.orderedsamplesetssubset_id for item in res]
            else:
                return res

        except Exception as e:
            logger.error(f"Error creating orderedsamplesetssubset: {e}")
        
    @export_model_instance_to_file
    async def retrieve(
        self,
        orderedsamplesetssubset_id: str = None,
        name: str = None,
        # name_display: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.OrderedSamplesetsSubset]:
        """ retrieve a list of orderedsamplesetssubset instances by orderedsamplesetssubset_id, name or filter_dict,
               returns a list of orderedsamplesetssubset data model objects,
               if raw is True, the raw protobuf objects are returned
        """
        results_list = []
        if  orderedsamplesetssubset_id is not None:
            try:
                res =  await self.orderedsamplesetssubset_client.Retrieve(
                    lara_django_samples_pb2.OrderedSamplesetsSubsetRetrieveRequest(orderedsamplesetssubset_id=orderedsamplesetssubset_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.OrderedSamplesetsSubset(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logger.error(f"Error retrieving orderedsamplesetssubset_id: {e}")

        if filter_dict is None:
            filter_dict = {}

        if name is not None:
            filter_dict["name"] = name
        # if name_display is not None:
        #    filter_dict["name_display"] = name_display

        metadata = (("filters", (json.dumps(filter_dict))),)
        try:
            res = await self.orderedsamplesetssubset_client.List(
                    lara_django_samples_pb2.OrderedSamplesetsSubsetListRequest(), metadata=metadata
                    )
            if raw:
                    results_list += res.results
            else:
                # a queue could be used here to speed up the retrieval
                results = res.results
                results_list += [ samples_dm.OrderedSamplesetsSubset(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
        except Exception as e:
            logger.error(f"Error retrieving orderedsamplesetssubset name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self,
                          name: str = None,
                          # name_display: str = None,
                          filter_dict : dict = None,
                          iri: str = None,) -> str:
        """ retrieve the orderedsamplesetssubset_id for a given name,
               (name_display) or iri
        """
        
        results_list = await self.retrieve(name=name, filter_dict=filter_dict, raw=True)
        if len(results_list) == 1 :
            return results_list[0].orderedsamplesetssubset_id
        else:
            raise ValueError(f"Error retrieving orderedsamplesetssubset_id - no or too many results found.")
    
    async def get_by_id(self, orderedsamplesetssubset_id: str = None) -> samples_dm.OrderedSamplesetsSubset:
        """ retrieve a orderedsamplesetssubset data model by orderedsamplesetssubset_id """
        try:
            res = await self.orderedsamplesetssubset_client.Retrieve(
                lara_django_samples_pb2.OrderedSamplesetsSubsetRetrieveRequest(orderedsamplesetssubset_id=orderedsamplesetssubset_id)
            )
            return samples_dm.OrderedSamplesetsSubset(**MessageToDict(res, preserving_proto_field_name=True))
        except Exception as e:
            logger.error(f"Error retrieving orderedsamplesetssubset_id: {e}")
    
    async def get_by_name(self, name: str = None, id_only: bool = False) -> samples_dm.OrderedSamplesetsSubset | str:
        """ retrieve a orderedsamplesetssubset data model by name """
        try:
            res = await self.orderedsamplesetssubset_client.RetrieveByNameNamespace(
                lara_django_samples_pb2.OrderedSamplesetsSubsetRetrieveByNameNSRequest(name=name, namespace_uri="")
            )
            if id_only:
                return MessageToDict(res, preserving_proto_field_name=True)["orderedsamplesetssubset_id"]
            else:
                return samples_dm.OrderedSamplesetsSubset(**MessageToDict(res, preserving_proto_field_name=True))
        except Exception as e:
            logger.error(f"Error retrieving orderedsamplesetssubset name: {e}")
    
    
    
    
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        """ list all orderedsamplesetssubsets """
        if filter_dict is None:
            res = await self.orderedsamplesetssubset_client.List(lara_django_samples_pb2.OrderedSamplesetsSubsetListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.orderedsamplesetssubset_client.List(
                lara_django_samples_pb2.OrderedSamplesetsSubsetListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.orderedsamplesetssubset_id for item in res.results]
        else:
            return res.results

    async def list_full(self, filter_dict: dict = None) -> list:
        """ list all orderedsamplesetssubsets """
        if self.orderedsamplesetssubset_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.orderedsamplesetssubset_full_client.List(
                lara_django_samples_pb2.OrderedSamplesetsSubsetFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.orderedsamplesetssubset_full_client.List(
                lara_django_samples_pb2.OrderedSamplesetsSubsetFullListRequest(), metadata=metadata
            )
    
    @import_model_instance_from_file()
    async def update(self, orderedsamplesetssubset : samples_dm.OrderedSamplesetsSubset = None) -> None:
        """ update a orderedsamplesetssubset model instance """
        try:
            res = await self.orderedsamplesetssubset_client.Update(
                lara_django_samples_pb2.OrderedSamplesetsSubsetRequest(**orderedsamplesetssubset.model_dump()
            ))
        except Exception as e:
            logger.error(f"Error updating orderedsamplesetssubset_id: {e}")
        else:
            return res
    
    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        """ delete a orderedsamplesetssubset by orderedsamplesetssubset_id or filter_dict """
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)

        try:
            delete_coroutines = ( self.orderedsamplesetssubset_client.Destroy(lara_django_samples_pb2.OrderedSamplesetsSubsetDestroyRequest(orderedsamplesetssubset_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)

        except Exception as e:
            logger.error(f"Error deleting orderedsamplesetssubset_id: {e}")

