From d4aab6891dcf1e7f6bd7b1dfb2a8e7f5348c69f2 Mon Sep 17 00:00:00 2001
From: stedif <stefano.difrischia@inaf.it>
Date: Tue, 14 Dec 2021 16:21:38 +0100
Subject: [PATCH] L2SS-528: implement inheritance

---
 .../toolkit/archiver_base_ts.py               |  56 ++---
 .../tangostationcontrol/toolkit/retriever.py  | 234 +++++++++++-------
 2 files changed, 175 insertions(+), 115 deletions(-)

diff --git a/tangostationcontrol/tangostationcontrol/toolkit/archiver_base_ts.py b/tangostationcontrol/tangostationcontrol/toolkit/archiver_base_ts.py
index f73ca2abc..048077723 100644
--- a/tangostationcontrol/tangostationcontrol/toolkit/archiver_base_ts.py
+++ b/tangostationcontrol/tangostationcontrol/toolkit/archiver_base_ts.py
@@ -90,7 +90,7 @@ class Scalar_Boolean(Scalar):
     value_w = Column(Boolean)
 
     def __repr__(self):
-        return f"<Scalar_Boolean(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Boolean(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Double(Scalar):
     """
@@ -102,7 +102,7 @@ class Scalar_Double(Scalar):
     value_w = Column(FLOAT)
 
     def __repr__(self):
-        return f"<Scalar_Double(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Double(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Encoded(Scalar):
     """
@@ -114,7 +114,7 @@ class Scalar_Encoded(Scalar):
     value_w = Column(BYTEA)
 
     def __repr__(self):
-        return f"<Scalar_Encoded(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Encoded(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Enum(Scalar):
     """
@@ -128,7 +128,7 @@ class Scalar_Enum(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_Enum(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r_label='{self.value_r_label}',value_r='{self.value_r}',value_w_label='{self.value_w_label},value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Enum(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r_label='{self.value_r_label}',value_r='{self.value_r}',value_w_label='{self.value_w_label}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Float(Scalar):
     """
@@ -140,7 +140,7 @@ class Scalar_Float(Scalar):
     value_w = Column(FLOAT)
 
     def __repr__(self):
-        return f"<Scalar_Float(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Float(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Long(Scalar):
     """
@@ -152,7 +152,7 @@ class Scalar_Long(Scalar):
     value_w = Column(INT4RANGE)
 
     def __repr__(self):
-        return f"<Scalar_Long(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Long(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Long64(Scalar):
     """
@@ -164,7 +164,7 @@ class Scalar_Long64(Scalar):
     value_w = Column(INT8RANGE)
 
     def __repr__(self):
-        return f"<Scalar_Long64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Long64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_Short(Scalar):
     """
@@ -176,7 +176,7 @@ class Scalar_Short(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_Short(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_Short(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_State(Scalar):
     """
