blob: df4dadd197c29bdf21bfc16ab569aa94c9900168 [file] [log] [blame]
"""
Copyright (c) 2019, OptoFidelity OY
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from sqlalchemy.orm import joinedload
from datetime import datetime
import numpy as np
import math
import time
import traceback
import TPPTAnalysisSW.plotinfo as plotinfo
import TPPTAnalysisSW.analyzers as analyzers
from .measurementdb import get_database, TestItem, TestResult
from .settings import settings
from TPPTAnalysisSW.sqluploader import Session, is_sql_uploader_initialized
import TPPTAnalysisSW.test_refs as test_refs
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
# generator functions for the different images - will be filled by the decorator
# This is a directory id -> generating function
_test_generators = {}
def timestr_to_datetime(timestr):
"""
Convert time string that has been generated in TPPT scripts via time.strftime to datetime object.
:param timestr: Time string.
"""
return datetime.strptime(timestr, TIME_FORMAT)
# Decorator class
class testclasscreator(object):
""" Creates test reports and images for the test. Gets the test type id as parameter """
_generators = {}
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
pass
def __call__(self, f):
global _test_generators
for arg in self.args:
_test_generators[arg] = f
return f
class TestBase(object):
"""Base class for all test objects. """
def __init__(self, test_item_row, *args, **kwargs):
""" Initialize the common report parameters """
# Create dictionary for testsession parameters
self.test_item = {column.name: getattr(test_item_row, column.name) for column in test_item_row.__table__.columns}
self.test_item['test_type_name'] = test_item_row.type.name
self.test_id = self.test_item['id']
self.reporting_rates = {}
self.verdict = "Pass"
# Set in child class __init__ to allow exists_in_database_with_start_time to be called easily from child classes
self.sql_summary_class = None
# Create dictionary for test session parameters
session = test_item_row.testsession
self.testsession = {column.name: getattr(session, column.name) for column in session.__table__.columns}
dut = test_item_row.dut
self.dut = {column.name: getattr(dut, column.name) for column in dut.__table__.columns}
@staticmethod
def create(test_id, dbsession=None, **kwargs):
""" Create a correct subclass for given test id """
global _test_generators
if dbsession is None:
dbsession = get_database().session()
test = dbsession.query(TestItem).options(joinedload(TestItem.testsession)).\
options(joinedload(TestItem.type)).\
filter(TestItem.id==test_id).first()
cache = True
test_id = str(test_id)
if test.testtype_id in _test_generators:
if test_id not in test_refs.testclass_refs:
generator = _test_generators[test.testtype_id](test, **kwargs)
test_refs.testclass_refs[test_id] = generator
cache = False
if test.testtype_id != 15:
cache = False
return test_refs.testclass_refs[test_id], cache
def get_test_item(self, dbsession=None):
if dbsession is None:
dbsession = get_database().session()
test = dbsession.query(TestItem).options(joinedload(TestItem.testsession)). \
options(joinedload(TestItem.type)). \
filter(TestItem.id == self.test_id).first()
return test
def get_test_session(self, dbsession=None):
if dbsession is None:
dbsession = get_database().session()
test = dbsession.query(TestItem).options(joinedload(TestItem.testsession)). \
options(joinedload(TestItem.testsession)). \
filter(TestItem.id == self.test_id).first()
return test.testsession
def get_session_and_dutinfo(self):
""" Gets db session and dutinfo objects. """
dbsession = get_database().session()
dutinfo = plotinfo.TestDUTInfo(testdut_id=self.dut['id'], dbsession=dbsession)
return dbsession, dutinfo
def disable_upload_button_if_already_uploaded(self, template_params):
"""
Called from the test.createreport() for each test class
:param template_params: template parameters for the test case
"""
test_item = self.get_test_item()
time_test_start = timestr_to_datetime(test_item.starttime)
already_exists = self.exists_in_database_with_start_time(time_test_start)
# Using pyattrs in the test_configured_body.html this disables the upload button if necessary
template_params['upload_button_attributes'] = {'disabled': 'true'} if already_exists else {}
def exists_in_database_with_start_time(self, time_test_start):
"""
Assumes that time_test_start is a unique identifier for test items regardless of sqlite database id mixups
:param time_test_start: Timestamp for the TestItem.starttime in measurementdb
:return: True if rows found for the test class in database, False otherwise
"""
# If DB is not initialized, return True (return value should not be used in this case)
if not is_sql_uploader_initialized():
return True
session = Session()
try:
query = session.query(self.sql_summary_class)\
.filter(self.sql_summary_class.time_test_start == time_test_start)
exists = session.query(query.exists()).scalar()
finally:
session.close()
return exists
# These functions must be implemented in the child classes
def runanalysis(self, *args, **kwargs):
raise NotImplementedError("Method not implemented")
def clearanalysis(self, *args, **kwargs):
raise NotImplementedError("Method not implemented")
def createreport(self, *args, **kwargs):
raise NotImplementedError("Method not implemented")
def createimage(self, *args, **kwargs):
raise NotImplementedError("Method not implemented")
def create_common_templateparams(self, *args, **kwargs):
templateParams = {}
templateParams["test_item"] = self.test_item
templateParams["testsession"] = self.testsession
templateParams["dut"] = self.dut
templateParams["test_id"] = self.test_item['id']
templateParams["test_type_name"] = self.test_item['test_type_name']
templateParams["curtime"] = time.time()
templateParams["settings"] = settings
templateParams["sql_uploader_initialized"] = is_sql_uploader_initialized()
templateParams["kwargs"] = kwargs
return templateParams
@staticmethod
def evaluateresult(test_id, dbsession=None, recalculate=True):
if dbsession is None:
session = get_database().session()
else:
session = dbsession
result = None
if recalculate:
testclass = TestBase.create(test_id, dbsession=session)[0]
try:
if recalculate:
if testclass is not None:
result = testclass.runanalysis()
else:
# Test class not found!
print("Test class not found for test " + str(test_id))
result = "Error"
dbresult = TestResult()
dbresult.test_id = test_id
if recalculate:
dbresult.result = result
else:
result = "Requires recalculate"
dbresult.result = result
dbresult.calculated = datetime.now()
session.query(TestResult).filter(TestResult.test_id == test_id).delete()
session.add(dbresult)
session.commit()
except Exception as e:
print(traceback.format_exc())
result = "Error"
if dbsession is None:
session.close()
return result