#!D:\IDE\Anaconda3\envs\Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName   : CustomScanningModel.py
@Account    : Administrator
@Time       : 2025/8/29 13:55
@Author     : xsh
@Description: 
"""
from AppCore.AppDBModels.CoreModel import (CoreModel, dbController)
from sqlalchemy.orm import Session
from sqlalchemy.orm import (Mapped, mapped_column)
from sqlalchemy.types import (Text)
from AppUtils.utils import *
from AppCore.Levels import *
from AppCore.AppDBModels.TemplateModels.ScanTemplate            import ScanTemplate, Collectsettings, Previewsettings
from AppSettings.Settings import DEFAULT_AUTO_SCAN_CODE

class CustomScanningModel(CoreModel):
    __tablename__ = 't_CustomScanningModel'
    ID                      : Mapped[int    ] = mapped_column(primary_key=True                                      , nullable=False , doc='二维码编号　', comment='二维码编号　')
    ChamberGroupNumber      : Mapped[int    ] = mapped_column(default=1                                             , nullable=True  , doc='片仓组号　　', comment='片仓组号　　')
    ChamberRowNumber        : Mapped[int    ] = mapped_column(default=1                                             , nullable=True  , doc='片仓行号　　', comment='片仓行号　　')
    TemplateID              : Mapped[int    ] = mapped_column(default=1                                             , nullable=True  , doc='扫描模板　　', comment='扫描模板　　')
    PreviewSettingID        : Mapped[int    ] = mapped_column(default=1                                             , nullable=True  , doc='预览模板　　', comment='预览模板　　')
    CollectSettingID        : Mapped[int    ] = mapped_column(default=1                                             , nullable=True  , doc='采集模板　　', comment='采集模板　　')
    CustomSpecimen          : Mapped[str    ] = mapped_column(Text      , default='#'                               , nullable=True  , doc='自定义样本号', comment='自定义样本号')
    CodeString              : Mapped[str    ] = mapped_column(Text      , default=''                                , nullable=True  , doc='二维码编号　', comment='二维码编号　')
    SpecimenNumber          : Mapped[str    ] = mapped_column(Text      , default=''                                , nullable=True  , doc='样本编号　　', comment='样本编号　　')
    SlideNumber             : Mapped[str    ] = mapped_column(Text      , default=''                                , nullable=True  , doc='玻片编号　　', comment='玻片编号　　')
    LineNumber              : Mapped[str    ] = mapped_column(Text      , default=''                                , nullable=True  , doc='样本线号　　', comment='样本线号　　')
    SavePath                : Mapped[str    ] = mapped_column(Text      , default=''                                , nullable=True  , doc='保存路径　　', comment='保存路径　　')
    uiIndex                 : Mapped[str    ] = mapped_column(Text      , default=''                                , nullable=True  , doc='前端序号　　', comment='前端序号　　')
    color                   : Mapped[str    ] = mapped_column(Text      , default='#FF0000'                         , nullable=True  , doc='模板颜色　　', comment='模板颜色　　')
    def __init__(self, **args):
        super(CustomScanningModel, self).__init__()
        self.initialize()
        self.initParameter(**args)

    def initValue(self):
        super(CustomScanningModel, self).initValue()
        self.ID                       = ''
        self.ChamberGroupNumber       = 1
        self.ChamberRowNumber         = 1
        self.TemplateID               = 1
        self.PreviewSettingID         = 1
        self.CollectSettingID         = 1
        self.CodeString               = ''
        self.SpecimenNumber           = ''
        self.SlideNumber              = ''
        self.LineNumber               = ''
        self.SavePath                 = 'D:\\DeepLearning_data\\Cases\\'
        self.uiIndex                  = '1-1'
        self.color                    = '#FF0000'
        self.CustomSpecimen           = '#'
    __field__ = (
        'ID'                 ,
        'ChamberGroupNumber' ,
        'ChamberRowNumber'   ,
        'TemplateID'         ,
        'PreviewSettingID'   ,
        'CollectSettingID'   ,
        'CustomSpecimen'     ,
        'CodeString'         ,
        'SpecimenNumber'     ,
        'SlideNumber'        ,
        'LineNumber'         ,
        'SavePath'           ,
        'uiIndex'            ,
        'color'              ,
    )
    __types__ = (
        int    ,
        int    ,
        int    ,
        int    ,
        int    ,
        int    ,
        str    ,
        str    ,
        str    ,
        str    ,
        str    ,
        str    ,
        str    ,
        str    ,
    )

    def isAutoScanCode(self) -> bool:
        return not self.hasCustomSpecimen or self.CustomSpecimen == DEFAULT_AUTO_SCAN_CODE or DEFAULT_AUTO_SCAN_CODE in self.CustomSpecimen

    @classmethod
    def IsAutoScanCode(cls, chamber_number: int, row_number: int) -> bool:
        item: cls = cls.get(cls.ChamberGroupNumber == chamber_number, cls.ChamberRowNumber == row_number)
        return item is not None and item.isAutoScanCode()

    @classmethod
    def initAutoScanCode(cls):
        item: cls = None
        try:
            dbController.initEngine()
            with Session(dbController.Engine) as session:
                try:
                    session.query(cls).update({
                        cls.CustomSpecimen      : DEFAULT_AUTO_SCAN_CODE    ,
                        cls.TemplateID          : None                      ,
                        cls.PreviewSettingID    : None                      ,
                        cls.CollectSettingID    : None                      ,
                        cls.CodeString          : None                      ,
                        cls.SpecimenNumber      : None                      ,
                        cls.SlideNumber         : None                      ,
                        cls.LineNumber          : None                      ,
                    })
                    session.commit()
                except Exception as commonE:
                    AppUtils.print_Exception(commonE)
                    session.rollback()
                session.close()
        except Exception as e:
            from AppLog.logHelper import logDB
            logDB << AppUtils.toString(e)
        return item

    @classmethod
    def setCodeString(cls, chamber_number: int, row_number: int, codestring: str):
        from AppSettings.SystemSetting import SystemSetting, SystemConfig
        isWrong         = 0
        ChamberNumber   : int
        RowNumber       : int
        ChamberNumber, RowNumber = SystemConfig.getChamberNumbers(chamber_number, row_number)
        cls.model_update({cls.CodeString: codestring}, cls.ChamberGroupNumber == ChamberNumber, cls.ChamberRowNumber == RowNumber)

    @classmethod
    def getSpecimenNumber(cls, chamber_number: int, row_number: int) -> str:
        item: cls = cls.getModel(chamber_number, row_number)
        return item.SpecimenNumber if item is not None and item.hasSpecimenNumber else None


    @classmethod
    def setChamberCodeString(cls, chamber_number: int, row_number: int, CodeString: str, SpecimenNumber: str = None, SlideNumber: str = None):
        from AppSettings.SystemSetting import SystemSetting, SystemConfig
        isWrong = 0
        ChamberNumber: int
        RowNumber: int
        ChamberNumber, RowNumber = SystemConfig.getChamberNumbers(chamber_number, row_number)
        cls.model_update(
            {
                cls.CodeString      : CodeString        ,
                cls.SpecimenNumber  : SpecimenNumber    ,
                cls.SlideNumber     : SlideNumber       ,
             },
            cls.ChamberGroupNumber  == ChamberNumber    ,
            cls.ChamberRowNumber    == RowNumber        )

    @classmethod
    def getCustomSpecimen(cls, chamber_number: int, row_number: int) -> str:
        from AppSettings.SystemSetting import SystemSetting, SystemConfig
        isWrong = 0
        ChamberNumber: int
        RowNumber: int
        ChamberNumber, RowNumber = SystemConfig.getChamberNumbers(chamber_number, row_number)
        item: cls = None
        try:
            dbController.initEngine()
            with Session(dbController.Engine) as session:
                item = session.query(cls).filter(cls.ChamberGroupNumber == ChamberNumber, cls.ChamberRowNumber == RowNumber).first()
                session.close()
        except Exception as e:
            AppUtils.print_Exception(e)
            item = None
        return item.CustomSpecimen if item is not None else ''

    @classmethod
    def initModels(cls):
        models: list[cls] = []
        item: cls
        MinChamberNumber: int
        MaxChamberNumber: int
        MinRowNumber    : int
        MaxRowNumber    : int
        ChamberNumber   : int
        RowNumber       : int
        Index           : int = 1

        from AppSettings.SystemSetting import SystemConfig
        MinChamberNumber, MaxChamberNumber = SystemConfig.getChamberRange  ()
        MinRowNumber    , MaxRowNumber     = SystemConfig.getRowRange      ()
        ChamberNumber = MinChamberNumber
        RowNumber     = MinRowNumber
        for ChamberNumber in range(MinChamberNumber, MaxChamberNumber + 1):
            for RowNumber in range(MinRowNumber, MaxRowNumber + 1):
                models.append(cls.instance(ID=Index, ChamberGroupNumber = ChamberNumber, ChamberRowNumber = RowNumber,
                                           CustomSpecimen   = DEFAULT_AUTO_SCAN_CODE   ,
                                           TemplateID       = None                     ,CodeString       = None                             ,
                                           PreviewSettingID = None                     ,CollectSettingID = None                             ,
                                           SpecimenNumber   = None                     ,SlideNumber      = None                             ,
                                           LineNumber       = None                     ,uiIndex          = f'{RowNumber}-{ChamberNumber}')  )
                Index += 1

        return models

    @classmethod
    def InitCustomData(cls):
        MinChamberNumber: int
        MaxChamberNumber: int
        MinRowNumber    : int
        MaxRowNumber    : int
        ChamberNumber   : int
        RowNumber       : int
        from AppSettings.SystemSetting import SystemConfig
        MinChamberNumber, MaxChamberNumber  = SystemConfig.getChamberRange  ()
        MinRowNumber    , MaxRowNumber      = SystemConfig.getRowRange      ()
        for ChamberNumber in range(MinChamberNumber, MaxChamberNumber + 1):
            for RowNumber in range(MinRowNumber, MaxRowNumber + 1):
                cls.model_update(
                    {
                        cls.CustomSpecimen   : DEFAULT_AUTO_SCAN_CODE   ,
                        cls.TemplateID       : None                     ,
                        cls.CodeString       : None                     ,
                        cls.PreviewSettingID : None                     ,
                        cls.CollectSettingID : None                     ,
                        cls.SpecimenNumber   : None                     ,
                        cls.SlideNumber      : None                     ,
                        cls.LineNumber       : None                     ,
                        cls.uiIndex          : None                     ,
                        cls.SavePath         : None                     ,
                    }, cls.ChamberGroupNumber == ChamberNumber, cls.ChamberRowNumber == RowNumber
                )

    @classmethod
    def InitChamberCustomData(cls, chamber_number: int):
        MinRowNumber: int
        MaxRowNumber: int
        from AppSettings.SystemSetting import SystemConfig
        MinRowNumber, MaxRowNumber = SystemConfig.getRowRange()
        for RowNumber in range(MinRowNumber, MaxRowNumber + 1):
            cls.model_update(
                {
                    cls.CustomSpecimen   : DEFAULT_AUTO_SCAN_CODE   ,
                    cls.TemplateID       : None                     ,
                    cls.CodeString       : None                     ,
                    cls.PreviewSettingID : None                     ,
                    cls.CollectSettingID : None                     ,
                    cls.SpecimenNumber   : None                     ,
                    cls.SlideNumber      : None                     ,
                    cls.LineNumber       : None                     ,
                    cls.uiIndex          : None                     ,
                    cls.SavePath         : None                     ,
                }, cls.ChamberGroupNumber == chamber_number, cls.ChamberRowNumber == RowNumber
            )

    @classmethod
    def InitCustomScanningData(cls, chamber_number: int, row_number: int):
        cls.model_update(
            {
                cls.CustomSpecimen   : DEFAULT_AUTO_SCAN_CODE   ,
                cls.TemplateID       : None                     ,
                cls.CodeString       : None                     ,
                cls.PreviewSettingID : None                     ,
                cls.CollectSettingID : None                     ,
                cls.SpecimenNumber   : None                     ,
                cls.SlideNumber      : None                     ,
                cls.LineNumber       : None                     ,
                cls.uiIndex          : None                     ,
                cls.SavePath         : None                     ,
            }, cls.ChamberGroupNumber == chamber_number, cls.ChamberRowNumber == row_number
        )


    @classmethod
    def setCustomData(cls, items: list[list[dict[str, object]]]):
        chamber_data             : list[dict[str, object]]
        row_data                 : dict[str, object]
        barcode                  : str
        templateID               : int
        ChamberGroupNumber       : int
        ChamberRowNumber         : int
        CodeString               : str
        SpecimenNumber           : str
        SlideNumber              : str
        LineNumber               : str
        SavePath                 : str
        uiIndex                  : str
        color                    : str
        TemplateID               : int = None
        PreviewSettingID         : int = None
        CollectSettingID         : int = None
        template                 : ScanTemplate = None
        SavePath                                = ''
        models: list[cls] = []
        cls.InitCustomData()
        for ChamberGroupNumber, chamber_data in enumerate(items):
            if isinstance(chamber_data, list):
                for ChamberRowNumber, row_data in enumerate(chamber_data):
                    if isinstance(row_data, dict):
                        barcode     = AppConvert.ObjectConvert(row_data.get('barcode'   , None), str)
                        color       = AppConvert.ObjectConvert(row_data.get('color'     , None), str)
                        uiIndex     = AppConvert.ObjectConvert(row_data.get('uiIndex'   , None), str)
                        TemplateID  = AppConvert.ObjectConvert(row_data.get("templateID", None), int)
                        if isinstance(TemplateID, int):
                            if not isinstance(template, ScanTemplate) or template.ID != TemplateID:
                                template = ScanTemplate.Primary(TemplateID)
                        else:
                            TemplateID = None
                        CustomSpecimen      = DEFAULT_AUTO_SCAN_CODE
                        CodeString          = None
                        PreviewSettingID    = None
                        CollectSettingID    = None
                        SpecimenNumber      = None
                        SlideNumber         = None
                        LineNumber          = None
                        SlideNumber         = f'{ChamberRowNumber + 1}'
                        separator_no        : str = '-'
                        if isinstance(template, ScanTemplate):
                            PreviewSettingID = template.previewsettings_ID
                            CollectSettingID = template.collectsettings_ID
                            separator_no     = f'{template.separator_no}'
                        if isinstance(barcode, str):
                            if barcode != DEFAULT_AUTO_SCAN_CODE:
                                SpecimenNumber  = barcode
                                CustomSpecimen  = barcode
                                CodeString      = f'{SpecimenNumber}{separator_no}{SlideNumber}'

                        models.append(cls.instance(
                            ChamberGroupNumber       = ChamberGroupNumber + 1   ,
                            ChamberRowNumber         = ChamberRowNumber + 1     ,
                            TemplateID               = TemplateID               ,
                            PreviewSettingID         = PreviewSettingID         ,
                            CollectSettingID         = CollectSettingID         ,
                            CustomSpecimen           = CustomSpecimen           ,
                            CodeString               = CodeString               ,
                            SpecimenNumber           = SpecimenNumber           ,
                            SlideNumber              = SlideNumber              ,
                            LineNumber               = LineNumber               ,
                            SavePath                 = SavePath                 ,
                            uiIndex                  = uiIndex                  ,
                            color                    = color                    ,
                        ))
        try:
            dbController.initEngine()
            with Session(dbController.Engine) as session:
                for model in models:
                    session.query(cls).filter(cls.ChamberGroupNumber == model.ChamberGroupNumber, cls.ChamberRowNumber == model.ChamberRowNumber).update(
                        {
                            cls.TemplateID               : model.TemplateID               ,
                            cls.PreviewSettingID         : model.PreviewSettingID         ,
                            cls.CollectSettingID         : model.CollectSettingID         ,
                            cls.CodeString               : model.CodeString               ,
                            cls.SpecimenNumber           : model.SpecimenNumber           ,
                            cls.SlideNumber              : model.SlideNumber              ,
                            cls.LineNumber               : model.LineNumber               ,
                            cls.SavePath                 : model.SavePath                 ,
                            cls.uiIndex                  : model.uiIndex                  ,
                            cls.color                    : model.color                    ,
                            cls.CustomSpecimen           : model.CustomSpecimen           ,
                        }
                    )
                session.commit()
                session.close()
        except Exception as e:
            AppUtils.print_Exception(e)


    @classmethod
    def setTrayTemplate(cls, chamber_number: int, templateID: int = None):
        TemplateID          : int
        PreviewSettingID    : int
        CollectSettingID    : int
        Template            : ScanTemplate      = None
        PreviewSetting      : Previewsettings   = None
        CollectSetting      : CollectSettings   = None
        CustomSpecimen      : str
        if isinstance(templateID, int):
            Template = ScanTemplate.Primary(templateID)
            if isinstance(Template, ScanTemplate):
                PreviewSetting = Previewsettings.Primary(Template.previewsettings_ID)
                CollectSetting = CollectSettings.Primary(Template.collectsettings_ID)
        TemplateID       = Template.ID       if isinstance(Template         , ScanTemplate      ) else None
        PreviewSettingID = PreviewSetting.ID if isinstance(PreviewSetting   , Previewsettings   ) else None
        CollectSettingID = CollectSetting.ID if isinstance(CollectSetting   , CollectSettings   ) else None
        if isinstance(Template         , ScanTemplate      ) and isinstance(CollectSetting   , CollectSettings   ) and isinstance(PreviewSetting   , Previewsettings   ):
            CustomSpecimen = None
        else:
            CustomSpecimen = DEFAULT_AUTO_SCAN_CODE

        cls.model_update(
            {
                cls.CustomSpecimen   : CustomSpecimen           ,
                cls.TemplateID       : TemplateID               ,
                cls.PreviewSettingID : PreviewSettingID         ,
                cls.CollectSettingID : CollectSettingID         ,
                cls.CodeString       : None                     ,
                cls.SpecimenNumber   : None                     ,
                cls.SlideNumber      : None                     ,
                cls.LineNumber       : None                     ,
                cls.uiIndex          : None                     ,
                cls.SavePath         : None                     ,
            }, cls.ChamberGroupNumber == chamber_number
        )

    @classmethod
    def setTraySlideTemplate(cls, chamber_number: int, row_number: int, templateID: int):
        TemplateID          : int
        PreviewSettingID    : int
        CollectSettingID    : int
        Template            : ScanTemplate      = None
        PreviewSetting      : Previewsettings   = None
        CollectSetting      : CollectSettings   = None
        CustomSpecimen      : str
        if isinstance(templateID, int):
            Template = ScanTemplate.Primary(templateID)
            if isinstance(Template, ScanTemplate):
                PreviewSetting = Previewsettings.Primary(Template.previewsettings_ID)
                CollectSetting = CollectSettings.Primary(Template.collectsettings_ID)
        TemplateID       = Template.ID       if isinstance(Template         , ScanTemplate      ) else None
        PreviewSettingID = PreviewSetting.ID if isinstance(PreviewSetting   , Previewsettings   ) else None
        CollectSettingID = CollectSetting.ID if isinstance(CollectSetting   , CollectSettings   ) else None
        if isinstance(Template         , ScanTemplate      ) and isinstance(CollectSetting   , CollectSettings   ) and isinstance(PreviewSetting   , Previewsettings   ):
            CustomSpecimen = None
        else:
            CustomSpecimen = DEFAULT_AUTO_SCAN_CODE


        cls.model_update(
            {
                cls.CustomSpecimen   : CustomSpecimen           ,
                cls.TemplateID       : TemplateID               ,
                cls.PreviewSettingID : PreviewSettingID         ,
                cls.CollectSettingID : CollectSettingID         ,
                cls.CodeString       : None                     ,
                cls.SpecimenNumber   : None                     ,
                cls.SlideNumber      : None                     ,
                cls.LineNumber       : None                     ,
                cls.uiIndex          : None                     ,
                cls.SavePath         : None                     ,
            }, cls.ChamberGroupNumber == chamber_number, cls.ChamberRowNumber == row_number
        )

    @classmethod
    def getCustomData(cls) -> list:
        models: list[cls] = cls.all()
        if not isinstance(models, list) or len(models) <= 0:
            return []
        item: cls
        buffer: list = [{'barcode': item.CodeString, 'color': item.color, 'uiIndex': item.uiIndex, "templateID": item.TemplateID} for item in models]
        return buffer


    @classmethod
    def template(cls, chamber_number: int, row_number: int) -> (ScanTemplate, Previewsettings, Collectsettings):
        item: cls = cls.getModel(chamber_number, row_number)
        template: ScanTemplate
        if item is None:
            template = ScanTemplate.get(ScanTemplate.is_default == 1)
            if template is None:
                template = ScanTemplate.first()
            if template is None:
                template = ScanTemplate.get(ScanTemplate.ID == 1)
            if template is None:
                return None, None, None
            cls.set_template(chamber_number, row_number, template.ID, template.previewsettings_ID, template.collectsettings_ID)
            return template, Previewsettings.Primary(template.previewsettings_ID), Collectsettings.Primary(template.collectsettings_ID)
        return ScanTemplate.Primary(item.TemplateID), Previewsettings.Primary(item.PreviewSettingID), Collectsettings.Primary(item.CollectSettingID)

    @classmethod
    def getTemplate(cls, chamber_number: int, row_number: int) -> ScanTemplate:
        item: cls = cls.getModel(chamber_number, row_number)
        template: ScanTemplate
        if item is None:
            template = ScanTemplate.get(ScanTemplate.is_default == 1)
            if template is None:
                template = ScanTemplate.first()
            if template is None:
                template = ScanTemplate.get(ScanTemplate.ID == 1)
            if template is None:
                return None
            cls.set_template(chamber_number, row_number, template.ID, template.previewsettings_ID,template.collectsettings_ID)
            return template
        return ScanTemplate.Primary(item.TemplateID)
    @classmethod
    def set_template(cls, chamber_number: int, row_number: int, template_ID: int, preview_ID: int, collect_ID: int):
        cls.model_update(
            {
                cls.TemplateID      : template_ID,
                cls.PreviewSettingID: preview_ID,
                cls.CollectSettingID: collect_ID,
            },
            cls.ChamberGroupNumber == chamber_number,
            cls.ChamberRowNumber   == row_number
        )
    @classmethod
    def PreviewSetting(cls, chamber_number: int, row_number: int) -> Previewsettings:
        item: cls = cls.getModel(chamber_number, row_number)
        if item is None:
            return None
        return Previewsettings.Primary(item.PreviewSettingID)

    @classmethod
    def CollectSetting(cls, chamber_number: int, row_number: int) -> Collectsettings:
        item: cls = cls.getModel(chamber_number, row_number)
        if item is None:
            return None
        return Collectsettings.Primary(item.CollectSettingID)

    @classmethod
    def getModel(cls, chamber_number: int, row_number: int):
        item: cls = None
        try:
            dbController.initEngine()
            with Session(dbController.Engine) as session:
                item = session.query(cls).filter(cls.ChamberGroupNumber == chamber_number, cls.ChamberRowNumber == row_number).first()
                session.close()
        except Exception as e:
            from AppLog.logHelper import logDB
            logDB << AppUtils.toString(e)
        return item

    @classmethod
    def getSlide(cls, chamber_number: int, row_number: int):
        item: cls
        try:
            dbController.initEngine()
            with Session(dbController.Engine) as session:
                item = session.query(cls).filter(cls.ChamberGroupNumber == chamber_number, cls.ChamberRowNumber == row_number).first()
                session.close()
        except Exception as e:
            from AppLog.logHelper import logDB
            logDB << AppUtils.toString(e)
            item = cls.instance()
        return item

    @property
    def hasID                      (self) -> bool:
        return hasattr(self, 'ID'                 ) and isinstance(self.ID                      , int    )

    @property
    def hasChamberGroupNumber      (self) -> bool:
        return hasattr(self, 'ChamberGroupNumber' ) and isinstance(self.ChamberGroupNumber      , int    )

    @property
    def hasChamberRowNumber        (self) -> bool:
        return hasattr(self, 'ChamberRowNumber'   ) and isinstance(self.ChamberRowNumber        , int    )

    @property
    def hasTemplateID              (self) -> bool:
        return hasattr(self, 'TemplateID'         ) and isinstance(self.TemplateID              , int    )

    @property
    def hasPreviewSettingID        (self) -> bool:
        return hasattr(self, 'PreviewSettingID'   ) and isinstance(self.PreviewSettingID        , int    )

    @property
    def hasCollectSettingID        (self) -> bool:
        return hasattr(self, 'CollectSettingID'   ) and isinstance(self.CollectSettingID        , int    )

    @property
    def hasCodeString              (self) -> bool:
        return hasattr(self, 'CodeString'         ) and isinstance(self.CodeString              , str    )

    @property
    def hasSpecimenNumber          (self) -> bool:
        return hasattr(self, 'SpecimenNumber'     ) and isinstance(self.SpecimenNumber          , str    )

    @property
    def hasSlideNumber             (self) -> bool:
        return hasattr(self, 'SlideNumber'        ) and isinstance(self.SlideNumber             , str    )

    @property
    def hasLineNumber              (self) -> bool:
        return hasattr(self, 'LineNumber'         ) and isinstance(self.LineNumber              , str    )

    @property
    def hasSavePath                (self) -> bool:
        return hasattr(self, 'SavePath'           ) and isinstance(self.SavePath                , str    )

    @property
    def hasUiIndex                 (self) -> bool:
        return hasattr(self, 'uiIndex') and isinstance(self.uiIndex, str)

    @property
    def hasCustomSpecimen(self) -> bool:
        return hasattr(self, 'CustomSpecimen') and isinstance(self.CustomSpecimen, str)