@@ -188,7 +188,7 @@ class Scalar_State(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_State(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_State(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_String(Scalar):
     """
@@ -200,7 +200,7 @@ class Scalar_String(Scalar):
     value_w = Column(TEXT)
 
     def __repr__(self):
-        return f"<Scalar_String(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_String(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_UChar(Scalar):
     """
@@ -212,7 +212,7 @@ class Scalar_UChar(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_UChar(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_UChar(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_ULong(Scalar):
     """
@@ -224,7 +224,7 @@ class Scalar_ULong(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_ULong(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_ULong(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_ULong64(Scalar):
     """
@@ -236,7 +236,7 @@ class Scalar_ULong64(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_ULong64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_ULong64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Scalar_UShort(Scalar):
     """
@@ -248,7 +248,7 @@ class Scalar_UShort(Scalar):
     value_w = Column(INTEGER)
 
     def __repr__(self):
-        return f"<Scalar_UShort(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Scalar_UShort(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array(Base):
     """
@@ -274,7 +274,7 @@ class Array_Boolean(Array):
     value_w = Column(ARRAY(Boolean))
 
     def __repr__(self):
-        return f"<Array_Boolean(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Boolean(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Double(Array):
     """
@@ -286,7 +286,7 @@ class Array_Double(Array):
     value_w = Column(ARRAY(FLOAT))
 
     def __repr__(self):
-        return f"<Array_Double(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Double(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Encoded(Array):
     """
@@ -298,7 +298,7 @@ class Array_Encoded(Array):
     value_w = Column(ARRAY(BYTEA))
 
     def __repr__(self):
-        return f"<Array_Encoded(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Encoded(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Enum(Array):
     """
@@ -312,7 +312,7 @@ class Array_Enum(Array):
     value_w = Column(ARRAY(INTEGER))
 
     def __repr__(self):
-        return f"<Array_Enum(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r_label='{self.value_r_label}',value_r='{self.value_r}',value_w_label='{self.value_w_label},value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Enum(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r_label='{self.value_r_label}',value_r='{self.value_r}',value_w_label='{self.value_w_label}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Float(Array):
     """
@@ -324,7 +324,7 @@ class Array_Float(Array):
     value_w = Column(ARRAY(FLOAT))
 
     def __repr__(self):
-        return f"<Array_Float(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Float(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Long(Array):
     """
@@ -336,7 +336,7 @@ class Array_Long(Array):
     value_w = Column(ARRAY(INT4RANGE))
 
     def __repr__(self):
-        return f"<Array_Long(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Long(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Long64(Array):
     """
@@ -348,7 +348,7 @@ class Array_Long64(Array):
     value_w = Column(ARRAY(INT8RANGE))
 
     def __repr__(self):
-        return f"<Array_Long64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Long64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_Short(Array):
     """
@@ -360,7 +360,7 @@ class Array_Short(Array):
     value_w = Column(ARRAY(INTEGER))
 
     def __repr__(self):
-        return f"<Array_Short(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_Short(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_State(Array):
     """
@@ -372,7 +372,7 @@ class Array_State(Array):
     value_w = Column(ARRAY(INT4RANGE))
 
     def __repr__(self):
-        return f"<Array_State(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_State(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_String(Array):
     """
@@ -384,7 +384,7 @@ class Array_String(Array):
     value_w = Column(ARRAY(TEXT))
 
     def __repr__(self):
-        return f"<Array_String(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_String(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_UChar(Array):
     """
@@ -396,7 +396,7 @@ class Array_UChar(Array):
     value_w = Column(ARRAY(INTEGER))
 
     def __repr__(self):
-        return f"<Array_UChar(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_UChar(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_ULong(Array):
     """
@@ -408,7 +408,7 @@ class Array_ULong(Array):
     value_w = Column(ARRAY(INTEGER))
 
     def __repr__(self):
-        return f"<Array_ULong(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_ULong(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_ULong64(Array):
     """
@@ -420,7 +420,7 @@ class Array_ULong64(Array):
     value_w = Column(ARRAY(INTEGER))
 
     def __repr__(self):
-        return f"<Array_ULong64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_ULong64(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 class Array_UShort(Array):
     """
@@ -432,7 +432,7 @@ class Array_UShort(Array):
     value_w = Column(ARRAY(INTEGER))
 
     def __repr__(self):
-        return f"<Array_UShort(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w},quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
+        return f"<Array_UShort(att_conf_id='{self.att_conf_id}',data_time='{self.data_time}',value_r='{self.value_r}',value_w='{self.value_w}',quality='{self.quality}',att_error_desc_id='{self.att_error_desc_id}',details='{self.details}')>"
 
 def get_class_by_tablename(tablename: str):
     """
diff --git a/tangostationcontrol/tangostationcontrol/toolkit/retriever.py b/tangostationcontrol/tangostationcontrol/toolkit/retriever.py
index 2905abee5..b84802cde 100644
--- a/tangostationcontrol/tangostationcontrol/toolkit/retriever.py
+++ b/tangostationcontrol/tangostationcontrol/toolkit/retriever.py
@@ -13,7 +13,7 @@ import numpy
 
 class Retriever(ABC):
     """
-    The Retriever class implements retrieve operations on a given DBMS
+    The Retriever abstract class implements retrieve operations on a given DBMS
     """
     
     def get_db_credentials(self):
@@ -38,6 +38,14 @@ class Retriever(ABC):
         engine = create_engine(connection_string)
         Session = sessionmaker(bind=engine)
         return Session
+    
+    @abstractmethod
+    def set_archiver_base(self):
+        return
+   
+    @abstractmethod
+    def connect_to_archiving_db(self):
+        return
 
     def get_all_archived_attributes(self):
         """
@@ -70,67 +78,19 @@ class Retriever(ABC):
             raise Exception(f"Attribute {attribute_fqname} not found!") from e
         except NoResultFound as e:
             raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
-
-    def get_attribute_datatype(self,attribute_fqname: str):
-        """
-        Takes as input the fully-qualified name of an attribute and returns its Data-Type.
-        Data Type name indicates the type (e.g. string, int, ...) and the read/write property. The name is used
-        as DB table name suffix in which values are stored.
-        """
-        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
-        try:
-            if self.dbms=='mysql': 
-                result = self.session.query(self.ab.DataType.data_type).join(self.ab.Attribute,self.ab.Attribute.att_conf_data_type_id==self.ab.DataType.att_conf_data_type_id).\
-                            filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
-            elif self.dbms=='postgres':
-                result = self.session.query(self.ab.DataType.type).join(self.ab.Attribute,self.ab.Attribute.att_conf_type_id==self.ab.DataType.att_conf_type_id).\
-                            filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
-            return result[0]
-        except TypeError as e:
-            raise Exception(f"Attribute not {attribute_fqname} found!") from e
-        except NoResultFound as e:
-            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
-
-    def get_attribute_format(self,attribute_fqname: str):
-        """
-        Takes as input the fully-qualified name of an attribute and returns its format.
-        Formats are basically three: Scalar, Spectrum and Image.
-        * Works only for POSTGRESQL * 
-        """
-        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
-        try:
-            result = self.session.query(self.ab.Format.format).join(self.ab.Attribute,self.ab.Attribute.att_conf_format_id==self.ab.Format.att_conf_format_id).\
-                filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
-            return result[0]
-        except TypeError as e:
-            raise Exception("Attribute not found!") from e
-        except NoResultFound as e:
-            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
     
-    def get_attribute_tablename(self,attribute_fqname: str):
-        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
-        try:
-            result = self.session.query(self.ab.Attribute.table_name).filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, \
-                                    self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
-            return result[0]
-        except TypeError as e:
-            raise Exception("Attribute not found!") from e
-        except NoResultFound as e:
-            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
+    @abstractmethod
+    def get_attribute_datatype(self,attribute_fqname: str):
+        return
     
-    def get_attribute_value_by_hours(self,attribute_fqname: str, hours: float = 1.0):
+    def get_attribute_value_by_hours(self, attribute_fqname: str, hours: float, tablename:str):
         """
         Takes as input the attribute fully-qualified name and the number of past hours since the actual time 
         (e.g. hours=1 retrieves values in the last hour, hours=8.5 retrieves values in the last eight hours and half).
         Returns a list of timestamps and a list of values
         """
         attr_id = self.get_attribute_id(attribute_fqname)
-        attr_datatype = self.get_attribute_datatype(attribute_fqname)
         # Retrieves the class that maps the DB table given the tablename
-        if self.dbms=='mysql':
-            tablename = f"att_{attr_datatype}"          
-        elif self.dbms=='postgres':
-            tablename = self.get_attribute_tablename(attribute_fqname)
         base_class = self.ab.get_class_by_tablename(tablename)    
         # Retrieves the timestamp 
         time_now = datetime.now()
@@ -147,19 +107,14 @@ class Retriever(ABC):
             raise Exception(f"Empty result: Attribute {attribute_fqname} not found") from e
         return result
 
-    def get_attribute_value_by_interval(self,attribute_fqname: str, start_time: datetime, stop_time: datetime):
+    def get_attribute_value_by_interval(self,attribute_fqname: str, start_time: datetime, stop_time: datetime, tablename:str):
         """
         Takes as input the attribute name and a certain starting and ending point-time. 
         The datetime format is pretty flexible (e.g. "YYYY-MM-dd hh:mm:ss").
         Returns a list of timestamps and a list of values
         """
         attr_id = self.get_attribute_id(attribute_fqname)
-        attr_datatype = self.get_attribute_datatype(attribute_fqname)
         # Retrieves the class that maps the DB table given the tablename
-        if self.dbms=='mysql':
-            tablename = f"att_{attr_datatype}"           
-        elif self.dbms=='postgres':
-            tablename = self.get_attribute_tablename(attribute_fqname)
         base_class = self.ab.get_class_by_tablename(tablename)
         try:
             result = self.session.query(base_class).\
@@ -169,7 +124,73 @@ class Retriever(ABC):
         except AttributeError as e:
             raise Exception(f"Empty result: Attribute {attribute_fqname} not found") from e
         return result
+
+class RetrieverMySQL(Retriever):
+    
+    def __init__(self, cm_name: str = 'archiving/hdbpp/confmanager01'):
+        self.cm_name = cm_name
+        self.session = self.connect_to_archiving_db()
+        self.ab = self.set_archiver_base()
+    
+    def connect_to_archiving_db(self):
+        """
+        Returns a session to a MySQL DBMS using default credentials.
+        """
+        host,dbname,port,user,pw = super().get_db_credentials()
+        # Set sqlalchemy library connection
+        if host=='archiver-maria-db':
+            libname = 'mysql+pymysql'         
+        else:
+            raise ValueError(f"Invalid hostname: {host}")
+        Session = super().create_session(libname,user,pw,host,port,dbname)
+        return Session()
     
+    def set_archiver_base(self):
+        """
+        Sets the right mapper class following the DBMS connection
+        """    
+        return importlib.import_module('.archiver_base_mysql', package=__package__)
+    
+    def get_attribute_datatype(self,attribute_fqname: str):
+        """
+        Takes as input the fully-qualified name of an attribute and returns its Data-Type.
+        Data Type name indicates the type (e.g. string, int, ...) and the read/write property. The name is used
+        as DB table name suffix in which values are stored.
+        """
+        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
+        try:
+            result = self.session.query(self.ab.DataType.data_type).join(self.ab.Attribute,self.ab.Attribute.att_conf_data_type_id==self.ab.DataType.att_conf_data_type_id).\
+                        filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
+            return result[0]
+        except TypeError as e:
+            raise Exception(f"Attribute not {attribute_fqname} found!") from e
+        except NoResultFound as e:
+            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
+    
+    def get_attribute_value_by_hours(self,attribute_fqname: str, hours: float = 1.0):
+        """
+        Takes as input the attribute fully-qualified name and the number of past hours since the actual time 
+        (e.g. hours=1 retrieves values in the last hour, hours=8.5 retrieves values in the last eight hours and half).
+        Returns a list of timestamps and a list of values
+        """
+        attr_datatype = self.get_attribute_datatype(attribute_fqname)
+        # Retrieves the class that maps the DB table given the tablename
+        tablename = f"att_{attr_datatype}"
+        return super().get_attribute_value_by_hours(attribute_fqname,hours,tablename) 
+
+
+    def get_attribute_value_by_interval(self,attribute_fqname: str, start_time: datetime, stop_time: datetime):
+        """
+        Takes as input the attribute name and a certain starting and ending point-time. 
+        The datetime format is pretty flexible (e.g. "YYYY-MM-dd hh:mm:ss").
+        Returns a list of timestamps and a list of values
+        """
+        attr_datatype = self.get_attribute_datatype(attribute_fqname)
+        # Retrieves the class that maps the DB table given the tablename
+        tablename = f"att_{attr_datatype}"           
+        return super().get_attribute_value_by_interval(attribute_fqname,start_time,stop_time,tablename)
+    
+    # DRAFT #
     def get_masked_fpga_temp(self,start_time: datetime, stop_time: datetime,temp_attr_name:str='stat/sdp/1/fpga_temp_r',
                     mask_attr_name:str='stat/sdp/1/tr_fpga_mask_r'):
         """
@@ -200,11 +221,11 @@ class Retriever(ABC):
         masked_values = numpy.ma.masked_array(temp_array_values,mask=numpy.invert(mask_array_values.astype(bool)))
         return masked_values, mask_values, temp_values
 
-class Retriever_MySQL(Retriever):
+class RetrieverTimescale(Retriever):
     
-    def __init__(self, cm_name: str = 'archiving/hdbpp/confmanager01'):
+    def __init__(self, cm_name: str = 'archiving/hdbppts/confmanager01'):
         self.cm_name = cm_name
-        self.session, self.dbms = self.connect_to_archiving_db()
+        self.session = self.connect_to_archiving_db()
         self.ab = self.set_archiver_base()
     
     def connect_to_archiving_db(self):
@@ -212,9 +233,9 @@ class Retriever_MySQL(Retriever):
         Returns a session to a MySQL DBMS using default credentials.
         """
         host,dbname,port,user,pw = super().get_db_credentials()
-        # Set sqlalchemy library connection
-        if host=='archiver-maria-db':
-            libname = 'mysql+pymysql'         
+        # Set sqlalchemy library connection        
+        if host=='archiver-timescale':
+            libname = 'postgresql+psycopg2'
         else:
             raise ValueError(f"Invalid hostname: {host}")
         Session = super().create_session(libname,user,pw,host,port,dbname)
@@ -223,32 +244,71 @@ class Retriever_MySQL(Retriever):
     def set_archiver_base(self):
         """
         Sets the right mapper class following the DBMS connection
-        """    
-        return importlib.import_module('.archiver_base_mysql', package=__package__)
-
-class Retriever_Timescale(Retriever):
+        """
+        return importlib.import_module('.archiver_base_ts', package=__package__)
     
-    def __init__(self, cm_name: str = 'archiving/hdbppts/confmanager01'):
-        self.cm_name = cm_name
-        self.session, self.dbms = self.connect_to_archiving_db()
-        self.ab = self.set_archiver_base()
+    def get_attribute_datatype(self,attribute_fqname: str):
+        """
+        Takes as input the fully-qualified name of an attribute and returns its Data-Type.
+        Data Type name indicates the type (e.g. string, int, ...) and the read/write property. The name is used
+        as DB table name suffix in which values are stored.
+        """
+        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
+        try:
+            result = self.session.query(self.ab.DataType.type).join(self.ab.Attribute,self.ab.Attribute.att_conf_type_id==self.ab.DataType.att_conf_type_id).\
+                            filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
+            return result[0]
+        except TypeError as e:
+            raise Exception(f"Attribute not {attribute_fqname} found!") from e
+        except NoResultFound as e:
+            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
     
-    def connect_to_archiving_db(self):
+    def get_attribute_format(self,attribute_fqname: str):
         """
-        Returns a session to a MySQL DBMS using default credentials.
+        Takes as input the fully-qualified name of an attribute and returns its format.
+        Formats are basically three: Scalar, Spectrum and Image.
+        * Works only for POSTGRESQL * 
         """
-        host,dbname,port,user,pw = super().get_db_credentials()
-        # Set sqlalchemy library connection        
-        if host=='archiver-timescale':
-            libname = 'postgresql+psycopg2'
-        else:
-            raise ValueError(f"Invalid hostname: {host}")
-        Session = super().create_session(libname,user,pw,host,port,dbname)
-        return Session()
+        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
+        try:
+            result = self.session.query(self.ab.Format.format).join(self.ab.Attribute,self.ab.Attribute.att_conf_format_id==self.ab.Format.att_conf_format_id).\
+                filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
+            return result[0]
+        except TypeError as e:
+            raise Exception("Attribute not found!") from e
+        except NoResultFound as e:
+            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
     
-    def set_archiver_base(self):
+    def get_attribute_tablename(self,attribute_fqname: str):
         """
-        Sets the right mapper class following the DBMS connection
+        Takes as input the fully-qualified name of an attribute and returns the tablename where it is stored.
+        * Works only for POSTGRESQL * 
         """
-        return importlib.import_module('.archiver_base_ts', package=__package__)
-        
\ No newline at end of file
+        domain, family, member, name = split_tango_name(attribute_fqname,"attribute")
+        try:
+            result = self.session.query(self.ab.Attribute.table_name).filter(and_(self.ab.Attribute.domain == domain, self.ab.Attribute.family == family, \
+                                    self.ab.Attribute.member == member, self.ab.Attribute.name == name)).one()
+            return result[0]
+        except TypeError as e:
+            raise Exception("Attribute not found!") from e
+        except NoResultFound as e:
+            raise Exception(f"No records of attribute {attribute_fqname} found in DB") from e
+    
+    def get_attribute_value_by_hours(self, attribute_fqname: str, hours: float = 1.0):
+        """
+        Takes as input the attribute fully-qualified name and the number of past hours since the actual time 
+        (e.g. hours=1 retrieves values in the last hour, hours=8.5 retrieves values in the last eight hours and half).
+        Returns a list of timestamps and a list of values
+        """
+        tablename = self.get_attribute_tablename(attribute_fqname)
+        return super().get_attribute_value_by_hours(attribute_fqname,hours,tablename)
+    
+    def get_attribute_value_by_interval(self,attribute_fqname: str, start_time: datetime, stop_time: datetime):
+        """
+        Takes as input the attribute name and a certain starting and ending point-time. 
+        The datetime format is pretty flexible (e.g. "YYYY-MM-dd hh:mm:ss").
+        Returns a list of timestamps and a list of values
+        """
+        tablename = self.get_attribute_tablename(attribute_fqname)
+        return super().get_attribute_value_by_interval(attribute_fqname,start_time,stop_time,tablename)
+    
\ No newline at end of file
-- 
GitLab