diff --git a/.gitignore b/.gitignore index fb548b8..276a6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ test_results # PyCharm .idea/ -# HLM_PV_Import +# hlm_pv_import logs/ # C extensions diff --git a/HlmManager.py b/HlmManager.py index bae1751..b1f950d 100644 --- a/HlmManager.py +++ b/HlmManager.py @@ -1,4 +1,4 @@ -from ServiceManager.__main__ import main +from service_manager.__main__ import main if __name__ == '__main__': main() diff --git a/HlmService.py b/HlmService.py index 4008bc5..26b4204 100644 --- a/HlmService.py +++ b/HlmService.py @@ -5,9 +5,9 @@ import win32service import win32serviceutil -import HLM_PV_Import.__main__ as main_ -from HLM_PV_Import.settings import Service -from HLM_PV_Import.logger import log_exception, logger +import hlm_pv_import.__main__ as main_ +from hlm_pv_import.settings import Service +from hlm_pv_import.logger import log_exception, logger class PVImportService(win32serviceutil.ServiceFramework): diff --git a/Jenkinsfile b/Jenkinsfile index eb61bb4..a3ccb5c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -70,7 +70,7 @@ pipeline { steps { bat """ call "%VENV_PATH%\\Scripts\\activate.bat" - python -m pylint ServiceManager HLM_PV_Import --output-format=parseable --reports=no module --exit-zero > pylint.log + python -m pylint service_manager hlm_pv_import --output-format=parseable --reports=no module --exit-zero > pylint.log echo pylint exited with %errorlevel% """ echo "linting Success, Generating Report" diff --git a/HLM_PV_Import/__init__.py b/hlm_pv_import/__init__.py similarity index 100% rename from HLM_PV_Import/__init__.py rename to hlm_pv_import/__init__.py diff --git a/HLM_PV_Import/__main__.py b/hlm_pv_import/__main__.py similarity index 72% rename from HLM_PV_Import/__main__.py rename to hlm_pv_import/__main__.py index 909580c..99563f0 100644 --- a/HLM_PV_Import/__main__.py +++ b/hlm_pv_import/__main__.py @@ -2,13 +2,13 @@ Helium Level Monitoring Project - HeRecovery Database PV Import """ -from HLM_PV_Import.ca_wrapper import PvMonitors -from HLM_PV_Import.user_config import UserConfig -from HLM_PV_Import.pv_import import PvImport -from HLM_PV_Import.settings import CA, HEDB -from HLM_PV_Import.logger import logger -from HLM_PV_Import.db_func import db_connect, check_db_connection -from HLM_PV_Import.external_pvs import MercuryPVs +from hlm_pv_import.ca_wrapper import PvMonitors +from hlm_pv_import.user_config import UserConfig +from hlm_pv_import.pv_import import PvImport +from hlm_pv_import.settings import CA, HEDB +from hlm_pv_import.logger import logger +from hlm_pv_import.db_func import db_connect, check_db_connection +from hlm_pv_import.external_pvs import MercuryPVs from shared.db_models import initialize_database import os import sys @@ -43,9 +43,9 @@ def main(): # Set up monitoring and fetching of the PV data pv_monitors = PvMonitors(pv_list) - # Initialize and set-up the PV import in charge of preparing the PV data, handling logging periods & tasks, - # running content checks for the user config, and looping through each record every few seconds to check for - # records scheduled to be updated with a new measurement. + # Initialize and set-up the PV import in charge of preparing the PV data, handling logging + # periods & tasks, running content checks for the user config, and looping through each record + # every few seconds to check for records scheduled to be updated with a new measurement. this.pv_import = PvImport(pv_monitors, config, external_pvs_configs) # Start the monitors and continuously store the PV data received on every update diff --git a/HLM_PV_Import/ca_wrapper.py b/hlm_pv_import/ca_wrapper.py similarity index 91% rename from HLM_PV_Import/ca_wrapper.py rename to hlm_pv_import/ca_wrapper.py index 13fc28d..c1bea5a 100644 --- a/HLM_PV_Import/ca_wrapper.py +++ b/hlm_pv_import/ca_wrapper.py @@ -8,14 +8,15 @@ from caproto.sync.client import read from caproto import CaprotoError -from HLM_PV_Import.logger import pv_logger, logger -from HLM_PV_Import.settings import CA -from HLM_PV_Import.utils import dehex_and_decompress, ints_to_string +from hlm_pv_import.logger import pv_logger, logger +from hlm_pv_import.settings import CA +from hlm_pv_import.utils import dehex_and_decompress, ints_to_string # Default timeout for reading a PV TIMEOUT = CA.CONN_TIMEOUT -# time in s after which PV data is considered stale and will no longer be considered when adding a measurement +# time in s after which PV data is considered stale and will no longer be considered when adding +# a measurement STALE_AGE = CA.STALE_AFTER # PV that contains the instrument list @@ -88,9 +89,11 @@ def _callback_f(self, sub, response): Stash the PV Name/Value results in the data dictionary, and the Name/Last Update in another. Args: - sub (caproto.threading.client.Subscription): The subscription, also containing the pertinent PV + sub (caproto.threading.client.Subscription): The subscription, also containing the + pertinent PV and its name. - response (caproto._commands.EventAddResponse): The full response from the server, which includes data + response (caproto._commands.EventAddResponse): The full response from the server, + which includes data and any metadata. """ value = response.data[0] @@ -114,7 +117,8 @@ def start_monitors(self): def pv_data_is_stale(self, pv_name): """ - Checks whether a PVs data is stale or not, by looking at the time since its last update and the set length of + Checks whether a PVs data is stale or not, by looking at the time since its last update + and the set length of time after which a PV is considered stale. Args: diff --git a/HLM_PV_Import/db_func.py b/hlm_pv_import/db_func.py similarity index 85% rename from HLM_PV_Import/db_func.py rename to hlm_pv_import/db_func.py index 68b7a4b..0fa4104 100644 --- a/HLM_PV_Import/db_func.py +++ b/hlm_pv_import/db_func.py @@ -1,3 +1,4 @@ +# pylint: disable=E1101 import sys import time from datetime import datetime @@ -8,15 +9,17 @@ from shared.const import DBTypeIDs, DBClassIDs from shared.db_models import * from shared.utils import get_object_module -from HLM_PV_Import.logger import logger, db_logger, log_exception +from hlm_pv_import.logger import logger, db_logger, log_exception RECONNECT_ATTEMPTS_MAX = 1000 RECONNECT_WAIT = 5 # base wait time between attempts in seconds RECONNECT_MAX_WAIT_TIME = 14400 # maximum wait time between attempts, in sec -def increase_reconnect_wait_time(current_wait): # increasing wait time in s between attempts for each failed attempt - return current_wait*2 if current_wait*2 < RECONNECT_MAX_WAIT_TIME else RECONNECT_MAX_WAIT_TIME +# increasing wait time in s between attempts for each failed attempt +def increase_reconnect_wait_time(current_wait): + double_current = current_wait * 2 + return double_current if double_current < RECONNECT_MAX_WAIT_TIME else RECONNECT_MAX_WAIT_TIME def db_connect(): @@ -40,8 +43,8 @@ def check_db_connection(attempt: int = 1, wait_until_reconnect: int = RECONNECT_ logger.error(conn_aborted) raise Exception(conn_aborted) else: - logger.error(f'Connection to the database could not be established, re-attempting to connect in ' - f'{wait_until_reconnect}s. (Attempt: {attempt})') + logger.error(f'Connection to the database could not be established,' + f' re-attempting to connect in {wait_until_reconnect}s. (Attempt: {attempt})') time.sleep(wait_until_reconnect) time_until_next_reconnect = increase_reconnect_wait_time(current_wait=wait_until_reconnect) db_connect() @@ -82,7 +85,8 @@ def add_measurement(object_id, mea_values: dict): Args: object_id (int): Record/Object id of the object the measurement is for. - mea_values (dict): A dict of the measurement values, max 5, in measurement_number(str)/pv_value pairs. + mea_values (dict): A dict of the measurement values, max 5, + in measurement_number(str)/pv_value pairs. """ obj = GamObject.get(GamObject.ob_id == object_id) obj_class_id = obj.ob_objecttype.ot_objectclass.oc_id @@ -109,15 +113,16 @@ def add_measurement(object_id, mea_values: dict): mea_bookingcode=0 # 0 = measurement is not from the balance program (HZB) ).execute() - logger.info(f'Added measurement {record_id} for {obj.ob_name} ({object_id}) with values: {dict(mea_values)}') + logger.info(f'Added measurement {record_id} for ' + f'{obj.ob_name} ({object_id}) with values: {dict(mea_values)}') # noinspection PyProtectedMember db_logger.info(f"Added record no. {record_id} to {GamMeasurement._meta.table_name}") def _generate_mea_comment(obj: GamObject, object_module: GamObject): """ - Check whether the object has a module, then generate the measurement comment and update the object ID to add - the measurement to. + Check whether the object has a module, then generate the measurement comment + and update the object ID to add the measurement to. Args: obj (GamObject): The object. @@ -132,8 +137,10 @@ def _generate_mea_comment(obj: GamObject, object_module: GamObject): # If object has a module, mention this in the mea. comment if object_module is not None: module_type = object_module.ob_objecttype.ot_id - module_name = "SLD" if module_type == DBTypeIDs.SLD else "GCM" if module_type == DBTypeIDs.GCM else "Module" - mea_comment = f'{module_name} for {obj.ob_id} "{obj.ob_name}" ({type_name} - {class_name}) via HLM PV IMPORT' + module_name = "SLD" if module_type == DBTypeIDs.SLD \ + else "GCM" if module_type == DBTypeIDs.GCM else "Module" + mea_comment = f'{module_name} for {obj.ob_id} "{obj.ob_name}"' \ + f' ({type_name} - {class_name}) via HLM PV IMPORT' else: mea_comment = f'"{obj.ob_name}" ({type_name} - {class_name}) via HLM PV IMPORT' @@ -146,7 +153,8 @@ def _calculate_mea_values(mea_obj_id: int, object_class_id: int, mea_values: dic Do any measurement values calculations (e.g. Revolutions to Liquid Litres for Gas Counters). Args: - mea_obj_id (int): The measurement object ID. If the object has a module, this is the module object ID. + mea_obj_id (int): The measurement object ID. If the object has a module, this is the module + object ID. object_class_id (int): The object class ID. mea_values (dict): Measurement values. @@ -192,7 +200,8 @@ def get_obj_id_and_create_if_not_exist(obj_name: str, type_id: int, comment: str """ obj_id = GamObject.select(GamObject.ob_id).where(GamObject.ob_name == obj_name).first() if obj_id is None: - added_obj_id = GamObject.insert(ob_name=obj_name, ob_objecttype=type_id, ob_comment=comment).execute() + added_obj_id = GamObject.insert(ob_name=obj_name, ob_objecttype=type_id, + ob_comment=comment).execute() db_logger.info(f'Created object no. {added_obj_id} ("{obj_name}") of type {type_id}.') return added_obj_id else: diff --git a/HLM_PV_Import/external_pvs.py b/hlm_pv_import/external_pvs.py similarity index 66% rename from HLM_PV_Import/external_pvs.py rename to hlm_pv_import/external_pvs.py index fe7aaf6..9f28694 100644 --- a/HLM_PV_Import/external_pvs.py +++ b/hlm_pv_import/external_pvs.py @@ -1,6 +1,6 @@ """ PVs that are not part of the Helium Recovery PLC """ -from HLM_PV_Import.ca_wrapper import get_instrument_list -from HLM_PV_Import.settings import DBTypeIDs +from hlm_pv_import.ca_wrapper import get_instrument_list +from hlm_pv_import.settings import DBTypeIDs class MercuryPVs: @@ -8,15 +8,18 @@ def __init__(self): self.full_inst_list = get_instrument_list() self.instruments = [x['name'] for x in self.full_inst_list] - self.ignored_instruments = ['DEMO', 'DETMON', 'RIKENFE', 'MUONFE', 'ENGINX', 'INES', 'NIMROD', 'SANDALS', - 'IMAT', 'ALF', 'CRISP', 'INTER', 'LOQ', 'SURF', 'TOSCA', 'VESUVIO'] + self.ignored_instruments = ['DEMO', 'DETMON', 'RIKENFE', 'MUONFE', 'ENGINX', 'INES', + 'NIMROD', 'SANDALS', 'IMAT', 'ALF', 'CRISP', 'INTER', 'LOQ', + 'SURF', 'TOSCA', 'VESUVIO'] # Add _SETUP instruments to ignored - self.ignored_instruments.extend([x['name'] for x in self.full_inst_list if '_SETUP' in x['name']]) + self.ignored_instruments.extend( + [x['name'] for x in self.full_inst_list if '_SETUP' in x['name']]) self.IOCs = ['MERCURY_01', 'MERCURY_02'] self.PVs = ['LEVEL:1:HELIUM'] # max 5 self.inst_to_check = list(set(self.instruments) ^ set(self.ignored_instruments)) - self.prefixes = {x['name']: x['pvPrefix'] for x in self.full_inst_list if x['name'] in self.inst_to_check} + self.prefixes = {x['name']: x['pvPrefix'] for x in self.full_inst_list if + x['name'] in self.inst_to_check} self.pv_config = self._get_config() @@ -32,4 +35,5 @@ def _get_config(self): return pv_config def get_full_pv_list(self): - return [f'{prefix}{ioc}:{pv}' for prefix in self.prefixes.values() for ioc in self.IOCs for pv in self.PVs] + return [f'{prefix}{ioc}:{pv}' for prefix in self.prefixes.values() for ioc in self.IOCs for + pv in self.PVs] diff --git a/HLM_PV_Import/logger.py b/hlm_pv_import/logger.py similarity index 98% rename from HLM_PV_Import/logger.py rename to hlm_pv_import/logger.py index c5b50f5..cc773b6 100644 --- a/HLM_PV_Import/logger.py +++ b/hlm_pv_import/logger.py @@ -1,7 +1,7 @@ import os import sys import logging.config -from HLM_PV_Import.settings import LoggingFiles +from hlm_pv_import.settings import LoggingFiles def setup_log_file(log_path): diff --git a/HLM_PV_Import/pv_import.py b/hlm_pv_import/pv_import.py similarity index 69% rename from HLM_PV_Import/pv_import.py rename to hlm_pv_import/pv_import.py index da7717a..e1ade6e 100644 --- a/HLM_PV_Import/pv_import.py +++ b/hlm_pv_import/pv_import.py @@ -1,13 +1,13 @@ -from HLM_PV_Import.ca_wrapper import PvMonitors -from HLM_PV_Import.user_config import UserConfig -from HLM_PV_Import.settings import PvImportConfig -from HLM_PV_Import.logger import logger, pv_logger -from HLM_PV_Import.settings import CA -from HLM_PV_Import.db_func import add_measurement, get_obj_id_and_create_if_not_exist +from hlm_pv_import.ca_wrapper import PvMonitors +from hlm_pv_import.user_config import UserConfig +from hlm_pv_import.settings import PvImportConfig +from hlm_pv_import.logger import logger, pv_logger +from hlm_pv_import.settings import CA +from hlm_pv_import.db_func import add_measurement, get_obj_id_and_create_if_not_exist from collections import defaultdict import time -LOOP_TIMER = PvImportConfig.LOOP_TIMER # The timer between each PV import loop +LOOP_TIMER = PvImportConfig.LOOP_TIMER # The timer between each PV import loop EXTERNAL_PVS_UPDATE_INTERVAL = 3600 EXTERNAL_PVS_TASK = 'External PVs' ONE_MINUTE_IN_SECONDS = 60 @@ -17,7 +17,8 @@ class PvImport: def __init__(self, pv_monitors: PvMonitors, user_config: UserConfig, external_pvs_list: list): self.pv_monitors = pv_monitors self.config = user_config - self.external_pvs_list = external_pvs_list # Configurations for PVs not part of the Helium Recovery PLC + self.external_pvs_list = external_pvs_list # Configurations for PVs not part of the + # Helium Recovery PLC self.tasks = {} self.running = False @@ -37,11 +38,14 @@ def start(self): # Helium Recovery PLC Measurements for object_id in self.config.object_ids: - # Check the object's next logging time in tasks, if not yet then go to next object_id + # Check the object's next logging time in tasks, if not yet then go to next + # object_id if self.tasks[object_id] > time.time(): continue - # If object is ready to be updated, set curr time + log period in minutes as next run, then proceed - self.tasks[object_id] = time.time() + (ONE_MINUTE_IN_SECONDS * self.config.logging_periods[object_id]) + # If object is ready to be updated, set curr time + log period in minutes as next + # run, then proceed + self.tasks[object_id] = time.time() + ( + ONE_MINUTE_IN_SECONDS * self.config.logging_periods[object_id]) # Get the object measurement PVs names object_meas = self.config.get_entry_measurement_pvs(object_id, full_names=True) @@ -52,7 +56,8 @@ def start(self): # If none of the measurement PVs values were found in the PV data, # skip to the next object. if all(value is None for value in mea_values.values()): - logger.warning(f'No PV values for measurement of object {object_id}, skipping. ') + logger.warning( + f'No PV values for measurement of object {object_id}, skipping. ') continue # Create a new measurement with the PV values for the object @@ -68,13 +73,16 @@ def start(self): for external_pvs_config in self.external_pvs_list: for obj_name, mea_pvs in external_pvs_config.pv_config.items(): - mea_values = self._get_mea_values({f'{i+1}': pv for i, pv in enumerate(mea_pvs)}, - ignore_stale_pvs=True) + mea_values = self._get_mea_values( + {f'{i + 1}': pv for i, pv in enumerate(mea_pvs)}, + ignore_stale_pvs=True) if all(value is None for value in mea_values.values()): continue comment = f'Non-PLC PVs ({external_pvs_config.name})' - obj_id = get_obj_id_and_create_if_not_exist(obj_name, external_pvs_config.objects_type, comment) + obj_id = get_obj_id_and_create_if_not_exist(obj_name, + external_pvs_config.objects_type, + comment) add_measurement(object_id=obj_id, mea_values=mea_values) @@ -86,7 +94,8 @@ def stop(self): def _get_mea_values(self, meas_pv_config: dict, ignore_stale_pvs: bool = False): """ - Iterate through the list of PVs, get the values from the PV monitor data dict, and add them to the + Iterate through the list of PVs, get the values from the PV monitor data dict, + and add them to the measurement values. Args: @@ -102,13 +111,16 @@ def _get_mea_values(self, meas_pv_config: dict, ignore_stale_pvs: bool = False): if not pv_name: continue - # Add the PV value to the measurements. If the PV does not exist in the PV monitors data dict, - # then skip it. This could happen because of a monitor not receiving updates from the existing PV. + # Add the PV value to the measurements. If the PV does not exist in the PV monitors + # data dict, then skip it. This could happen because of a monitor not receiving + # updates from the existing PV. try: - # If the PV data is stale, then ignore it. If Add Stale PVs setting is enabled, add it anyway. - # If called with 'ignore stale PVs' set to True, don't add stale PVs, no matter the CA settings. - if self.pv_monitors.pv_data_is_stale(pv_name) and not CA.ADD_STALE_PVS and not ignore_stale_pvs: + # If the PV data is stale, then ignore it. If Add Stale PVs setting is enabled, + # add it anyway. If called with 'ignore stale PVs' set to True, don't add stale + # PVs, no matter the CA settings. + if self.pv_monitors.pv_data_is_stale( + pv_name) and not CA.ADD_STALE_PVS and not ignore_stale_pvs: continue pv_value = self.pv_monitors.get_pv_data(pv_name) diff --git a/HLM_PV_Import/settings.py b/hlm_pv_import/settings.py similarity index 95% rename from HLM_PV_Import/settings.py rename to hlm_pv_import/settings.py index 49d9185..40c2b5f 100644 --- a/HLM_PV_Import/settings.py +++ b/hlm_pv_import/settings.py @@ -14,13 +14,13 @@ # sys._MEIPASS: # For a one-folder bundle, this is the path to that folder, wherever the user may have put it. - # For a one-file bundle, this is the path to the _MEIxxxxxx temporary folder created by the bootloader. + # For a one-file bundle, this is the path to the _MEIxxxxxx temporary folder created by the + # bootloader. # To get the same path as the executable, use sys.executable for one-file executables. BASE_PATH = os.path.dirname(sys.executable) else: BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..') - PVConfig.PATH = os.path.join(BASE_PATH, PVConfig.FILE) config = configparser.ConfigParser() @@ -28,7 +28,8 @@ class CA: - EPICS_CA_ADDR_LIST = config['ChannelAccess']['EPICS_CA_ADDR_LIST'] # Epics channel access address list + # Epics channel access address list + EPICS_CA_ADDR_LIST = config['ChannelAccess']['EPICS_CA_ADDR_LIST'] CONN_TIMEOUT = config['ChannelAccess'].getfloat('ConnectionTimeout') STALE_AFTER = config['ChannelAccess'].getfloat('PvStaleAfter') ADD_STALE_PVS = config['ChannelAccess'].getboolean('AddStalePvs') diff --git a/HLM_PV_Import/user_config.py b/hlm_pv_import/user_config.py similarity index 86% rename from HLM_PV_Import/user_config.py rename to hlm_pv_import/user_config.py index 419bde5..aeb3c51 100644 --- a/HLM_PV_Import/user_config.py +++ b/hlm_pv_import/user_config.py @@ -1,8 +1,8 @@ from iteration_utilities import duplicates, unique_everseen -from HLM_PV_Import.logger import logger -from HLM_PV_Import.settings import PVConfig, CA -from HLM_PV_Import.ca_wrapper import get_connected_pvs -from HLM_PV_Import.db_func import get_object +from hlm_pv_import.logger import logger +from hlm_pv_import.settings import PVConfig, CA +from hlm_pv_import.ca_wrapper import get_connected_pvs +from hlm_pv_import.db_func import get_object import json from shared.utils import get_full_pv_name @@ -10,14 +10,15 @@ class UserConfig: """ - User configuration class that stores the entries, object IDs, available PVs, and methods to work with them, - including config schema and content validation. + User configuration class that stores the entries, object IDs, available PVs, and methods to + work with them, including config schema and content validation. """ def __init__(self): self.entries = self._get_all_entries() self.object_ids = [entry[PVConfig.OBJ] for entry in self.entries] - self.logging_periods = {entry[PVConfig.OBJ]: entry[PVConfig.LOG_PERIOD] for entry in self.entries} + self.logging_periods = {entry[PVConfig.OBJ]: entry[PVConfig.LOG_PERIOD] for entry in + self.entries} # Run config checks try: @@ -103,16 +104,19 @@ def _check_entries_have_measurement_pvs(self): objects_with_no_pvs.append(obj_id) if objects_with_no_pvs: - raise PVConfigurationException(f'Objects {objects_with_no_pvs} have no measurement PVs.') + raise PVConfigurationException( + f'Objects {objects_with_no_pvs} have no measurement PVs.') def get_measurement_pvs(self, no_duplicates=True, full_names=False): """ Gets a list of the measurement PVs, ignoring empty/null measurements. Args: - no_duplicates (boolean, optional): If one PV is present in multiple entries, add it to the list + no_duplicates (boolean, optional): If one PV is present in multiple entries, + add it to the list only once, Defaults to True. - full_names (boolean, optional): Get the PV names with their prefix and domain, Defaults to False. + full_names (boolean, optional): Get the PV names with their prefix and domain, + Defaults to False. Returns: (list): The list of PVs @@ -126,7 +130,8 @@ def get_measurement_pvs(self, no_duplicates=True, full_names=False): config_pvs.append(pv_name) if full_names: - config_pvs = [get_full_pv_name(pv_name, prefix=CA.PV_PREFIX, domain=CA.PV_DOMAIN) for pv_name in config_pvs] + config_pvs = [get_full_pv_name(pv_name, prefix=CA.PV_PREFIX, domain=CA.PV_DOMAIN) for + pv_name in config_pvs] if no_duplicates: config_pvs = list(set(config_pvs)) @@ -139,15 +144,17 @@ def get_entry_measurement_pvs(self, object_id, full_names=False): Args: object_id (str): The object ID. - full_names (boolean, optional): Get the PV names with their prefix and domain, Defaults to False. + full_names (boolean, optional): Get the PV names with their prefix and domain, + Defaults to False. Returns: (dict): The measurements PVs, in measurement number/pv name pairs. """ # Get the measurements of the entry with the given object_id, None if not found - entry_meas = next((x[PVConfig.MEAS] for x in self.entries if x[PVConfig.OBJ] == object_id), None) + entry_meas = next((x[PVConfig.MEAS] for x in self.entries if x[PVConfig.OBJ] == object_id), + None) return {key: get_full_pv_name(val, prefix=CA.PV_PREFIX, domain=CA.PV_DOMAIN) - if full_names else val for key, val in entry_meas.items() if val} + if full_names else val for key, val in entry_meas.items() if val} @staticmethod def _get_all_entries(): @@ -158,7 +165,8 @@ def _get_all_entries(): (list): The configuration entries. Raises: - PVConfigurationException: If the configuration file is either empty or does not have at least one entry. + PVConfigurationException: If the configuration file is either empty or does not have + at least one entry. """ config_file = PVConfig.PATH with open(config_file) as f: diff --git a/HLM_PV_Import/utils.py b/hlm_pv_import/utils.py similarity index 100% rename from HLM_PV_Import/utils.py rename to hlm_pv_import/utils.py diff --git a/pylintrc b/pylintrc index bcd0d4b..10ca2a7 100644 --- a/pylintrc +++ b/pylintrc @@ -1,4 +1,4 @@ -[MASTER] +[MASTER] # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may diff --git a/ServiceManager/GUI/__init__.py b/service_manager/__init__.py similarity index 100% rename from ServiceManager/GUI/__init__.py rename to service_manager/__init__.py diff --git a/ServiceManager/__main__.py b/service_manager/__main__.py similarity index 80% rename from ServiceManager/__main__.py rename to service_manager/__main__.py index 73d6d76..e635e9c 100644 --- a/ServiceManager/__main__.py +++ b/service_manager/__main__.py @@ -6,11 +6,11 @@ from PyQt5.QtGui import QIcon, QFont from PyQt5.QtWidgets import QApplication, QErrorMessage -from ServiceManager.GUI.main_window import UIMainWindow -from ServiceManager.GUI.service_path_dlg import UIServicePathDialog -from ServiceManager.constants import icon_path -from ServiceManager.logger import manager_logger -from ServiceManager.settings import Settings +from service_manager.gui.main_window import UIMainWindow +from service_manager.gui.service_path_dlg import UIServicePathDialog +from service_manager.constants import icon_path +from service_manager.logger import manager_logger +from service_manager.settings import Settings class App: @@ -26,9 +26,9 @@ def __init__(self): # Look for the service path in manager settings service_settings_path = Settings.Manager.service_path - # If service path is found, initialize service settings and open main window - # Otherwise, open Service Path dialog and ask for the service directory path. - # Once a path has been provided and service settings initialized from the dialog, close it and open main window. + # If service path is found, initialize service settings and open main window Otherwise, + # open Service Path dialog and ask for the service directory path. Once a path has been + # provided and service settings initialized from the dialog, close it and open main window. if service_settings_path: Settings.init_service_settings(service_settings_path) self.show_main_window() diff --git a/ServiceManager/constants.py b/service_manager/constants.py similarity index 91% rename from ServiceManager/constants.py rename to service_manager/constants.py index a293341..f3d521d 100644 --- a/ServiceManager/constants.py +++ b/service_manager/constants.py @@ -1,7 +1,9 @@ +""" +Contains constants used by the service manager such as folder paths and settings file templates. +""" +# pylint: disable=W0212 import os import sys -# noinspection PyUnresolvedReferences -from shared.const import * # About VER = '1.1.4' @@ -13,12 +15,12 @@ # BASE_PATH = os.path.dirname(sys.executable) # noinspection PyProtectedMember # noinspection PyUnresolvedReferences - BASE_PATH = sys._MEIPASS + BASE_PATH = sys._MEIPASS # used for pyinstaller else: BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__))) # region Assets & Layouts -GUI_DIR_PATH = os.path.join(BASE_PATH, 'GUI') +GUI_DIR_PATH = os.path.join(BASE_PATH, 'gui') ASSETS_PATH = os.path.join(GUI_DIR_PATH, 'assets') icon_path = os.path.join(ASSETS_PATH, 'icon.svg') about_logo_path = os.path.join(ASSETS_PATH, 'isis-logo.png') diff --git a/ServiceManager/db_func.py b/service_manager/db_func.py similarity index 94% rename from ServiceManager/db_func.py rename to service_manager/db_func.py index c93209f..3c67562 100644 --- a/ServiceManager/db_func.py +++ b/service_manager/db_func.py @@ -1,11 +1,11 @@ from peewee import DoesNotExist from datetime import datetime -from ServiceManager.utilities import generate_module_name +from service_manager.utilities import generate_module_name from shared.const import DBTypeIDs, DBClassIDs from shared.utils import need_connection from shared.db_models import * -from ServiceManager.logger import manager_logger as logger +from service_manager.logger import manager_logger as logger def db_connect(): @@ -244,10 +244,12 @@ def create_module_if_required(object_id: int, object_name: str, type_name: str, module_name = generate_module_name(object_name, object_id, class_id) if class_id in [DBClassIDs.VESSEL, DBClassIDs.CRYOSTAT]: module_id = add_object(name=module_name, type_id=DBTypeIDs.SLD, - comment=f'Software Level Device for {type_name} "{object_name}" (ID: {object_id})') + comment=f'Software Level Device for {type_name} "{object_name}"' + f' (ID: {object_id})') elif class_id == DBClassIDs.GAS_COUNTER: module_id = add_object(name=module_name, type_id=DBTypeIDs.GCM, - comment=f'Gas Counter Module for {type_name} "{object_name}" (ID: {object_id})') + comment=f'Gas Counter Module for {type_name} "{object_name}"' + f' (ID: {object_id})') if module_id is not None: add_relation(or_object_id=object_id, or_object_id_assigned=module_id) @@ -270,11 +272,11 @@ def add_object(name: str, type_id: int, display_group_id: int = None, comment: s DBObjectNameAlreadyExists: If an object with the given name already exists in the database. """ if get_object_id(object_name=name) is not None: - raise DBObjectNameAlreadyExists(f'Could not create object - ' - f'Object with name "{name}" already exists in the database.') + raise DBObjectNameAlreadyExists( + f'Could not create object - Object with name "{name}" already exists in the database.') - record_id = GamObject.insert(ob_name=name, ob_objecttype=type_id, ob_displaygroup=display_group_id, - ob_comment=comment).execute() + record_id = GamObject.insert(ob_name=name, ob_objecttype=type_id, + ob_displaygroup=display_group_id, ob_comment=comment).execute() logger.info(f'Created object no. {record_id} ("{name}") of type {type_id}.') diff --git a/ServiceManager/__init__.py b/service_manager/gui/__init__.py similarity index 100% rename from ServiceManager/__init__.py rename to service_manager/gui/__init__.py diff --git a/ServiceManager/GUI/about.py b/service_manager/gui/about.py similarity index 91% rename from ServiceManager/GUI/about.py rename to service_manager/gui/about.py index 573621c..9198735 100644 --- a/ServiceManager/GUI/about.py +++ b/service_manager/gui/about.py @@ -2,7 +2,7 @@ from PyQt5.QtGui import QIcon, QDesktopServices from PyQt5.QtWidgets import QDialog from PyQt5 import uic -from ServiceManager.constants import VER, B_DATE, ISIS_URL, about_ui, about_logo_path +from service_manager.constants import VER, B_DATE, ISIS_URL, about_ui, about_logo_path class UIAbout(QDialog): diff --git a/ServiceManager/GUI/assets/add.svg b/service_manager/gui/assets/add.svg similarity index 100% rename from ServiceManager/GUI/assets/add.svg rename to service_manager/gui/assets/add.svg diff --git a/ServiceManager/GUI/assets/delete.svg b/service_manager/gui/assets/delete.svg similarity index 100% rename from ServiceManager/GUI/assets/delete.svg rename to service_manager/gui/assets/delete.svg diff --git a/ServiceManager/GUI/assets/edit.svg b/service_manager/gui/assets/edit.svg similarity index 100% rename from ServiceManager/GUI/assets/edit.svg rename to service_manager/gui/assets/edit.svg diff --git a/ServiceManager/GUI/assets/expand.svg b/service_manager/gui/assets/expand.svg similarity index 100% rename from ServiceManager/GUI/assets/expand.svg rename to service_manager/gui/assets/expand.svg diff --git a/ServiceManager/GUI/assets/filter.svg b/service_manager/gui/assets/filter.svg similarity index 100% rename from ServiceManager/GUI/assets/filter.svg rename to service_manager/gui/assets/filter.svg diff --git a/ServiceManager/GUI/assets/icon.svg b/service_manager/gui/assets/icon.svg similarity index 100% rename from ServiceManager/GUI/assets/icon.svg rename to service_manager/gui/assets/icon.svg diff --git a/ServiceManager/GUI/assets/isis-logo.png b/service_manager/gui/assets/isis-logo.png similarity index 100% rename from ServiceManager/GUI/assets/isis-logo.png rename to service_manager/gui/assets/isis-logo.png diff --git a/ServiceManager/GUI/assets/loading.gif b/service_manager/gui/assets/loading.gif similarity index 100% rename from ServiceManager/GUI/assets/loading.gif rename to service_manager/gui/assets/loading.gif diff --git a/ServiceManager/GUI/assets/refresh.svg b/service_manager/gui/assets/refresh.svg similarity index 100% rename from ServiceManager/GUI/assets/refresh.svg rename to service_manager/gui/assets/refresh.svg diff --git a/ServiceManager/GUI/assets/refresh_config.svg b/service_manager/gui/assets/refresh_config.svg similarity index 100% rename from ServiceManager/GUI/assets/refresh_config.svg rename to service_manager/gui/assets/refresh_config.svg diff --git a/ServiceManager/GUI/assets/restart.svg b/service_manager/gui/assets/restart.svg similarity index 100% rename from ServiceManager/GUI/assets/restart.svg rename to service_manager/gui/assets/restart.svg diff --git a/ServiceManager/GUI/assets/search.svg b/service_manager/gui/assets/search.svg similarity index 100% rename from ServiceManager/GUI/assets/search.svg rename to service_manager/gui/assets/search.svg diff --git a/ServiceManager/GUI/assets/shrink.svg b/service_manager/gui/assets/shrink.svg similarity index 100% rename from ServiceManager/GUI/assets/shrink.svg rename to service_manager/gui/assets/shrink.svg diff --git a/ServiceManager/GUI/assets/start.svg b/service_manager/gui/assets/start.svg similarity index 100% rename from ServiceManager/GUI/assets/start.svg rename to service_manager/gui/assets/start.svg diff --git a/ServiceManager/GUI/assets/stop.svg b/service_manager/gui/assets/stop.svg similarity index 100% rename from ServiceManager/GUI/assets/stop.svg rename to service_manager/gui/assets/stop.svg diff --git a/ServiceManager/GUI/ca_settings.py b/service_manager/gui/ca_settings.py similarity index 92% rename from ServiceManager/GUI/ca_settings.py rename to service_manager/gui/ca_settings.py index a11a393..5847afe 100644 --- a/ServiceManager/GUI/ca_settings.py +++ b/service_manager/gui/ca_settings.py @@ -1,12 +1,13 @@ from PyQt5.QtCore import Qt, pyqtSignal, QModelIndex from PyQt5.QtGui import QCloseEvent, QShowEvent -from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem, QAbstractItemView, QStyledItemDelegate, \ +from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem, QAbstractItemView, \ + QStyledItemDelegate, \ QWidget, QStyleOptionViewItem from PyQt5 import uic -from ServiceManager.constants import ca_settings_ui -from ServiceManager.settings import Settings -from ServiceManager.utilities import apply_unsaved_changes_dialog +from service_manager.constants import ca_settings_ui +from service_manager.settings import Settings +from service_manager.utilities import apply_unsaved_changes_dialog class UICASettings(QDialog): @@ -14,7 +15,7 @@ def __init__(self): super(UICASettings, self).__init__() uic.loadUi(uifile=ca_settings_ui, baseinstance=self) self.setModal(True) - + self._settings_changed = False # region Get widgets @@ -26,8 +27,10 @@ def __init__(self): self.addr_edit_btn.clicked.connect(self.edit_address) self.addr_del_btn.clicked.connect(self.delete_address) - self.text_settings = [self.pv_timeout_ln, self.pv_stale_ln, self.pv_prefix_ln, self.pv_domain_ln] - [line_edit.textChanged.connect(lambda _: self.settings_changed(True)) for line_edit in self.text_settings] + self.text_settings = [self.pv_timeout_ln, self.pv_stale_ln, self.pv_prefix_ln, + self.pv_domain_ln] + [line_edit.textChanged.connect(lambda _: self.settings_changed(True)) for line_edit in + self.text_settings] self.add_stale_pvs.stateChanged.connect(lambda _: self.settings_changed(True)) self.button_box.rejected.connect(self.close) @@ -109,8 +112,8 @@ def delete_address(self): selected_item = self.addr_list.currentItem() item_row = self.addr_list.row(selected_item) self.addr_list.takeItem(item_row) - # Items removed from a list widget will not be managed by Qt, and will need to be deleted manually. - # https://doc.qt.io/qt-5/qlistwidget.html#takeItem + # Items removed from a list widget will not be managed by Qt, and will need to be deleted + # manually. https://doc.qt.io/qt-5/qlistwidget.html#takeItem del selected_item self.settings_changed() diff --git a/ServiceManager/GUI/config_entry.py b/service_manager/gui/config_entry.py similarity index 77% rename from ServiceManager/GUI/config_entry.py rename to service_manager/gui/config_entry.py index a13463f..bbe11fc 100644 --- a/ServiceManager/GUI/config_entry.py +++ b/service_manager/gui/config_entry.py @@ -1,11 +1,11 @@ from PyQt5.QtWidgets import QDialog, QApplication, QLineEdit from PyQt5 import uic -from ServiceManager.constants import config_entry_ui -from ServiceManager.settings import Settings -from ServiceManager.utilities import set_red_border -from ServiceManager.db_func import * -from ServiceManager.GUI.config_entry_utils import * +from service_manager.constants import config_entry_ui +from service_manager.settings import Settings +from service_manager.utilities import set_red_border +from service_manager.db_func import * +from service_manager.gui.config_entry_utils import * from shared.utils import get_object_module @@ -18,12 +18,18 @@ def __init__(self): self.setModal(True) # region Attributes - self.last_details_update_obj = None # Name of object whose details were last updated and displayed - self.loading_msg = LoadingPopupWindow() # Loading splash screen when testing PV connection - self.pv_check_obj_id = None # For PV auto-check. Save object ID and whether it already exists - self.pv_check_obj_already_exists = None # before starting the connection test thread. - self.existing_config_pvs = {} # Store existing object config measurement PVs when 'Load' clicked - self.type_and_comment_updated = False # If the object type and comment were updated by an existing object + # Name of object whose details were last updated and displayed + self.last_details_update_obj = None + # Loading splash screen when testing PV connection + self.loading_msg = LoadingPopupWindow() + # For PV auto-check. Save object ID and whether it already exists + self.pv_check_obj_id = None + # before starting the connection test thread. + self.pv_check_obj_already_exists = None + # Store existing object config measurement PVs when 'Load' clicked + self.existing_config_pvs = {} + # If the object type and comment were updated by an existing object + self.type_and_comment_updated = False # Default text of the Check PVs button ("Loading ...") self.check_pvs_btn_default_text = self.check_pvs_btn.text() @@ -51,26 +57,33 @@ def __init__(self): self.obj_name_cb.currentIndexChanged.connect(self.load_object_data) self.obj_name_cb.lineEdit().returnPressed.connect(self.load_object_data) - self.obj_name_cb.lineEdit().textChanged.connect(lambda: set_red_border(self.obj_name_frame, False)) + self.obj_name_cb.lineEdit().textChanged.connect( + lambda: set_red_border(self.obj_name_frame, False)) self.obj_name_cb.lineEdit().textChanged.connect(self.check_for_existing_config_pvs) self.obj_name_cb.lineEdit().textChanged.connect(self.load_object_data) - self.object_name_filter = ObjectNameCBFilter() # instantiate event filter with custom signals - self.object_name_filter.focusOut.connect(self.load_object_data) # connect filter custom signal to slot - self.obj_name_cb.installEventFilter(self.object_name_filter) # install filter to widget + # instantiate event filter with custom signals + self.object_name_filter = ObjectNameCBFilter() + # connect filter custom signal to slot + self.object_name_filter.focusOut.connect(self.load_object_data) + # install filter to widget + self.obj_name_cb.installEventFilter(self.object_name_filter) self.obj_display_group_cb.currentTextChanged.connect(self.message_lbl.clear) self.obj_type_cb.currentTextChanged.connect(self.message_lbl.clear) - self.obj_type_cb.currentTextChanged.connect(lambda: set_red_border(self.obj_type_frame, False)) + self.obj_type_cb.currentTextChanged.connect( + lambda: set_red_border(self.obj_type_frame, False)) self.obj_type_cb.currentTextChanged.connect(self.update_measurement_types) self.obj_details_btn.clicked.connect(self.toggle_details_frame) # Connect signals for each measurement PV name line edit for mea in self.mea_widgets: - # Need to bind mea for each function created (x=mea), otherwise only last mea will be affected + # Need to bind mea for each function created (x=mea), otherwise only last mea will be + # affected mea[0].textChanged.connect(lambda _, x=mea: self.on_mea_pv_text_change(x)) - # If any PV name has been changed, if the load existing config frame is visible, enable the load button + # If any PV name has been changed, if the load existing config frame is visible, + # enable the load button # and update its text. (Disabled, 'Loaded' --> Enabled, 'Load') mea[0].textEdited.connect(self.update_load_existing_config_btn) @@ -89,11 +102,15 @@ def __init__(self): # region Thread - PV Connection Check self.pvs_connection_thread = CheckPVsThread() self.pvs_connection_thread.mea_status_update.connect(self.update_measurements_pvs_status) - self.pvs_connection_thread.display_progress_bar.connect(lambda x: self.check_pvs_progress_bar.setVisible(x)) - self.pvs_connection_thread.progress_bar_update.connect(lambda val: self.check_pvs_progress_bar.setValue(val)) + self.pvs_connection_thread.display_progress_bar.connect( + lambda x: self.check_pvs_progress_bar.setVisible(x)) + self.pvs_connection_thread.progress_bar_update.connect( + lambda val: self.check_pvs_progress_bar.setValue(val)) self.pvs_connection_thread.started.connect(self.pvs_connection_check_started) - self.pvs_connection_thread.finished_check.connect(lambda x: self.pvs_connection_check_finished(x)) - QApplication.instance().aboutToQuit.connect(self.pvs_connection_thread.stop) # graceful exit on app close + self.pvs_connection_thread.finished_check.connect( + lambda x: self.pvs_connection_check_finished(x)) + # graceful exit on app close + QApplication.instance().aboutToQuit.connect(self.pvs_connection_thread.stop) # endregion # region Show & Close Events @@ -156,16 +173,16 @@ def update_fields(self): # region Accept, Reject, Save def on_accepted(self): """ - Check if the object name is given and measurements have at least one PV. - Gets the object name from the LineEdit and fetches its ID from the DB. - If ID is found, check if it already has an entry in the PV config. If it exists, show message box to confirm - overwriting or cancelling. - If ID is not found, enable the Type and Comment lines to be edited. An object can be created with the type - and comment (optional) after confirming object creation. - - If auto-check PVs is enabled, run the PV connection test thread, and continue after the thread finished signal - is emitted. If any PVs fail to connect, display warning, with the option or cancelling, or adding config anyway - which will call the final add_entry method. + Check if the object name is given and measurements have at least one PV. Gets the object + name from the LineEdit and fetches its ID from the DB. If ID is found, check if it + already has an entry in the PV config. If it exists, show message box to confirm + overwriting or cancelling. If ID is not found, enable the Type and Comment lines to be + edited. An object can be created with the type and comment (optional) after confirming + object creation. + + If auto-check PVs is enabled, run the PV connection test thread, and continue after the + thread finished signal is emitted. If any PVs fail to connect, display warning, with the + option or cancelling, or adding config anyway which will call the final add_entry method. If auto-check is not enabled, call add_entry directly. """ # Validate and display invalid input message + red highlight relevant frames if invalid @@ -175,32 +192,39 @@ def on_accepted(self): object_name = self.obj_name_cb.lineEdit().text() object_id = get_object_id(object_name) - # If object with the given name was not found in the database, ask whether to create a new one + # If object with the given name was not found in the database, ask whether to create a + # new one if not object_id: type_name = self.obj_type_cb.currentText() type_id = get_type_id(type_name=type_name) - display_group_id = get_display_group_id(display_group=self.obj_display_group_cb.currentText()) + display_group_id = get_display_group_id( + display_group=self.obj_display_group_cb.currentText()) msg_box = QMessageBox.question(self, 'Create new object', - f'Create new object "{object_name}" with type "{type_name}" ' + f'Create new object "{object_name}" with type "' + f'{type_name}" ' f'and save the PV configuration?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if msg_box != QMessageBox.Yes: return try: - object_id = add_object(object_name, type_id, display_group_id, self.obj_comment.text()) + object_id = add_object(object_name, type_id, display_group_id, + self.obj_comment.text()) create_module_if_required(object_id=object_id, object_name=object_name, type_name=type_name, class_id=get_class_id(type_id)) except DBObjectNameAlreadyExists: - self.set_message_colored_text(f'Object "{object_name}" already exists in the database.', 'red') + self.set_message_colored_text( + f'Object "{object_name}" already exists in the database.', 'red') set_red_border(self.obj_name_frame) return except Exception as e: - manager_logger.error(f'Exception occurred when adding new object to DB, aborting PV Config entry ' - f'creation: {e}') + manager_logger.error( + f'Exception occurred when adding new object to DB, aborting PV Config entry ' + f'creation: {e}') return - # Check if object already has a PV configuration (worth checking even for objects not in the DB) + # Check if object already has a PV configuration (worth checking even for objects not in + # the DB) existing_ids = Settings.Service.PVConfig.get_entry_object_ids() already_exists = False if object_id in existing_ids: # if it does already have a config, ask if overwrite @@ -210,13 +234,15 @@ def on_accepted(self): if resp != QMessageBox.Ok: return - # Test PV connections if auto-check is enabled, by starting the PV check thread. Once finished, it will emit - # a signal to be picked by the main thread that will call the add_entry method. - # If the auto-check is disabled, add entry directly. + # Test PV connections if auto-check is enabled, by starting the PV check thread. Once + # finished, it will emit a signal to be picked by the main thread that will call the + # add_entry method. If the auto-check is disabled, add entry directly. auto_pv_check_enabled = Settings.Manager.auto_pv_check if auto_pv_check_enabled: - self.pv_check_obj_id = object_id # Store the object ID and whether it already has - self.pv_check_obj_already_exists = already_exists # a config. To be used after check thread finishes. + # Store the object ID and whether it already has + self.pv_check_obj_id = object_id + # a config. To be used after check thread finishes. + self.pv_check_obj_already_exists = already_exists self.start_measurement_pvs_check(add_entry_after_check=True) self.loading_msg.show() else: @@ -228,13 +254,15 @@ def add_entry(self, object_id: int, overwrite: bool): Args: object_id (int): The object ID. - overwrite (bool): If object already has config, overwrite entry. If False, append as normal. + overwrite (bool): If object already has config, overwrite entry. If False, append as + normal. """ log_period = self.log_interval_sb.value() mea_pv_names = [mea[0].text() for mea in self.mea_widgets] measurement_pvs = {} for index, pv_name in enumerate(mea_pv_names): - measurement_pvs[f'{index + 1}'] = Settings.Service.CA.get_short_pv_name(pv_name) if pv_name else None + measurement_pvs[f'{index + 1}'] = Settings.Service.CA.get_short_pv_name( + pv_name) if pv_name else None config_data = { Settings.Service.PVConfig.OBJ: object_id, @@ -257,8 +285,8 @@ def add_entry(self, object_id: int, overwrite: bool): def validate(self): """ - Check if object name, type, and at least one measurement PV name is provided. - If not, highlight relevant frames with a red border and display the error message. + Check if object name, type, and at least one measurement PV name is provided. If not, + highlight relevant frames with a red border and display the error message. Returns: (bool): True if input is valid, False if not. @@ -280,11 +308,14 @@ def _set_invalid(msg: str, frame: QFrame): else: type_id = get_type_id(type_name=type_name) if not type_id: - input_valid = _set_invalid(f'Type "{type_name}" was not found.', self.obj_type_frame) + input_valid = _set_invalid(f'Type "{type_name}" was not found.', + self.obj_type_frame) else: class_id = get_class_id(type_id=type_id) - # if object will have a module lower max name length to make space for the module object name formatting - module_name = generate_module_name(object_name="", object_id=get_max_object_id() + 1, + # if object will have a module lower max name length to make space for the module + # object name formatting + module_name = generate_module_name(object_name="", + object_id=get_max_object_id() + 1, object_class=class_id) if module_name is not None: object_name_max_length -= len(module_name) @@ -294,8 +325,9 @@ def _set_invalid(msg: str, frame: QFrame): input_valid = _set_invalid('Object name is required.', self.obj_name_frame) else: if len(self.obj_name_cb.lineEdit().text()) > object_name_max_length: - input_valid = _set_invalid('Object name is too long. Max length for this object is: {}' - .format(object_name_max_length), self.obj_name_frame) + input_valid = _set_invalid( + 'Object name is too long. Max length for this object is: {}'.format( + object_name_max_length), self.obj_name_frame) # check measurement pv names if not any(mea[0].text() for mea in self.mea_widgets): @@ -316,8 +348,8 @@ def on_delete(self): if object_id not in Settings.Service.PVConfig.get_entry_object_ids(): self.set_message_colored_text( - f'Object "{object_name}" with ID {object_id} does not have a PV configuration.', 'red' - ) + f'Object "{object_name}" with ID {object_id} does not have a PV configuration.', + 'red') set_red_border(self.obj_name_frame) msg_box = ConfigDeleteMessageBox(object_name, object_id) @@ -338,7 +370,8 @@ def toggle_details_frame(self): def clear_details(self): """ - Clear the object details, and if the type and comment were last updated by selecting an existing object, + Clear the object details, and if the type and comment were last updated by selecting an + existing object, clear them as well. """ if self.type_and_comment_updated: @@ -354,12 +387,13 @@ def clear_details(self): def load_object_data(self, update_meas_pvs: bool = True): """ - Update object details, show existing config frame if exists and load it if auto-load config is enabled. - Disable and update type & comment if object is found in the DB, otherwise clear & enable editing - for object creation. + Update object details, show existing config frame if exists and load it if auto-load + config is enabled. Disable and update type & comment if object is found in the DB, + otherwise clear & enable editing for object creation. Args: - update_meas_pvs (bool): Update measurement PVs if auto-load config is enabled. If False, don't update, even + update_meas_pvs (bool): Update measurement PVs if auto-load config is enabled. If + False, don't update, even if auto-load is enabled in the settings. """ current_object_name = self.obj_name_cb.currentText() @@ -382,8 +416,10 @@ def load_object_data(self, update_meas_pvs: bool = True): self.update_details(obj_id, current_object_name) if update_meas_pvs: - if Settings.Manager.auto_load_existing_config: # If existing config auto-load setting is enabled - self.clear_measurement_pv_names() # clear the PV names before updating + # If existing config auto-load setting is enabled + if Settings.Manager.auto_load_existing_config: + # clear the PV names before updating + self.clear_measurement_pv_names() self.check_for_existing_config_pvs(obj_id) def update_details(self, obj_id: int, current_object: str): @@ -395,7 +431,8 @@ def update_details(self, obj_id: int, current_object: str): self.obj_comment.setText(obj.ob_comment) self.obj_type_cb.setCurrentText(obj.ob_objecttype.ot_name) - self.obj_display_group_cb.setCurrentText(obj.ob_displaygroup.dg_name if obj.ob_displaygroup else None) + self.obj_display_group_cb.setCurrentText( + obj.ob_displaygroup.dg_name if obj.ob_displaygroup else None) self.type_and_comment_updated = True self.obj_detail_name.setText(obj.ob_name) @@ -410,7 +447,8 @@ def update_details(self, obj_id: int, current_object: str): self.last_details_update_obj = current_object def check_for_existing_config_pvs(self, obj_id: int): - """ Check if a PV config with the given object already exists, and if it does, display config load frame. """ + """ Check if a PV config with the given object already exists, and if it does, + display config load frame. """ obj_entry = Settings.Service.PVConfig.get_entry_with_id(obj_id) if not obj_entry: self.existing_config_frame.hide() @@ -426,8 +464,10 @@ def check_for_existing_config_pvs(self, obj_id: int): self.existing_config_load_btn.setEnabled(True) self.existing_config_load_btn.setText('Load') - if Settings.Manager.auto_load_existing_config: # If existing config auto-load setting is enabled - self.load_existing_config_pvs() # update all PV names. + # If existing config auto-load setting is enabled + if Settings.Manager.auto_load_existing_config: + # update all PV names. + self.load_existing_config_pvs() def load_existing_config_pvs(self): """ Update the measurement PV names with those from the existing config. """ @@ -497,16 +537,16 @@ def toggle_widgets_based_on_pv_check_running(self, pvs_check_running: bool): def start_measurement_pvs_check(self, add_entry_after_check: bool = False): """ - Check all line edits. If no text, skip. If text, take PV name (should accept both FULL and PARTIAL names). - Starts the PV connection check thread. - For each PV, thread tries to connect and get value. If success, change label and set green, - if timeout set red etc. + Check all line edits. If no text, skip. If text, take PV name (should accept both FULL + and PARTIAL names). Starts the PV connection check thread. + For each PV, thread tries to connect and get value. If success, change label and set + green, if timeout set red etc. While loading, change label text or add loading animation Args: add_entry_after_check (bool): Whether to to add the PV configuration on finish or not. - This is used when the check is started automatically by the Add Configuration button - if automatic PV check is enabled in general settings. + This is used when the check is started automatically by the Add Configuration + button if automatic PV check is enabled in general settings. """ names = [] for mea in self.mea_widgets: @@ -566,23 +606,28 @@ def pvs_connection_check_finished(self, add_entry=False): results = self.pvs_connection_thread.results failed_pvs = len(results['failed']) if failed_pvs: - self.set_message_colored_text(f'PV connection test finished.\n{failed_pvs} PVs failed to connect.', 'red') + self.set_message_colored_text( + f'PV connection test finished.\n{failed_pvs} PVs failed to connect.', 'red') else: - self.set_message_colored_text('PV connection test finished.\nAll PVs connected.', 'green') + self.set_message_colored_text('PV connection test finished.\nAll PVs connected.', + 'green') if add_entry: # Trigger once the PV connection check thread has finished. - # Check if there are any PVs that failed to connect, and if so, display warning to ask if cancel or add. - # If all PVs successfully connected or user decides to add anyway, call add_entry. + # Check if there are any PVs that failed to connect, and if so, display warning to + # ask if cancel or add. If all PVs successfully connected or user decides to add + # anyway, call add_entry. self.loading_msg.close() if self.pvs_connection_thread.results['failed']: msg_box = QMessageBox.warning(self, 'PV Connection Failed', - 'Could not establish connection to one or more PVs.\n' - 'Add configuration anyway?', - QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel) + 'Could not establish connection to one or more ' + 'PVs.\n' 'Add configuration anyway?', + QMessageBox.Yes | QMessageBox.Cancel, + QMessageBox.Cancel) if msg_box != QMessageBox.Yes: return - self.add_entry(object_id=self.pv_check_obj_id, overwrite=self.pv_check_obj_already_exists) + self.add_entry(object_id=self.pv_check_obj_id, + overwrite=self.pv_check_obj_already_exists) # endregion diff --git a/ServiceManager/GUI/config_entry_utils.py b/service_manager/gui/config_entry_utils.py similarity index 81% rename from ServiceManager/GUI/config_entry_utils.py rename to service_manager/gui/config_entry_utils.py index 4e38d94..5c445af 100644 --- a/ServiceManager/GUI/config_entry_utils.py +++ b/service_manager/gui/config_entry_utils.py @@ -2,9 +2,9 @@ from PyQt5.QtGui import QFont, QMovie, QShowEvent, QCloseEvent from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QFrame, QMessageBox -from ServiceManager.constants import loading_animation -from ServiceManager.logger import manager_logger -from ServiceManager.utilities import test_pv_connection +from service_manager.constants import loading_animation +from service_manager.logger import manager_logger +from service_manager.utilities import test_pv_connection class ObjectNameCBFilter(QObject): @@ -16,7 +16,7 @@ def eventFilter(self, widget, event): if event.type() == QEvent.FocusOut: # Custom actions self.focusOut.emit() - return False # return False so that the widget will also handle the event + return False # return False so that the widget will also handle the event class CheckPVsThread(QThread): @@ -35,9 +35,9 @@ def __init__(self, *args, **kwargs): self._running = None self.results = None - # If the PV connection test is started from the Save/Add Config button (auto-check before entry add/edit). - # This is used when the check is started automatically by the Add Configuration/Save button, and - # automatic PV check is enabled in general settings. + # If the PV connection test is started from the Save/Add Config button (auto-check before + # entry add/edit). This is used when the check is started automatically by the Add + # Configuration/Save button, and automatic PV check is enabled in general settings. self._add_entry = False def __del__(self): @@ -81,7 +81,8 @@ def run(self): self.progress_bar_update.emit(index + 1) if self._running: - manager_logger.info(f'PV connection check finished. Connected: {connected}. Failed: {failed}') + manager_logger.info( + f'PV connection check finished. Connected: {connected}. Failed: {failed}') self.results = {'connected': connected, 'failed': failed} @@ -125,14 +126,16 @@ def closeEvent(self, e: QCloseEvent): class OverwriteMessageBox(QMessageBox): - """ Message box displayed on Save/Add Config when the object already has an existing config entry. """ + """ Message box displayed on Save/Add Config when the object already has an existing config + entry. """ def __init__(self, object_name: str, object_id: int): super().__init__() self.setWindowTitle('Configuration already exists') - self.setText(f'{object_name} (ID: {object_id}) already has a PV configuration.\n' - f'Overwrite existing configuration?') + self.setText( + f'{object_name} (ID: {object_id}) already has a PV configuration.\n' + f'Overwrite existing configuration?') self.setIcon(QMessageBox.Warning) self.addButton(QMessageBox.Ok) ok_btn = self.button(QMessageBox.Ok) @@ -142,14 +145,16 @@ def __init__(self, object_name: str, object_id: int): class ConfigDeleteMessageBox(QMessageBox): - """ Confirmation message box to display on object config entry deletion within the Config Entry dialog window. """ + """ Confirmation message box to display on object config entry deletion within the Config + Entry dialog window. """ def __init__(self, object_name: str, object_id: int): super().__init__() self.setWindowTitle('Delete configuration') - self.setText(f'Deleting configuration for {object_name} (ID: {object_id}).\n' - f'Are you sure?') + self.setText( + f'Deleting configuration for {object_name} (ID: {object_id}).\n' + f'Are you sure?') self.setIcon(QMessageBox.Warning) self.addButton(QMessageBox.Ok) ok_btn = self.button(QMessageBox.Ok) diff --git a/ServiceManager/GUI/db_settings.py b/service_manager/gui/db_settings.py similarity index 73% rename from ServiceManager/GUI/db_settings.py rename to service_manager/gui/db_settings.py index 0a9a249..d00054f 100644 --- a/ServiceManager/GUI/db_settings.py +++ b/service_manager/gui/db_settings.py @@ -3,9 +3,10 @@ from PyQt5.QtWidgets import QDialog, QDialogButtonBox from PyQt5 import uic -from ServiceManager.constants import db_settings_ui -from ServiceManager.utilities import is_admin, make_bold, set_colored_text, apply_unsaved_changes_dialog -from ServiceManager.settings import Settings +from service_manager.constants import db_settings_ui +from service_manager.utilities import is_admin, make_bold, set_colored_text, \ + apply_unsaved_changes_dialog +from service_manager.settings import Settings class UIDBSettings(QDialog): @@ -17,10 +18,10 @@ def __init__(self): self.setModal(True) # Initialize attributes for storing current settings - self.settings_host = None # Store the DB host from settings.ini - self.settings_db = None # Store the DB name from settings.ini - self.reg_user = None # Store the DB user from Windows Registry - self.reg_pass = None # Store the DB password from Windows Registry + self.settings_host = None # Store the DB host from settings.ini + self.settings_db = None # Store the DB name from settings.ini + self.reg_user = None # Store the DB user from Windows Registry + self.reg_pass = None # Store the DB password from Windows Registry # Remove the "?" QWhatsThis button from the dialog # noinspection PyTypeChecker @@ -41,8 +42,8 @@ def __init__(self): def new_settings(self): """ - Make the labels of LineEdits that were modified bold, and enables the Apply button, and OK submit functionality, - if at least one setting has been changed. + Make the labels of LineEdits that were modified bold, and enables the Apply button, + and OK submit functionality, if at least one setting has been changed. """ any_setting_changed = False settings_to_check = [(self.host.text() != self.settings_host, self.host_label, False), @@ -51,7 +52,7 @@ def new_settings(self): (self.password.text() != self.reg_pass, self.password_label, True)] for setting_changed, label, admin_req in settings_to_check: - if not(admin_req is True and is_admin() is False): + if not (admin_req is True and is_admin() is False): any_setting_changed |= setting_changed if not admin_req or is_admin(): make_bold(label, setting_changed) @@ -60,7 +61,8 @@ def new_settings(self): self.apply_btn.setEnabled(any_setting_changed) def on_accepted(self): - if self.apply_btn.isEnabled(): # if apply button is disabled, it means there is nothing new to save + # if apply button is disabled, it means there is nothing new to save + if self.apply_btn.isEnabled(): self.save_new_settings() self.close() @@ -80,33 +82,40 @@ def save_new_settings(self): registry_pass = Settings.Service.HeliumDB.password if registry_user == self.user.text() and registry_pass == self.password.text(): - set_colored_text(label=self.message, text='Updated DB configuration.', color=QColor('green')) + set_colored_text(label=self.message, text='Updated DB configuration.', + color=QColor('green')) else: - set_colored_text(label=self.message, text='User/Password could not be updated.\nPlease verify the ' - 'service is found.', color=QColor('red')) + set_colored_text(label=self.message, + text='User/Password could not be updated.\nPlease verify the ' + '' + 'service is found.', + color=QColor('red')) except Exception as e: set_colored_text(label=self.message, text=f'{e}', color=QColor('red')) else: - set_colored_text(label=self.message, text='Updated DB configuration.', color=QColor('green')) + set_colored_text(label=self.message, text='Updated DB configuration.', + color=QColor('green')) self.reset_styles() # Establish new DB connection and get result connected = Settings.Service.connect_to_db() if connected is True: - set_colored_text(label=self.message, text=f'{self.message.text()}\nConnected to DB.', color=QColor('green')) + set_colored_text(label=self.message, text=f'{self.message.text()}\nConnected to DB.', + color=QColor('green')) elif connected is False: - set_colored_text( - label=self.message, - text=f'{self.message.text()}\nCould not establish DB connection, please check log for details.', - color=QColor('red') - ) + set_colored_text(label=self.message, + text=f'{self.message.text()}\nCould not establish DB connection, ' + f'please check log for details.', + color=QColor('red') + ) self.update_db_connection_status.emit() def closeEvent(self, event: QCloseEvent): """ Upon dialog close """ - set_colored_text(label=self.message, text='', color=QColor('black')) # Remove message upon window close + # Remove message upon window close + set_colored_text(label=self.message, text='', color=QColor('black')) apply_unsaved_changes_dialog(event, self.save_new_settings, self.apply_btn.isEnabled()) def showEvent(self, event: QShowEvent): @@ -137,14 +146,16 @@ def update_fields(self): self.user.setText(None) for widget in [self.user, self.password]: widget.setPlaceholderText(msg) - widget.setToolTip('Please restart the app in Administrator Mode to edit this setting.') + widget.setToolTip( + 'Please restart the app in Administrator Mode to edit this setting.') else: self.reg_user = Settings.Service.HeliumDB.user self.reg_pass = Settings.Service.HeliumDB.password self.user.setText(self.reg_user) self.password.setText(self.reg_pass) - # Check if host and db name are already in the settings.ini, and if so add them to the fields + # Check if host and db name are already in the settings.ini, and if so add them to the + # fields self.settings_host = Settings.Service.HeliumDB.host self.settings_db = Settings.Service.HeliumDB.name diff --git a/ServiceManager/GUI/general_settings.py b/service_manager/gui/general_settings.py similarity index 86% rename from ServiceManager/GUI/general_settings.py rename to service_manager/gui/general_settings.py index 60a3ebb..1499ff7 100644 --- a/ServiceManager/GUI/general_settings.py +++ b/service_manager/gui/general_settings.py @@ -1,9 +1,9 @@ from PyQt5.QtGui import QCloseEvent, QShowEvent from PyQt5.QtWidgets import QDialog, QDialogButtonBox from PyQt5 import uic -from ServiceManager.constants import general_settings_ui -from ServiceManager.settings import Settings -from ServiceManager.utilities import apply_unsaved_changes_dialog +from service_manager.constants import general_settings_ui +from service_manager.settings import Settings +from service_manager.utilities import apply_unsaved_changes_dialog class UIGeneralSettings(QDialog): @@ -17,9 +17,11 @@ def __init__(self): self.apply_btn = self.button_box.button(QDialogButtonBox.Apply) # region Connect signals to slots - self.default_meas_update_interval_sb.valueChanged.connect(lambda _: self.settings_changed(True)) + self.default_meas_update_interval_sb.valueChanged.connect( + lambda _: self.settings_changed(True)) self.check_pv_on_new_entry_cb.stateChanged.connect(lambda _: self.settings_changed(True)) - self.auto_load_existing_config_cb.stateChanged.connect(lambda _: self.settings_changed(True)) + self.auto_load_existing_config_cb.stateChanged.connect( + lambda _: self.settings_changed(True)) self.button_box.rejected.connect(self.on_rejected) self.button_box.accepted.connect(self.on_accepted) diff --git a/ServiceManager/GUI/layouts/About.ui b/service_manager/gui/layouts/About.ui similarity index 100% rename from ServiceManager/GUI/layouts/About.ui rename to service_manager/gui/layouts/About.ui diff --git a/ServiceManager/GUI/layouts/CASettings.ui b/service_manager/gui/layouts/CASettings.ui similarity index 100% rename from ServiceManager/GUI/layouts/CASettings.ui rename to service_manager/gui/layouts/CASettings.ui diff --git a/ServiceManager/GUI/layouts/ConfigEntry.ui b/service_manager/gui/layouts/ConfigEntry.ui similarity index 100% rename from ServiceManager/GUI/layouts/ConfigEntry.ui rename to service_manager/gui/layouts/ConfigEntry.ui diff --git a/ServiceManager/GUI/layouts/DBSettings.ui b/service_manager/gui/layouts/DBSettings.ui similarity index 100% rename from ServiceManager/GUI/layouts/DBSettings.ui rename to service_manager/gui/layouts/DBSettings.ui diff --git a/ServiceManager/GUI/layouts/GeneralSettings.ui b/service_manager/gui/layouts/GeneralSettings.ui similarity index 100% rename from ServiceManager/GUI/layouts/GeneralSettings.ui rename to service_manager/gui/layouts/GeneralSettings.ui diff --git a/ServiceManager/GUI/layouts/MainWindow.ui b/service_manager/gui/layouts/MainWindow.ui similarity index 100% rename from ServiceManager/GUI/layouts/MainWindow.ui rename to service_manager/gui/layouts/MainWindow.ui diff --git a/ServiceManager/GUI/layouts/ServicePathDialog.ui b/service_manager/gui/layouts/ServicePathDialog.ui similarity index 100% rename from ServiceManager/GUI/layouts/ServicePathDialog.ui rename to service_manager/gui/layouts/ServicePathDialog.ui diff --git a/ServiceManager/GUI/main_window.py b/service_manager/gui/main_window.py similarity index 80% rename from ServiceManager/GUI/main_window.py rename to service_manager/gui/main_window.py index 592c2e7..7f1ebfa 100644 --- a/ServiceManager/GUI/main_window.py +++ b/service_manager/gui/main_window.py @@ -5,22 +5,24 @@ import win32serviceutil from PyQt5.QtCore import Qt from PyQt5.QtGui import QCloseEvent, QShowEvent, QColor, QIcon -from PyQt5.QtWidgets import QMainWindow, QMessageBox, QTableWidgetItem, QApplication, QListWidget, QSizePolicy +from PyQt5.QtWidgets import QMainWindow, QMessageBox, QTableWidgetItem, QApplication, QListWidget, \ + QSizePolicy from PyQt5 import uic -from ServiceManager.logger import manager_logger -from ServiceManager.settings import Settings -from ServiceManager.constants import main_window_ui, ASSETS_PATH, MANAGER_SETTINGS_DIR, MANAGER_SETTINGS_FILE, \ - MANAGER_LOGS_FILE -from ServiceManager.GUI.about import UIAbout -from ServiceManager.GUI.db_settings import UIDBSettings -from ServiceManager.GUI.general_settings import UIGeneralSettings -from ServiceManager.GUI.ca_settings import UICASettings -from ServiceManager.GUI.service_path_dlg import UIServicePathDialog -from ServiceManager.GUI.config_entry import UIConfigEntryDialog -from ServiceManager.utilities import is_admin, set_colored_text, setup_button -from ServiceManager.GUI.main_window_threads import ServiceLogUpdaterThread, ServiceStatusCheckThread -from ServiceManager.db_func import db_connected, get_object_name, get_object_type +from service_manager.logger import manager_logger +from service_manager.settings import Settings +from service_manager.constants import main_window_ui, ASSETS_PATH, MANAGER_SETTINGS_DIR, \ + MANAGER_SETTINGS_FILE, MANAGER_LOGS_FILE +from service_manager.gui.about import UIAbout +from service_manager.gui.db_settings import UIDBSettings +from service_manager.gui.general_settings import UIGeneralSettings +from service_manager.gui.ca_settings import UICASettings +from service_manager.gui.service_path_dlg import UIServicePathDialog +from service_manager.gui.config_entry import UIConfigEntryDialog +from service_manager.utilities import is_admin, set_colored_text, setup_button +from service_manager.gui.main_window_threads import ServiceLogUpdaterThread, \ + ServiceStatusCheckThread +from service_manager.db_func import db_connected, get_object_name, get_object_type from shared.const import SERVICE_NAME from shared.utils import get_object_module @@ -42,12 +44,12 @@ def __init__(self): self.setWindowTitle(self.windowTitle() + ' (Administrator)') # region External windows - self.about_w = UIAbout() # "About" dialogue window - self.db_settings_w = UIDBSettings() # "DB Settings" dialogue window - self.general_settings_w = UIGeneralSettings() # "General Settings" dialogue window - self.ca_settings_w = UICASettings() # "Channel Access" dialogue window - self.service_dir_path_w = UIServicePathDialog() # "Service Directory" dialogue window - self.config_entry_w = UIConfigEntryDialog() # Add/Edit Configuration Entry dialogue window + self.about_w = UIAbout() # "About" dialogue window + self.db_settings_w = UIDBSettings() # "DB Settings" dialogue window + self.general_settings_w = UIGeneralSettings() # "General Settings" dialogue window + self.ca_settings_w = UICASettings() # "Channel Access" dialogue window + self.service_dir_path_w = UIServicePathDialog() # "Service Directory" dialogue window + self.config_entry_w = UIConfigEntryDialog() # Add/Edit Configuration Entry dialogue window # endregion # region External windows setup @@ -58,21 +60,26 @@ def __init__(self): # endregion # region Attributes - self.table_expanded = False # For toggling table expansion ("Expand Table"/"Service Info") - self.pv_config_data = [] # Store the PV configuration data to be displayed in the config. table + self.table_expanded = False # For toggling table expansion ("Expand Table"/"Service Info") + self.pv_config_data = [] # Store the PV configuration data to be displayed in the + # config. table self.expand_table_btn_text = EXPAND_CONFIG_TABLE_BTN # endregion # region Menu actions & signals self.about_action.triggered.connect(lambda _: self.trigger_window(self.about_w)) self.manager_log_action.triggered.connect(lambda _: os.startfile(MANAGER_LOGS_FILE)) - self.manager_settings_action.triggered.connect(lambda _: os.startfile(MANAGER_SETTINGS_FILE)) - self.manager_settings_dir_action.triggered.connect(lambda _: os.startfile(MANAGER_SETTINGS_DIR)) + self.manager_settings_action.triggered.connect( + lambda _: os.startfile(MANAGER_SETTINGS_FILE)) + self.manager_settings_dir_action.triggered.connect( + lambda _: os.startfile(MANAGER_SETTINGS_DIR)) self.show_service_log.triggered.connect(self.open_service_log) self.show_service_dir.triggered.connect(self.trigger_open_service_dir) - self.show_service_settings.triggered.connect(lambda _: os.startfile(Settings.Service.settings_path)) + self.show_service_settings.triggered.connect( + lambda _: os.startfile(Settings.Service.settings_path)) self.db_settings_action.triggered.connect(lambda _: self.trigger_window(self.db_settings_w)) - self.general_settings_action.triggered.connect(lambda _: self.trigger_window(self.general_settings_w)) + self.general_settings_action.triggered.connect( + lambda _: self.trigger_window(self.general_settings_w)) self.ca_settings_action.triggered.connect(lambda _: self.trigger_window(self.ca_settings_w)) self.service_directory_action.triggered.connect(self.trigger_service_directory) self.open_pv_config_action.triggered.connect(self.trigger_open_pv_config) @@ -97,7 +104,9 @@ def __init__(self): if not is_admin(): for btn in [self.btn_service_start, self.btn_service_stop, self.btn_service_restart]: btn.setEnabled(False) - btn.setToolTip(btn.toolTip() + '\nRun the manager as administrator to start/stop/restart the service.') + btn.setToolTip( + btn.toolTip() + '\nRun the manager as administrator to start/stop/restart the ' + 'service.') # Filter/Search Frame Setup self.filter_frame.setVisible(False) @@ -109,13 +118,17 @@ def __init__(self): # endregion # region Signals to Slots - self.btn_service_start.clicked.connect(lambda _: self.call_on_service(win32serviceutil.StartService)) - self.btn_service_stop.clicked.connect(lambda _: self.call_on_service(win32serviceutil.StopService)) - self.btn_service_restart.clicked.connect(lambda _: self.call_on_service(win32serviceutil.RestartService)) + self.btn_service_start.clicked.connect( + lambda _: self.call_on_service(win32serviceutil.StartService)) + self.btn_service_stop.clicked.connect( + lambda _: self.call_on_service(win32serviceutil.StopService)) + self.btn_service_restart.clicked.connect( + lambda _: self.call_on_service(win32serviceutil.RestartService)) self.db_connection_refresh_btn.clicked.connect(self.refresh_db_connection) - self.config_table.itemSelectionChanged.connect(self.enable_or_disable_edit_and_delete_buttons) + self.config_table.itemSelectionChanged.connect( + self.enable_or_disable_edit_and_delete_buttons) self.expand_table_btn.clicked.connect(self.expand_table_btn_clicked) self.refresh_btn.clicked.connect(self.refresh_config) self.show_filter_btn.clicked.connect(self.show_filter_btn_clicked) @@ -133,15 +146,18 @@ def __init__(self): # noinspection PyTypeChecker self.thread_service_status = ServiceStatusCheckThread() self.thread_service_status.update_status_title.connect(self.service_status_title.setText) - self.thread_service_status.update_status_style.connect(self.service_status_title.setStyleSheet) + self.thread_service_status.update_status_style.connect( + self.service_status_title.setStyleSheet) self.thread_service_status.update_service_details.connect(self.update_service_details) - self.thread_service_status.update_service_control_btns.connect(self.update_service_control_btns) + self.thread_service_status.update_service_control_btns.connect( + self.update_service_control_btns) self.thread_service_status.start() # endregion # region Service Log Thread # noinspection PyTypeChecker - self.thread_service_log = ServiceLogUpdaterThread(self.service_log_show_lines_spinbox.value()) + self.thread_service_log = ServiceLogUpdaterThread( + self.service_log_show_lines_spinbox.value()) self.thread_service_log.log_fetched.connect(self.update_service_log) self.thread_service_log.file_not_found.connect(self.clear_service_log) self.thread_service_log.enable_or_disable_buttons.connect(self.update_service_log_btns) @@ -157,11 +173,13 @@ def __init__(self): self.service_log_file_open_btn.clicked.connect(self.open_service_log) self.service_log_scroll_down_btn.clicked.connect(self.log_scroll_to_bottom) - # Emit spinner valueChanged only on return key pressed, focus lost, and widget arrow keys clicked + # Emit spinner valueChanged only on return key pressed, focus lost, and widget arrow keys + # clicked self.service_log_show_lines_spinbox.setKeyboardTracking(False) self.service_log_font_size.currentTextChanged.connect(self.update_log_font_size) - self.service_log_show_lines_spinbox.valueChanged.connect(self.thread_service_log.set_displayed_lines_no) + self.service_log_show_lines_spinbox.valueChanged.connect( + self.thread_service_log.set_displayed_lines_no) # endregion # region Show & Close Events @@ -170,7 +188,8 @@ def showEvent(self, event: QShowEvent): def closeEvent(self, event: QCloseEvent): quit_msg = "Close the application?" - reply = QMessageBox.question(self, 'HLM PV Import', quit_msg, QMessageBox.Yes, QMessageBox.No) + reply = QMessageBox.question(self, 'HLM PV Import', quit_msg, QMessageBox.Yes, + QMessageBox.No) if reply == QMessageBox.Yes: event.accept() @@ -184,9 +203,11 @@ def closeEvent(self, event: QCloseEvent): def update_fields(self): self.update_config_data() self.update_config_table() - expand_table_btn_settings = self.expand_table_btn_text[self.table_expanded] # text and icon depending on table + expand_table_btn_settings = self.expand_table_btn_text[ + self.table_expanded] # text and icon depending on table self.expand_table_btn.setText(expand_table_btn_settings[0]) - self.expand_table_btn.setIcon(QIcon(os.path.join(ASSETS_PATH, expand_table_btn_settings[1]))) + self.expand_table_btn.setIcon( + QIcon(os.path.join(ASSETS_PATH, expand_table_btn_settings[1]))) self.edit_config_btn.setEnabled(False) self.delete_config_btn.setEnabled(False) @@ -203,8 +224,8 @@ def update_db_connection_status(self): else: set_colored_text(self.db_connection_status, 'not connected', QColor('red')) - self.db_connection_last_checked.setText(f"Connection status last updated on: " - f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S:%f')}") + self.db_connection_last_checked.setText( + f"Connection status last updated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S:%f')}") def update_service(self): self.thread_service_log.start() @@ -255,7 +276,8 @@ def update_config_data(self): def expand_table_btn_clicked(self): """ - Toggle table expanded by hiding the service status & log frames, allowing the table to fill the remaining + Toggle table expanded by hiding the service status & log frames, allowing the table to + fill the remaining space. """ # expanded is false -> set visible false (hide) -> expanded = true; @@ -265,9 +287,11 @@ def expand_table_btn_clicked(self): self.h_line_one.setVisible(self.table_expanded) self.h_line_two.setVisible(self.table_expanded) self.table_expanded = not self.table_expanded # toggle bool - expand_table_btn_settings = self.expand_table_btn_text[self.table_expanded] # text and icon depending on table + expand_table_btn_settings = self.expand_table_btn_text[ + self.table_expanded] # text and icon depending on table self.expand_table_btn.setText(expand_table_btn_settings[0]) - self.expand_table_btn.setIcon(QIcon(os.path.join(ASSETS_PATH, expand_table_btn_settings[1]))) + self.expand_table_btn.setIcon( + QIcon(os.path.join(ASSETS_PATH, expand_table_btn_settings[1]))) def refresh_config(self): """ Re-fetch PV config data, update table contents. """ @@ -294,7 +318,8 @@ def apply_filters(self): show_or_hide = index not in rows self.config_table.setRowHidden(index, show_or_hide) else: - column_of_interest -= 1 # So indexes match (as filters columns comboBox has an extra "All Columns" on 0) + column_of_interest -= 1 # So indexes match (as filters columns comboBox has an extra + # "All Columns" on 0) for rowIndex in range(self.config_table.rowCount()): item = self.config_table.item(rowIndex, column_of_interest) contains_search = value_of_interest in item.text() @@ -306,7 +331,8 @@ def clear_filters(self): self.refresh_config() def new_config_btn_clicked(self): - """ Open the Config Entry dialog window. If database is not connected, display error message. """ + """ Open the Config Entry dialog window. If database is not connected, display error + message. """ if not db_connected(): QMessageBox.critical(self, 'Database connection required', 'Database connection is required to edit the PV configuration.', @@ -317,7 +343,8 @@ def new_config_btn_clicked(self): self.config_entry_w.activateWindow() def edit_config_btn_clicked(self): - """ On Edit, open Config Entry dialog window as normal with object selected and PV config loaded. """ + """ On Edit, open Config Entry dialog window as normal with object selected and PV config + loaded. """ if not db_connected(): QMessageBox.critical(self, 'Database connection required', 'Database connection is required to edit the PV configuration.', @@ -365,10 +392,12 @@ def delete_config_btn_clicked(self): def update_config_table(self): """ Update the config table contents with the stored entries' data. """ - self.config_table.setSortingEnabled(False) # otherwise table will not be properly updated if columns are sorted + self.config_table.setSortingEnabled( + False) # otherwise table will not be properly updated if columns are sorted self.config_table.setRowCount(0) # it will delete the QTableWidgetItems automatically - pv_config_data = self.pv_config_data # Get the stored PV config data (from update_config_data) + pv_config_data = self.pv_config_data # Get the stored PV config data (from + # update_config_data) for entry in pv_config_data: object_id = entry[Settings.Service.PVConfig.OBJ] @@ -385,7 +414,8 @@ def update_config_table(self): self.config_table.insertRow(self.config_table.rowCount()) - # for each element of the entry data, add it to an item then add the item to the appropriate table cell + # for each element of the entry data, add it to an item then add the item to the + # appropriate table cell for index, elem in enumerate(entry_data): item = QTableWidgetItem() item.setFlags(item.flags() ^ Qt.ItemIsEditable) @@ -465,7 +495,8 @@ def update_service_control_btns(self, service_status): class DeleteConfigsMessageBox(QMessageBox): - """ Message Box on deleting configuration entries. Display a list of entries to be deleted and confirm button. """ + """ Message Box on deleting configuration entries. Display a list of entries to be deleted + and confirm button. """ def __init__(self, obj_list: list): super().__init__() @@ -486,9 +517,12 @@ def __init__(self, obj_list: list): self.addButton(QMessageBox.Cancel) self.setDefaultButton(QMessageBox.Cancel) - # QMessageBox "resists" resizing, and the widget will always get hard-resized to the width of the - # main text attribute, and informativeText will always get word-wrapped to that width whether you want it to or not. - # To set the size manually, subclass QMessageBox and reimplement the resize event handler to override the layout. + # QMessageBox "resists" resizing, and the widget will always get hard-resized to the width of + # the + # main text attribute, and informativeText will always get word-wrapped to that width whether + # you want it to or not. + # To set the size manually, subclass QMessageBox and reimplement the resize event handler to + # override the layout. def resizeEvent(self, e): result = QMessageBox.resizeEvent(self, e) self.setMinimumWidth(0) diff --git a/ServiceManager/GUI/main_window_threads.py b/service_manager/gui/main_window_threads.py similarity index 81% rename from ServiceManager/GUI/main_window_threads.py rename to service_manager/gui/main_window_threads.py index b51be84..b71b4e5 100644 --- a/ServiceManager/GUI/main_window_threads.py +++ b/service_manager/gui/main_window_threads.py @@ -3,18 +3,17 @@ import psutil from collections import deque, defaultdict from PyQt5.QtCore import QTimer, QThread, QEventLoop, pyqtSignal -from ServiceManager.logger import manager_logger -from ServiceManager.settings import Settings -from ServiceManager.utilities import is_admin +from service_manager.logger import manager_logger +from service_manager.settings import Settings +from service_manager.utilities import is_admin from shared.const import SERVICE_NAME SERVICE_NOT_FOUND = 'service-not-found' -SERVICE_LOG_UPDATE_INTERVAL = 1000 # msec -SERVICE_STATUS_CHECK_INTERVAL = 5000 # msec +SERVICE_LOG_UPDATE_INTERVAL = 1000 # msec +SERVICE_STATUS_CHECK_INTERVAL = 5000 # msec class ServiceLogUpdaterThread(QThread): - # Custom signals log_fetched = pyqtSignal(str) file_not_found = pyqtSignal() @@ -65,7 +64,6 @@ def stop(self): class ServiceStatusCheckThread(QThread): - # Custom signals update_status_title = pyqtSignal(str) update_status_style = pyqtSignal(str) @@ -79,7 +77,8 @@ def update_status(self): except psutil.NoSuchProcess as e: manager_logger.warning(e) self.update_status_title.emit(f"Service {SERVICE_NAME} not found") - self.update_status_style.emit(f"background-color: {self.status_color[None]}; padding: 20px;") + self.update_status_style.emit( + f"background-color: {self.status_color[None]}; padding: 20px;") self.update_service_details.emit(defaultdict(lambda: None)) self.stop() if is_admin(): @@ -90,7 +89,8 @@ def update_status(self): status = service_info['status'] self.update_status_title.emit(f"{service_info['display_name']} is {status.upper()}") - self.update_status_style.emit(f"background-color: {self.status_color[status]}; padding: 20px;") + self.update_status_style.emit( + f"background-color: {self.status_color[status]}; padding: 20px;") self.update_service_details.emit(service_info) @@ -101,13 +101,13 @@ def __init__(self, *args, **kwargs): QThread.__init__(self, *args, **kwargs) self.timer = QTimer() self.timer.moveToThread(self) - self.finished.connect(self.timer.stop) # When thread is finished, stop timer + self.finished.connect(self.timer.stop) # When thread is finished, stop timer self.timer.timeout.connect(self.update_status) # region Styles - self.status_color = defaultdict(lambda: '#ffff00') # electric yellow - self.status_color[psutil.STATUS_RUNNING] = '#90ee90' # medium light shade of green - self.status_color[psutil.STATUS_STOPPED] = '#add8e6' # light shade of cyan + self.status_color = defaultdict(lambda: '#ffff00') # electric yellow + self.status_color[psutil.STATUS_RUNNING] = '#90ee90' # medium light shade of green + self.status_color[psutil.STATUS_STOPPED] = '#add8e6' # light shade of cyan # endregion def run(self): diff --git a/ServiceManager/GUI/service_path_dlg.py b/service_manager/gui/service_path_dlg.py similarity index 93% rename from ServiceManager/GUI/service_path_dlg.py rename to service_manager/gui/service_path_dlg.py index beb80b8..dfb84f0 100644 --- a/ServiceManager/GUI/service_path_dlg.py +++ b/service_manager/gui/service_path_dlg.py @@ -5,9 +5,9 @@ from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QFileDialog, QMessageBox from PyQt5 import uic -from ServiceManager.constants import SERVICE_SETTINGS_FILE_NAME, service_path_dlg_ui -from ServiceManager.logger import manager_logger -from ServiceManager.settings import Settings +from service_manager.constants import SERVICE_SETTINGS_FILE_NAME, service_path_dlg_ui +from service_manager.logger import manager_logger +from service_manager.settings import Settings class UIServicePathDialog(QDialog): @@ -65,7 +65,8 @@ def on_finished(self, result): Settings.Manager.service_path = path manager_logger.info('Service directory path changed.') service_settings_path = Settings.Manager.service_path - Settings.init_service_settings(service_settings_path) # Init/Update Service settings with path + # Init/Update Service settings with path + Settings.init_service_settings(service_settings_path) self.service_updated.emit() def browse_file_dialog(self): diff --git a/ServiceManager/logger.py b/service_manager/logger.py similarity index 96% rename from ServiceManager/logger.py rename to service_manager/logger.py index bef4a18..9a4fbb0 100644 --- a/ServiceManager/logger.py +++ b/service_manager/logger.py @@ -1,7 +1,7 @@ import sys import os import logging.config -from ServiceManager.constants import MANAGER_LOGS_FILE, MANAGER_ERR_LOGS_FILE +from service_manager.constants import MANAGER_LOGS_FILE, MANAGER_ERR_LOGS_FILE # Setup log file for logfile in [MANAGER_LOGS_FILE, MANAGER_ERR_LOGS_FILE]: diff --git a/ServiceManager/settings.py b/service_manager/settings.py similarity index 87% rename from ServiceManager/settings.py rename to service_manager/settings.py index 008cead..cb8bd5a 100644 --- a/ServiceManager/settings.py +++ b/service_manager/settings.py @@ -3,13 +3,16 @@ import json import configparser import win32serviceutil -from ServiceManager.constants import MANAGER_SETTINGS_FILE, MANAGER_SETTINGS_TEMPLATE, SERVICE_SETTINGS_FILE_NAME, \ - SERVICE_SETTINGS_TEMPLATE, SERVICE_NAME, PV_CONFIG_FILE_NAME -from ServiceManager.logger import manager_logger, log_exception -from ServiceManager.utilities import setup_settings_file -from ServiceManager.db_func import db_connect, db_connected, DBConnectionError +from service_manager.constants import MANAGER_SETTINGS_FILE, MANAGER_SETTINGS_TEMPLATE, \ + SERVICE_SETTINGS_FILE_NAME, \ + SERVICE_SETTINGS_TEMPLATE +from service_manager.logger import manager_logger, log_exception +from service_manager.utilities import setup_settings_file +from service_manager.db_func import db_connect, db_connected, DBConnectionError from shared import db_models -from shared.utils import get_full_pv_name as get_full_pv_name_, get_short_pv_name as get_short_pv_name_ +from shared.utils import get_full_pv_name as get_full_pv_name_, \ + get_short_pv_name as get_short_pv_name_ +from shared.const import SERVICE_NAME, PV_CONFIG_FILE_NAME class _Settings: @@ -29,7 +32,8 @@ def __init__(self, settings_path): self.settings_path = settings_path if not os.path.exists(settings_path): - setup_settings_file(path=settings_path, template=MANAGER_SETTINGS_TEMPLATE, parser=self.config_parser) + setup_settings_file(path=settings_path, template=MANAGER_SETTINGS_TEMPLATE, + parser=self.config_parser) self.config_parser.read(self.settings_path) @@ -82,7 +86,8 @@ def __init__(self, service_path): self.settings_path = os.path.join(service_path, SERVICE_SETTINGS_FILE_NAME) if not os.path.exists(self.settings_path): - setup_settings_file(path=self.settings_path, template=SERVICE_SETTINGS_TEMPLATE, parser=self.config_parser) + setup_settings_file(path=self.settings_path, template=SERVICE_SETTINGS_TEMPLATE, + parser=self.config_parser) self.config_parser.read(self.settings_path) @@ -129,7 +134,8 @@ def setup_file(self): """ Creates the user PV-Records config file if it doesn't exist. """ path = self.get_path() settings_dir = os.path.dirname(path) - if not os.path.exists(settings_dir): # If settings directory does not exist either, create it too + if not os.path.exists( + settings_dir): # If settings directory does not exist either, create it too os.makedirs(settings_dir) data = {self.ROOT: []} @@ -182,7 +188,8 @@ def add_entry(self, new_entry: dict, overwrite: bool = False): Args: new_entry (dict): The record config. - overwrite (bool, optional): If True, overwrites the entry that matches the object ID, Defaults to False. + overwrite (bool, optional): If True, overwrites the entry that matches the object ID, + Defaults to False. """ data = self.get_entries() if overwrite: @@ -193,7 +200,8 @@ def add_entry(self, new_entry: dict, overwrite: bool = False): overwritten = True break if not overwritten: - manager_logger.error(f'Entry with object ID {new_entry[self.OBJ]} was not overwritten.') + manager_logger.error( + f'Entry with object ID {new_entry[self.OBJ]} was not overwritten.') return else: data.append(new_entry) @@ -213,7 +221,8 @@ def delete_entry(self, object_id: int): break if not deleted: - manager_logger.warning(f'Entry with object ID {object_id} should have been deleted but was not.') + manager_logger.warning( + f'Entry with object ID {object_id} should have been deleted but was not.') data = {self.ROOT: data} self._json_dump(data) @@ -265,7 +274,8 @@ def _get_credentials(service_option): if not SERVICE_NAME: return try: - return win32serviceutil.GetServiceCustomOption(serviceName=SERVICE_NAME, option=service_option) + return win32serviceutil.GetServiceCustomOption(serviceName=SERVICE_NAME, + option=service_option) except Exception as e: manager_logger.error(e) @@ -281,13 +291,15 @@ def host(self, new_host: str): @user.setter def user(self, new_user): - self._set_credentials(self.user_option, new_user, 'DB Connection user could not be set as Service Name ' - 'was not found.') + self._set_credentials(self.user_option, new_user, + 'DB Connection user could not be set as Service Name ' + 'was not found.') @password.setter def password(self, new_pass): - self._set_credentials(self.pass_option, new_pass, 'DB Connection password could not be set as Service Name ' - 'was not found.') + self._set_credentials(self.pass_option, new_pass, + 'DB Connection password could not be set as Service Name ' + 'was not found.') @staticmethod def _set_credentials(service_option, new_value, err_msg: str): @@ -372,6 +384,8 @@ def get_full_pv_name(self, name): def get_short_pv_name(self, name): return get_short_pv_name_(name, prefix=self.prefix, domain=self.domain) + + # endregion diff --git a/ServiceManager/utilities.py b/service_manager/utilities.py similarity index 88% rename from ServiceManager/utilities.py rename to service_manager/utilities.py index e8c5c2f..c7b6071 100644 --- a/ServiceManager/utilities.py +++ b/service_manager/utilities.py @@ -6,8 +6,8 @@ from PyQt5.QtGui import QPalette, QColor, QCloseEvent, QIcon from PyQt5.QtWidgets import QMessageBox, QPushButton -from ServiceManager.constants import ASSETS_PATH -from ServiceManager.logger import manager_logger +from service_manager.constants import ASSETS_PATH +from service_manager.logger import manager_logger from caproto.sync.client import read from shared.const import DBClassIDs @@ -71,22 +71,25 @@ def set_colored_text(label, text, color): def set_red_border(frame: QObject, highlight: bool = True): - frame.setStyleSheet(f"QObject#{frame.objectName()} {{{'border: 1px solid red;' if highlight else ''}}}") + frame.setStyleSheet( + f"QObject#{frame.objectName()} {{{'border: 1px solid red;' if highlight else ''}}}") def setup_settings_file(path: str, template: dict, parser: configparser.ConfigParser): """ - Creates the settings file and its directory, if it doesn't exist, and writes the given config template with - blank values to it. + Creates the settings file and its directory, if it doesn't exist, and writes the given config + template with blank values to it. Args: path (str): The full path to the file. - template (dict): The template containing sections (keys, str) and their options (values, list of str). + template (dict): The template containing sections (keys, str) and their options (values, + list of str). parser (ConfigParser): The ConfigParser object. """ # Create file and directory if not exists and write config template to it with blank values settings_dir = os.path.dirname(path) - if not os.path.exists(settings_dir): # If settings directory does not exist either, create it too + if not os.path.exists( + settings_dir): # If settings directory does not exist either, create it too os.makedirs(settings_dir) for section, options in template.items(): @@ -126,4 +129,3 @@ def generate_module_name(object_name: str, object_id: int, object_class: int): return f'SLD "{object_name}" (ID: {object_id})' elif object_class == DBClassIDs.GAS_COUNTER: return f'GCM "{object_name}" (ID: {object_id})' - diff --git a/setup_jenkins_settings_file.py b/setup_jenkins_settings_file.py index a8597a1..c7bca95 100644 --- a/setup_jenkins_settings_file.py +++ b/setup_jenkins_settings_file.py @@ -3,8 +3,8 @@ import configparser import os -from ServiceManager.utilities import setup_settings_file -from ServiceManager.constants import SERVICE_SETTINGS_TEMPLATE +from service_manager.utilities import setup_settings_file +from service_manager.constants import SERVICE_SETTINGS_TEMPLATE if __name__ == '__main__': settings_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '', 'settings.ini') diff --git a/tests/test_ca_wrapper.py b/tests/test_ca_wrapper.py index afdedba..f8083a9 100644 --- a/tests/test_ca_wrapper.py +++ b/tests/test_ca_wrapper.py @@ -1,8 +1,8 @@ import unittest from mock import patch -from HLM_PV_Import import ca_wrapper -from HLM_PV_Import.ca_wrapper import PvMonitors +from hlm_pv_import import ca_wrapper +from hlm_pv_import.ca_wrapper import PvMonitors from parameterized import parameterized from caproto.threading import client @@ -15,7 +15,7 @@ class TestWrapper(unittest.TestCase): ({'1': False, '2': False, '3': False, '4': False, '5': False}, []), ({'1': True}, ['1']), ({'1': False}, []) ]) - @patch('HLM_PV_Import.ca_wrapper.Context') + @patch('hlm_pv_import.ca_wrapper.Context') def test_WHEN_get_connected_pvs_THEN_return_correct_list(self, pvs_param, expected, mock_ctx): # Arrange class TestPV: @@ -38,7 +38,7 @@ def __init__(self, name_, connected_): class TestPvMonitors(unittest.TestCase): def setUp(self): - patcher = patch('HLM_PV_Import.ca_wrapper.Context') + patcher = patch('hlm_pv_import.ca_wrapper.Context') self.mock_ctx = patcher.start().return_value self.addCleanup(patcher.stop) self.pvm = PvMonitors([]) @@ -73,7 +73,7 @@ def test_WHEN_start_monitors_THEN_subscribe_to_pvs(self, mock_pv): (1, 1, False) ]) def test_GIVEN_pv_name_WHEN_check_if_data_is_stale_THEN_correct_check(self, last_update, current_time, expected): - with patch('time.time') as mock_time, patch('HLM_PV_Import.ca_wrapper.pv_logger'): + with patch('time.time') as mock_time, patch('hlm_pv_import.ca_wrapper.pv_logger'): # Arrange ca_wrapper.STALE_AGE = 1 # set 1 second old as stale data diff --git a/tests/test_manager_db_func.py b/tests/test_manager_db_func.py index b616fdd..2550508 100644 --- a/tests/test_manager_db_func.py +++ b/tests/test_manager_db_func.py @@ -2,7 +2,7 @@ import mock from tests import mock_database -from ServiceManager import db_func +from service_manager import db_func VESSEL = 2 CRYOSTAT = 4 @@ -16,18 +16,18 @@ class TestManagerDBFunc(unittest.TestCase): def test_db_connect_WHEN_database_not_mocked_THEN_raise_exception(self): self.assertRaises(db_func.DBConnectionError, db_func.db_connect) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) def test_db_connect_WHEN_mock_database_THEN_raise_exception(self): db_func.db_connect() mock_database.database.close() - @mock.patch("ServiceManager.db_func.database.is_connection_usable") + @mock.patch("service_manager.db_func.database.is_connection_usable") def test_db_connected_WHEN_db_connected_THEN_returns_true(self, mock_func): mock_func.return_value = True self.assertTrue(db_func.db_connected()) mock_func.assert_called() - @mock.patch("ServiceManager.db_func.database.is_connection_usable") + @mock.patch("service_manager.db_func.database.is_connection_usable") def test_db_connected_WHEN_db_not_connected_THEN_returns_false(self, mock_func): mock_func.return_value = False self.assertFalse(db_func.db_connected()) @@ -36,14 +36,14 @@ def test_db_connected_WHEN_db_not_connected_THEN_returns_false(self, mock_func): def test_get_object_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object(0)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test", ob_objecttype=1) self.assertIsNone(db_func.get_object(0)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -53,21 +53,21 @@ def test_get_object_WHEN_present_THEN_returns_it(self): def test_get_object_id_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object_id("test")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_id_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertIsNone(db_func.get_object_id("test")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_id_WHEN_present_THEN_returns_it(self): with mock_database.Database(): obj = mock_database.GamObject.create(ob_name="test", ob_objecttype=1) self.assertEqual(db_func.get_object_id("test"), obj.ob_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_id_WHEN_multiple_present_THEN_returns_lowest_id(self): with mock_database.Database(): @@ -78,21 +78,21 @@ def test_get_object_id_WHEN_multiple_present_THEN_returns_lowest_id(self): def test_get_max_object_id_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_max_object_id()) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_max_object_id_WHEN_not_present_THEN_returns_zero(self): with mock_database.Database(): self.assertTrue(db_func.db_connected()) self.assertEqual(db_func.get_max_object_id(), 0) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_max_object_id_WHEN_present_THEN_returns_it(self): with mock_database.Database(): obj = mock_database.GamObject.create(ob_name="test", ob_objecttype=1) self.assertEqual(db_func.get_max_object_id(), obj.ob_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_max_object_id_WHEN_multi_present_THEN_returns_highest(self): with mock_database.Database(): @@ -106,21 +106,21 @@ def test_get_max_object_id_WHEN_multi_present_THEN_returns_highest(self): def test_get_object_name_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object_name(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_name_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertIsNone(db_func.get_object_name(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_name_WHEN_present_THEN_returns_it(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertEqual(db_func.get_object_name(1), "test1") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_name_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -131,14 +131,14 @@ def test_get_object_name_WHEN_multi_present_THEN_returns_correct(self): def test_get_object_type_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object_type(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_type_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertIsNone(db_func.get_object_type(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_type_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -146,7 +146,7 @@ def test_get_object_type_WHEN_present_THEN_returns_it(self): mock_database.GamObjecttype.create(ot_name="test2", ot_objectclass=0) self.assertEqual(db_func.get_object_type(1), "test2") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_type_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -158,14 +158,14 @@ def test_get_object_type_WHEN_multi_present_THEN_returns_correct(self): def test_get_object_class_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object_class(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_class_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertIsNone(db_func.get_object_class(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_class_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -174,7 +174,7 @@ def test_get_object_class_WHEN_present_THEN_returns_it(self): mock_database.GamObjectclass.create(oc_name="test3", oc_function=0, oc_positiontype=0) self.assertEqual(db_func.get_object_class(1), "test3") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_class_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -187,14 +187,14 @@ def test_get_object_class_WHEN_multi_present_THEN_returns_correct(self): def test_get_object_function_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object_function(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_function_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertIsNone(db_func.get_object_function(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_function_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -204,7 +204,7 @@ def test_get_object_function_WHEN_present_THEN_returns_it(self): mock_database.GamFunction.create(of_name="test4") self.assertEqual(db_func.get_object_function(1), "test4") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_function_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -215,7 +215,7 @@ def test_get_object_function_WHEN_multi_present_THEN_returns_correct(self): mock_database.GamFunction.create(of_name="test5") self.assertEqual(db_func.get_object_function(1), "test5") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_function_WHEN_present_but_unnamed_THEN_returns_none(self): with mock_database.Database(): @@ -228,14 +228,14 @@ def test_get_object_function_WHEN_present_but_unnamed_THEN_returns_none(self): def test_get_object_display_group_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_object_display_group(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_display_group_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertIsNone(db_func.get_object_display_group(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_display_group_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -243,7 +243,7 @@ def test_get_object_display_group_WHEN_present_THEN_returns_it(self): mock_database.GamDisplaygroup.create(dg_name="test2") self.assertEqual(db_func.get_object_display_group(1), "test2") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_display_group_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -254,7 +254,7 @@ def test_get_object_display_group_WHEN_multi_present_THEN_returns_correct(self): self.assertEqual(db_func.get_object_display_group(1), "test4") self.assertEqual(db_func.get_object_display_group(2), "test3") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_object_display_group_WHEN_present_but_unnamed_THEN_returns_none(self): with mock_database.Database(): @@ -265,14 +265,14 @@ def test_get_object_display_group_WHEN_present_but_unnamed_THEN_returns_none(sel def test_get_class_id_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_class_id(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_class_id_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObjectclass.create(oc_name="test3", oc_function=0, oc_positiontype=0) self.assertIsNone(db_func.get_class_id(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_class_id_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -280,7 +280,7 @@ def test_get_class_id_WHEN_present_THEN_returns_it(self): test_class = mock_database.GamObjectclass.create(oc_name="test3", oc_function=0, oc_positiontype=0) self.assertEqual(db_func.get_class_id(1), test_class.oc_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_class_id_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -291,21 +291,21 @@ def test_get_class_id_WHEN_multi_present_THEN_returns_correct(self): def test_get_measurement_types_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_measurement_types(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_measurement_types_WHEN_class_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObjectclass.create(oc_name="test1", oc_function=0, oc_positiontype=0) self.assertIsNone(db_func.get_measurement_types(2)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_measurement_types_WHEN_no_types_THEN_returns_list_of_none(self): with mock_database.Database(): mock_database.GamObjectclass.create(oc_name="test1", oc_function=0, oc_positiontype=0) self.assertListEqual(db_func.get_measurement_types(1), [None, None, None, None, None]) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_measurement_types_WHEN_present_THEN_returns_it(self): with mock_database.Database(): @@ -315,7 +315,7 @@ def test_get_measurement_types_WHEN_present_THEN_returns_it(self): oc_measuretype5="test6") self.assertListEqual(db_func.get_measurement_types(1), ['test2', 'test3', 'test4', 'test5', 'test6']) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_measurement_types_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -330,20 +330,20 @@ def test_get_measurement_types_WHEN_multi_present_THEN_returns_correct(self): def test_get_all_object_names_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_all_object_names()) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_object_names_WHEN_not_present_THEN_returns_empty(self): with mock_database.Database(): self.assertListEqual(db_func.get_all_object_names(), []) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_object_names_WHEN_present_THEN_returns_it(self): with mock_database.Database(): mock_database.GamObject.create(ob_name="test1", ob_objecttype=1) self.assertListEqual(db_func.get_all_object_names(), ["test1"]) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_object_names_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -354,21 +354,21 @@ def test_get_all_object_names_WHEN_multi_present_THEN_returns_correct(self): def test_get_type_id_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_type_id("test1")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_type_id_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamObjecttype.create(ot_name="test2", ot_objectclass=0) self.assertIsNone(db_func.get_type_id("test1")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_type_id_WHEN_present_THEN_returns_it(self): with mock_database.Database(): obj = mock_database.GamObjecttype.create(ot_name="test1", ot_objectclass=0) self.assertEqual(db_func.get_type_id("test1"), obj.ot_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_type_id_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -376,7 +376,7 @@ def test_get_type_id_WHEN_multi_present_THEN_returns_correct(self): obj = mock_database.GamObjecttype.create(ot_name="test2", ot_objectclass=0) self.assertEqual(db_func.get_type_id("test2"), obj.ot_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_type_id_WHEN_multi_same_THEN_returns_first(self): with mock_database.Database(): @@ -387,20 +387,20 @@ def test_get_type_id_WHEN_multi_same_THEN_returns_first(self): def test_get_all_type_names_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_all_type_names()) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_type_names_WHEN_not_present_THEN_returns_empty(self): with mock_database.Database(): self.assertListEqual(db_func.get_all_type_names(), []) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_type_names_WHEN_present_THEN_returns_it(self): with mock_database.Database(): mock_database.GamObjecttype.create(ot_name="test1", ot_objectclass=0) self.assertListEqual(db_func.get_all_type_names(), ["test1"]) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_type_names_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -411,21 +411,21 @@ def test_get_all_type_names_WHEN_multi_present_THEN_returns_correct(self): def test_display_group_id_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_display_group_id("test1")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_display_group_id_WHEN_not_present_THEN_returns_none(self): with mock_database.Database(): mock_database.GamDisplaygroup.create(dg_name="test2") self.assertIsNone(db_func.get_display_group_id("test1")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_display_group_id_WHEN_present_THEN_returns_it(self): with mock_database.Database(): obj = mock_database.GamDisplaygroup.create(dg_name="test1") self.assertEqual(db_func.get_display_group_id("test1"), obj.dg_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_display_group_id_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -433,7 +433,7 @@ def test_get_display_group_id_WHEN_multi_present_THEN_returns_correct(self): obj = mock_database.GamDisplaygroup.create(dg_name="test2") self.assertEqual(db_func.get_display_group_id("test2"), obj.dg_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_display_group_id_WHEN_multi_same_THEN_returns_first(self): with mock_database.Database(): @@ -444,20 +444,20 @@ def test_get_display_group_id_WHEN_multi_same_THEN_returns_first(self): def test_get_all_display_names_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.get_all_display_names()) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_display_names_WHEN_not_present_THEN_returns_empty(self): with mock_database.Database(): self.assertListEqual(db_func.get_all_display_names(), []) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_display_names_WHEN_present_THEN_returns_it(self): with mock_database.Database(): mock_database.GamDisplaygroup.create(dg_name="test1") self.assertListEqual(db_func.get_all_display_names(), ["test1"]) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_get_all_display_names_WHEN_multi_present_THEN_returns_correct(self): with mock_database.Database(): @@ -468,7 +468,7 @@ def test_get_all_display_names_WHEN_multi_present_THEN_returns_correct(self): def test_add_object_WHEN_no_connection_THEN_returns_none(self): self.assertIsNone(db_func.add_object("test1", 0)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_object_WHEN_connection_THEN_returns_id(self): with mock_database.Database(): @@ -477,7 +477,7 @@ def test_add_object_WHEN_connection_THEN_returns_id(self): self.assertIsNotNone(db_func.get_object(ob_id)) self.assertIsNotNone(db_func.get_object_id("test1")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_object_WHEN_connection_AND_already_exists_THEN_exception(self): with mock_database.Database(): @@ -485,7 +485,7 @@ def test_add_object_WHEN_connection_AND_already_exists_THEN_exception(self): with self.assertRaises(db_func.DBObjectNameAlreadyExists): db_func.add_object("test1", 0) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_object_WHEN_connection_AND_add_multi_THEN_returns_id(self): with mock_database.Database(): @@ -498,7 +498,7 @@ def test_add_object_WHEN_connection_AND_add_multi_THEN_returns_id(self): self.assertIsNotNone(db_func.get_object(ob_id)) self.assertIsNotNone(db_func.get_object_id("test2")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_object_WHEN_connection_AND_valid_type_THEN_object_added_AND_has_type(self): with mock_database.Database(): @@ -509,7 +509,7 @@ def test_add_object_WHEN_connection_AND_valid_type_THEN_object_added_AND_has_typ self.assertIsNotNone(db_func.get_object_id("test1")) self.assertEqual(db_func.get_object_type(ob_id), "test2") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_object_WHEN_connection_AND_valid_display_group_THEN_object_added_AND_has_display_group(self): with mock_database.Database(): @@ -520,7 +520,7 @@ def test_add_object_WHEN_connection_AND_valid_display_group_THEN_object_added_AN self.assertIsNotNone(db_func.get_object_id("test1")) self.assertEqual(db_func.get_object_display_group(ob_id), "test2") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_object_WHEN_connection_AND_comment_THEN_has_comment(self): with mock_database.Database(): @@ -530,7 +530,7 @@ def test_add_object_WHEN_connection_AND_comment_THEN_has_comment(self): self.assertIsNotNone(db_func.get_object_id("test1")) self.assertEqual(db_func.get_object(1).ob_comment, "test_comment") - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_relation_WHEN_connection_AND_valid_objects_THEN_adds_relation(self): with mock_database.Database(): @@ -539,14 +539,14 @@ def test_add_relation_WHEN_connection_AND_valid_objects_THEN_adds_relation(self) db_func.add_relation(1, 2) self.assertIsNotNone(mock_database.GamObjectrelation.get_or_none(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_add_relation_WHEN_connectionTHEN_adds_relation(self): with mock_database.Database(): db_func.add_relation(1, 2) self.assertIsNotNone(mock_database.GamObjectrelation.get_or_none(1)) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_create_module_if_required_WHEN_connection_AND_module_not_required_THEN_no_module(self): with mock_database.Database(): @@ -554,7 +554,7 @@ def test_create_module_if_required_WHEN_connection_AND_module_not_required_THEN_ self.assertIsNone(mock_database.GamObjectrelation.get_or_none(1)) self.assertIsNone(db_func.get_object_id("SLD \"test1\" (ID: 1)")) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_create_module_if_required_WHEN_connection_AND_sld_required_THEN_sld(self): with mock_database.Database(): @@ -566,7 +566,7 @@ def test_create_module_if_required_WHEN_connection_AND_sld_required_THEN_sld(sel self.assertEqual("Software Level Device for test2 \"test1\" (ID: 1)", obj.ob_comment) self.assertEqual(SLD, obj.ob_objecttype.ot_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_create_module_if_required_WHEN_connection_AND_sld_required_for_cryostat_THEN_sld(self): with mock_database.Database(): @@ -578,7 +578,7 @@ def test_create_module_if_required_WHEN_connection_AND_sld_required_for_cryostat self.assertEqual("Software Level Device for test2 \"test1\" (ID: 1)", obj.ob_comment) self.assertEqual(SLD, obj.ob_objecttype.ot_id) - @mock.patch("ServiceManager.db_func.database", new=mock_database.database) + @mock.patch("service_manager.db_func.database", new=mock_database.database) @mock.patch("shared.utils.database", new=mock_database.database) def test_create_module_if_required_WHEN_connection_AND_gcm_required_THEN_gcm(self): with mock_database.Database(): diff --git a/tests/test_service_db_func.py b/tests/test_service_db_func.py index 800c82f..392557e 100644 --- a/tests/test_service_db_func.py +++ b/tests/test_service_db_func.py @@ -2,7 +2,7 @@ import mock from tests import mock_database -from HLM_PV_Import import db_func +from hlm_pv_import import db_func RECONNECT_MAX_WAIT_TIME = 14400 @@ -22,29 +22,29 @@ def test_increase_reconnect_wait_time_WHEN_double_less_than_max_THEN_return_doub def test_increase_reconnect_wait_time_WHEN_double_not_less_than_max_THEN_return_max(self): self.assertEqual(db_func.increase_reconnect_wait_time(10000), RECONNECT_MAX_WAIT_TIME) - @mock.patch("HLM_PV_Import.db_func.logger.info") - @mock.patch("HLM_PV_Import.db_func.database.connect") + @mock.patch("hlm_pv_import.db_func.logger.info") + @mock.patch("hlm_pv_import.db_func.database.connect") def test_db_connect_WHEN_valid_db_THEN_connects(self, mock_connection, mock_logger): mock_connection.return_value = True db_func.db_connect() mock_logger.assert_called_with('Database connection successful.') - @mock.patch("HLM_PV_Import.db_func.logger.error") - @mock.patch("HLM_PV_Import.db_func.database.connect") + @mock.patch("hlm_pv_import.db_func.logger.error") + @mock.patch("hlm_pv_import.db_func.database.connect") def test_db_connect_WHEN_no_db_THEN_fails(self, mock_connection, mock_logger): error = Exception("Failed") mock_connection.side_effect = error db_func.db_connect() mock_logger.assert_called_with(error) - @mock.patch("HLM_PV_Import.db_func.RECONNECT_ATTEMPTS_MAX", 100) - @mock.patch("HLM_PV_Import.db_func.check_db_connection", side_effect=db_func.check_db_connection) + @mock.patch("hlm_pv_import.db_func.RECONNECT_ATTEMPTS_MAX", 100) + @mock.patch("hlm_pv_import.db_func.check_db_connection", side_effect=db_func.check_db_connection) def test_check_db_connection_WHEN_no_db_THEN_exception_AND_called_max_attempt_times_before_exception(self, mock_function): self.assertRaises(Exception, mock_function, 1, 0) self.assertEqual(101, mock_function.call_count) - @mock.patch("HLM_PV_Import.db_func.check_db_connection", side_effect=db_func.check_db_connection) - @mock.patch("HLM_PV_Import.db_func.database.is_connection_usable", return_value=True) + @mock.patch("hlm_pv_import.db_func.check_db_connection", side_effect=db_func.check_db_connection) + @mock.patch("hlm_pv_import.db_func.database.is_connection_usable", return_value=True) def test_check_db_connection_WHEN_db_THEN_return_true_and_called_once(self, mock_function, mock_connection): self.assertTrue(mock_function(1, 1)) self.assertEqual(1, mock_function.call_count) @@ -60,8 +60,8 @@ def test_function(): self.assertTrue(test_function()) mock_function.assert_called() - @mock.patch("HLM_PV_Import.db_func.RECONNECT_ATTEMPTS_MAX", 1) - @mock.patch("HLM_PV_Import.db_func.check_db_connection", side_effect=db_func.check_db_connection) + @mock.patch("hlm_pv_import.db_func.RECONNECT_ATTEMPTS_MAX", 1) + @mock.patch("hlm_pv_import.db_func.check_db_connection", side_effect=db_func.check_db_connection) def test_GIVEN_database_connected_THEN_need_connection_fails(self, mock_function): @db_func.check_connection @@ -70,12 +70,12 @@ def test_function(): self.assertRaises(Exception, test_function) - @mock.patch("HLM_PV_Import.db_func.database", new=mock_database.database) + @mock.patch("hlm_pv_import.db_func.database", new=mock_database.database) def test_get_object_GIVEN_no_object_THEN_returns_none(self): with mock_database.Database(): self.assertIsNone(db_func.get_object(1)) - @mock.patch("HLM_PV_Import.db_func.database", new=mock_database.database) + @mock.patch("hlm_pv_import.db_func.database", new=mock_database.database) def test_get_object_GIVEN_object_THEN_returns_it(self): with mock_database.Database(): diff --git a/tests/test_user_config.py b/tests/test_user_config.py index a3c10b8..53ae10d 100644 --- a/tests/test_user_config.py +++ b/tests/test_user_config.py @@ -1,14 +1,14 @@ from parameterized import parameterized import unittest from mock import patch -from HLM_PV_Import.user_config import * -from HLM_PV_Import.settings import PVConfig +from hlm_pv_import.user_config import * +from hlm_pv_import.settings import PVConfig class TestUserConfig(unittest.TestCase): def setUp(self): - patch('HLM_PV_Import.user_config.logger').start() + patch('hlm_pv_import.user_config.logger').start() patcher = patch.object(UserConfig, "__init__", lambda x: None) patcher.start() self.addCleanup(patcher.stop) @@ -56,21 +56,21 @@ def test_GIVEN_no_pvs_WHEN_check_if_entries_have_measurement_pvs_THEN_exception_ with self.assertRaises(PVConfigurationException): self.config._check_entries_have_measurement_pvs() - @patch('HLM_PV_Import.user_config.get_object') + @patch('hlm_pv_import.user_config.get_object') def test_GIVEN_objects_exist_WHEN_check_if_objects_exist_THEN_no_exception(self, mock_obj_res): self.config.object_ids = ['a', 'b', 'c'] mock_obj_res.return_value = 1 self.config._check_objects_exist() - @patch('HLM_PV_Import.user_config.get_object') + @patch('hlm_pv_import.user_config.get_object') def test_GIVEN_objects_not_found_WHEN_check_if_objects_exist_THEN_exception_raised(self, mock_obj_res): self.config.object_ids = ['a', 'b', 'c'] mock_obj_res.return_value = None with self.assertRaises(PVConfigurationException): self.config._check_objects_exist() - @patch('HLM_PV_Import.user_config.get_connected_pvs') - @patch('HLM_PV_Import.user_config.UserConfig.get_measurement_pvs') + @patch('hlm_pv_import.user_config.get_connected_pvs') + @patch('hlm_pv_import.user_config.UserConfig.get_measurement_pvs') def test_GIVEN_existing_pvs_WHEN_check_if_measurement_pvs_connect_THEN_no_exception(self, mock_meas_pvs, mock_connected_pvs): mock_meas_pvs.return_value = ['a', 'b', 'c']