review: добавил TODO
This commit is contained in:
parent
64ced613dd
commit
a6a7653ca5
@ -20,8 +20,11 @@ class Controller(BaseController):
|
|||||||
self.signal_widgets.emit(widgets)
|
self.signal_widgets.emit(widgets)
|
||||||
|
|
||||||
def update_settings(self, settings: list[dict]) -> None:
|
def update_settings(self, settings: list[dict]) -> None:
|
||||||
|
# TODO: Почему контроллеру просто не сообщить о медиаторе, и ему сообщать что-то через notify? Тем более, что
|
||||||
|
# медиатор уже знает про контроллер...
|
||||||
self.signal_settings.emit(settings)
|
self.signal_settings.emit(settings)
|
||||||
|
|
||||||
|
# TODO: Объедини переключение режимов в один метод, и по входному аргументу (например 1 и 2) переключай их.
|
||||||
def raport_mode(self) -> None:
|
def raport_mode(self) -> None:
|
||||||
self.signal_raport_mode.emit()
|
self.signal_raport_mode.emit()
|
||||||
|
|
||||||
@ -29,12 +32,18 @@ class Controller(BaseController):
|
|||||||
self.signal_seeking_mode.emit()
|
self.signal_seeking_mode.emit()
|
||||||
|
|
||||||
def open_file(self, filepath: str) -> None:
|
def open_file(self, filepath: str) -> None:
|
||||||
|
# TODO: Где тут открытие файла?
|
||||||
self.signal_open_file.emit(filepath)
|
self.signal_open_file.emit(filepath)
|
||||||
|
|
||||||
def update_plots(self) -> None:
|
def update_plots(self) -> None:
|
||||||
self.signal_update_plots.emit()
|
self.signal_update_plots.emit()
|
||||||
|
|
||||||
def update_status(self, msg: Union[str, float, int]) -> None:
|
def update_status(self, msg: Union[str, float, int]) -> None:
|
||||||
|
# TODO: Довольно странный набор возможных типов входных аргументов.
|
||||||
|
# TODO: Для чего сюда принимается float, если он все равно приводится к int?
|
||||||
|
# TODO: Из медиатора сюда приходит статус 0.5, который становится нулем.
|
||||||
|
# Это точно ожидаемое и корректное поведение? Сомнительно...
|
||||||
|
# TODO: if isinstance(msg, float): ...
|
||||||
if type(msg) == float or type(msg) == int:
|
if type(msg) == float or type(msg) == int:
|
||||||
self.signal_statusBar.emit(int(msg))
|
self.signal_statusBar.emit(int(msg))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -18,6 +18,8 @@ class DataConverter(BaseDataConverter):
|
|||||||
dataframe[bool_columns] = dataframe[bool_columns].replace({True: 1, False: 0})
|
dataframe[bool_columns] = dataframe[bool_columns].replace({True: 1, False: 0})
|
||||||
return dataframe
|
return dataframe
|
||||||
except:
|
except:
|
||||||
|
# TODO: Обработка исключений!
|
||||||
|
# TODO: Осмысленное сообщение в лог. Без traceback и прочего подобного.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def convert_data(self, files: list[str]) -> None:
|
def convert_data(self, files: list[str]) -> None:
|
||||||
@ -25,10 +27,12 @@ class DataConverter(BaseDataConverter):
|
|||||||
dataframes = [pd.read_csv(file) if file != '' else None for file in files]
|
dataframes = [pd.read_csv(file) if file != '' else None for file in files]
|
||||||
converted_dataframes = list(map(self._replace_bool, dataframes))
|
converted_dataframes = list(map(self._replace_bool, dataframes))
|
||||||
except:
|
except:
|
||||||
|
# TODO: Обработка исключений!
|
||||||
# Get the traceback object
|
# Get the traceback object
|
||||||
tb = sys.exc_info()[2]
|
tb = sys.exc_info()[2]
|
||||||
tbinfo = traceback.format_tb(tb)[0]
|
tbinfo = traceback.format_tb(tb)[0]
|
||||||
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
|
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
|
||||||
|
# TODO: Осмысленное сообщение в лог. Без traceback и прочего подобного.
|
||||||
logger.error(pymsg)
|
logger.error(pymsg)
|
||||||
converted_dataframes = [None]
|
converted_dataframes = [None]
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class Mediator(BaseMediator):
|
|||||||
data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
|
data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
|
||||||
|
|
||||||
if issubclass(source.__class__, BaseDirectoryMonitor):
|
if issubclass(source.__class__, BaseDirectoryMonitor):
|
||||||
|
# TODO: self._controller.update_status("CSV found! Calculating...") А вот так нельзя написать?
|
||||||
self.update_status("CSV found! Calculating...")
|
self.update_status("CSV found! Calculating...")
|
||||||
self._converter.convert_data(data)
|
self._converter.convert_data(data)
|
||||||
|
|
||||||
@ -30,12 +31,17 @@ class Mediator(BaseMediator):
|
|||||||
if issubclass(source.__class__, BasePlotWidget):
|
if issubclass(source.__class__, BasePlotWidget):
|
||||||
self.update_status(100)
|
self.update_status(100)
|
||||||
self._controller.send_widgets(data)
|
self._controller.send_widgets(data)
|
||||||
|
|
||||||
def update_settings(self, settings: list[dict]):
|
def update_settings(self, settings: list[dict]):
|
||||||
|
# TODO: А дописать self.notify не?
|
||||||
|
# if issubclass(source.__class__, BaseController):
|
||||||
|
# self._monitor.update_settings(data)
|
||||||
|
# self._passportFormer.update_settings(data)
|
||||||
self._monitor.update_settings(settings)
|
self._monitor.update_settings(settings)
|
||||||
self._passportFormer.update_settings(settings)
|
self._passportFormer.update_settings(settings)
|
||||||
|
|
||||||
def update_status(self, msg: Union[str, float]) -> None:
|
def update_status(self, msg: Union[str, float]) -> None:
|
||||||
|
# TODO: Бессмысленный метод.
|
||||||
self._controller.update_status(msg)
|
self._controller.update_status(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,11 @@ from utils.base.base import BaseDirectoryMonitor
|
|||||||
class DirectoryMonitor(BaseDirectoryMonitor):
|
class DirectoryMonitor(BaseDirectoryMonitor):
|
||||||
|
|
||||||
def _init_state(self):
|
def _init_state(self):
|
||||||
|
|
||||||
self._files = [
|
self._files = [
|
||||||
os.path.join(self._directory_path, file)
|
os.path.join(self._directory_path, file)
|
||||||
for file in os.listdir(self._directory_path)
|
for file in os.listdir(self._directory_path)
|
||||||
|
# TODO: FileNotFoundError: [Errno 2] No such file or directory: 'D:/downloads/a22'
|
||||||
|
# при выборе real time mode.
|
||||||
if file.lower().endswith('.csv')
|
if file.lower().endswith('.csv')
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -31,6 +32,9 @@ class DirectoryMonitor(BaseDirectoryMonitor):
|
|||||||
if not current_files:
|
if not current_files:
|
||||||
self._files = []
|
self._files = []
|
||||||
|
|
||||||
|
# TODO: Почему монитор директории обновляет какие-то настройки, графики, стартует что-то?
|
||||||
|
# Это не должно быть в его зоне ответственности.
|
||||||
|
# TODO: Может стоит сделать какой-то класс, который будет заниматься обновлениями?
|
||||||
def update_settings(self, data: list[dict]) -> None:
|
def update_settings(self, data: list[dict]) -> None:
|
||||||
if self.isActive:
|
if self.isActive:
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|||||||
@ -7,6 +7,7 @@ from utils.base.base import BasePointPassportFormer, BaseIdealDataBuilder
|
|||||||
|
|
||||||
|
|
||||||
class idealDataBuilder(BaseIdealDataBuilder):
|
class idealDataBuilder(BaseIdealDataBuilder):
|
||||||
|
# TODO: Имя класса с большой буквы.
|
||||||
def get_closingDF(self) -> pd.DataFrame:
|
def get_closingDF(self) -> pd.DataFrame:
|
||||||
return self._get_data(self.Ts['tclose'], self.calcPhaseClose)
|
return self._get_data(self.Ts['tclose'], self.calcPhaseClose)
|
||||||
|
|
||||||
@ -35,10 +36,13 @@ class idealDataBuilder(BaseIdealDataBuilder):
|
|||||||
class PassportFormer(BasePointPassportFormer):
|
class PassportFormer(BasePointPassportFormer):
|
||||||
|
|
||||||
def form_passports(self, data: list[pd.DataFrame]) -> list[list[pd.DataFrame, dict, int]]:
|
def form_passports(self, data: list[pd.DataFrame]) -> list[list[pd.DataFrame, dict, int]]:
|
||||||
|
# TODO: сигнатура метода не соответствует сигнатуре метода класса родителя
|
||||||
try:
|
try:
|
||||||
return_data = [self._build_passports_pocket(dataframe) for dataframe in data]
|
return_data = [self._build_passports_pocket(dataframe) for dataframe in data]
|
||||||
except:
|
except:
|
||||||
|
# TODO: обработка исключений!!!
|
||||||
tb = sys.exc_info()[2]
|
tb = sys.exc_info()[2]
|
||||||
|
# TODO: Нормальные сообщения в лог!
|
||||||
tbinfo = traceback.format_tb(tb)[0]
|
tbinfo = traceback.format_tb(tb)[0]
|
||||||
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
|
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
|
||||||
logger.error(pymsg)
|
logger.error(pymsg)
|
||||||
@ -48,7 +52,8 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
|
|
||||||
|
|
||||||
def _build_passports_pocket(self, dataframe: pd.DataFrame) -> list[pd.DataFrame, dict, int]:
|
def _build_passports_pocket(self, dataframe: pd.DataFrame) -> list[pd.DataFrame, dict, int]:
|
||||||
|
# TODO: Еще раз проверь, что написано в аннотации, и что метод возвращает.
|
||||||
|
# TODO: Практически нечитаемо. Надо оптимизировать и декомпозировать.
|
||||||
if dataframe is not None:
|
if dataframe is not None:
|
||||||
events, point_quantity = self._filter_events(dataframe["time"], dataframe)
|
events, point_quantity = self._filter_events(dataframe["time"], dataframe)
|
||||||
if point_quantity == 0:
|
if point_quantity == 0:
|
||||||
@ -74,16 +79,21 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
}
|
}
|
||||||
params_list = [operator_settings, system_settings]
|
params_list = [operator_settings, system_settings]
|
||||||
cache_key = self._generate_cache_key(params_list)
|
cache_key = self._generate_cache_key(params_list)
|
||||||
|
# TODO: if-else заменить на:
|
||||||
|
# ideal_data = self._ideal_data_cashe.get(cache_key, self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list))
|
||||||
|
# self._ideal_data_cashe[cache_key] = ideal_data
|
||||||
if cache_key in self._ideal_data_cashe :
|
if cache_key in self._ideal_data_cashe :
|
||||||
ideal_data = self._ideal_data_cashe[cache_key]
|
ideal_data = self._ideal_data_cashe[cache_key]
|
||||||
else:
|
else:
|
||||||
ideal_data = self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list)
|
ideal_data = self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list)
|
||||||
self._ideal_data_cashe[cache_key] = ideal_data
|
self._ideal_data_cashe[cache_key] = ideal_data
|
||||||
|
# TODO: point_timeframe, point_events = None, None
|
||||||
if events is not None:
|
if events is not None:
|
||||||
idx = i+1 if idx_shift else i
|
idx = i+1 if idx_shift else i
|
||||||
point_timeframe = [events[self._stages[0]][0][i], events[self._stages[-1]][1][idx]]
|
point_timeframe = [events[self._stages[0]][0][i], events[self._stages[-1]][1][idx]]
|
||||||
point_events = {key: [value[0][i], value[1][i]] for key, value in events.items()}
|
point_events = {key: [value[0][i], value[1][i]] for key, value in events.items()}
|
||||||
else:
|
else:
|
||||||
|
# TODO: убрать else
|
||||||
point_timeframe, point_events = None, None
|
point_timeframe, point_events = None, None
|
||||||
useful_p_data = {"thickness": operator_settings["object_thickness"],
|
useful_p_data = {"thickness": operator_settings["object_thickness"],
|
||||||
"L2": operator_settings["distance_l_2"],
|
"L2": operator_settings["distance_l_2"],
|
||||||
|
|||||||
@ -14,8 +14,18 @@ from gui.reportGui import ReportSettings
|
|||||||
class MainWindow(BaseMainWindow):
|
class MainWindow(BaseMainWindow):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
controller: Optional[BaseController] = None) -> None:
|
controller: Optional[BaseController] = None) -> None:
|
||||||
|
# TODO: Почему контроллер Optional? Нигде нет обработки случаев, если он будет None: при вызове его методов
|
||||||
|
# будет ошибка.
|
||||||
|
|
||||||
|
# TODO: Касательно передачи экземпляра контроллера в конструктор. Не лучше ли реализовать взаимодействие с ним
|
||||||
|
# через сигналы? Как минимум это позволит исключить странную зависимость интерфейса от контроллера.
|
||||||
|
# Логичнее было бы использовать данный класс в качестве фасада над виджетами, о чем сказано ниже.
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._controller = controller
|
self._controller = controller
|
||||||
|
# TODO: касательно ВСЕХ методов данного класса, СОЗДАЮЩИХ ВИДЖЕТЫ. Исходя из названия данного класса - это
|
||||||
|
# финальное главное окно приложения: оно не должно создавать виджеты, а уже использовать готовые, поэтому
|
||||||
|
# правильнее будет каждый создаваемый тут виджет вынести в отдельный модуль в отдельный класс, там выполнять
|
||||||
|
# его создание и настройку, а тут уже получать готовый экземпляр виджета и его отображать.
|
||||||
self._init_startUI()
|
self._init_startUI()
|
||||||
self._init_dock_widgets()
|
self._init_dock_widgets()
|
||||||
self._init_menu()
|
self._init_menu()
|
||||||
@ -194,6 +204,11 @@ class MainWindow(BaseMainWindow):
|
|||||||
if child.widget() is not None:
|
if child.widget() is not None:
|
||||||
child.widget().deleteLater()
|
child.widget().deleteLater()
|
||||||
|
|
||||||
|
# TODO: По методам self._init_seekingUI и self._init_raportUI:
|
||||||
|
# 1. Дублирование кода (блок из первых трех строк) - вынести в отдельный метод
|
||||||
|
# 2. Оба метода призваны проинициализировать UI, но в то же время занимаются созданием виджетов и еще что-то
|
||||||
|
# в контроллере дергают: создание виджетов вынести отдельно, контроллеру сообщить о чем-то при помощи
|
||||||
|
# pyqtSignal при нажатии кнопки или выборе пункта меню.
|
||||||
def _init_seekingUI(self) -> None:
|
def _init_seekingUI(self) -> None:
|
||||||
self._clear()
|
self._clear()
|
||||||
self._init_TabWidget()
|
self._init_TabWidget()
|
||||||
@ -292,6 +307,7 @@ class MainWindow(BaseMainWindow):
|
|||||||
def _close_tab(self, index:int) -> None:
|
def _close_tab(self, index:int) -> None:
|
||||||
self.tabWidget.removeTab(index)
|
self.tabWidget.removeTab(index)
|
||||||
|
|
||||||
|
# TODO: Зачем разделять эти 2 метода? В self._select_csv путь к файлу получили и контроллеру сигналом отправили.
|
||||||
def _open_file(self) -> None:
|
def _open_file(self) -> None:
|
||||||
path = self._select_csv()
|
path = self._select_csv()
|
||||||
if path is not None:
|
if path is not None:
|
||||||
@ -307,7 +323,8 @@ class MainWindow(BaseMainWindow):
|
|||||||
def _on_tab_changed(self, index):
|
def _on_tab_changed(self, index):
|
||||||
try:
|
try:
|
||||||
tab = self.tabWidget.currentWidget()
|
tab = self.tabWidget.currentWidget()
|
||||||
except:
|
except:
|
||||||
|
# TODO: Очень плохая практика - не указывать конкретный тип исключений.
|
||||||
tab = None
|
tab = None
|
||||||
if tab is not None and self.report_dock.isVisible():
|
if tab is not None and self.report_dock.isVisible():
|
||||||
reg_items = tab.property("reg_items")
|
reg_items = tab.property("reg_items")
|
||||||
@ -316,6 +333,9 @@ class MainWindow(BaseMainWindow):
|
|||||||
self.repSettings.build(index, reg_items, curve_items, qt_items)
|
self.repSettings.build(index, reg_items, curve_items, qt_items)
|
||||||
|
|
||||||
def _save_plots(self) -> None:
|
def _save_plots(self) -> None:
|
||||||
|
# TODO: Нарушение принципа единичной ответственности. Почему ИНТЕРФЕЙС занимается сохранением чего-то?
|
||||||
|
# От него должен только исходить призыв к действию (кнопка или что-то подобное), сама логика же должна
|
||||||
|
# быть в другом месте.
|
||||||
filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)")
|
filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)")
|
||||||
tab = self.tabWidget.currentWidget()
|
tab = self.tabWidget.currentWidget()
|
||||||
if tab is not None:
|
if tab is not None:
|
||||||
|
|||||||
@ -13,8 +13,12 @@ import pandas as pd
|
|||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
|
|
||||||
from utils.base.base import BasePlotWidget
|
from utils.base.base import BasePlotWidget
|
||||||
|
# TODO: Навести порядок в импортах.
|
||||||
|
|
||||||
class ProcessStage():
|
class ProcessStage():
|
||||||
|
# TODO: Для чего тут наследование от ничего?
|
||||||
|
# TODO: Если я правильно понял сценарий использования этого класса, то правильно это сделать либо отнаследовавшись
|
||||||
|
# от NamedTuple, либо использовать @dataclass.
|
||||||
mean_value:int
|
mean_value:int
|
||||||
start_index:int
|
start_index:int
|
||||||
finish_index:int
|
finish_index:int
|
||||||
@ -157,6 +161,7 @@ class PlotWidget(BasePlotWidget):
|
|||||||
"""
|
"""
|
||||||
Добавляет QLabel с информацией о производительности.
|
Добавляет QLabel с информацией о производительности.
|
||||||
"""
|
"""
|
||||||
|
# TODO: Почему PlotWidget создает Label? Вынести в другое место.
|
||||||
tesla_TWC = round((1 - TWC_time/tesla_time)*100, 2) if tesla_time else 0.0
|
tesla_TWC = round((1 - TWC_time/tesla_time)*100, 2) if tesla_time else 0.0
|
||||||
tesla_ideal = round((1 - ideal_time/tesla_time)*100, 2) if tesla_time else 0.0
|
tesla_ideal = round((1 - ideal_time/tesla_time)*100, 2) if tesla_time else 0.0
|
||||||
TWC_ideal = round((ideal_time/TWC_time)*100, 2) if TWC_time else 0.0
|
TWC_ideal = round((ideal_time/TWC_time)*100, 2) if TWC_time else 0.0
|
||||||
@ -187,6 +192,12 @@ class PlotWidget(BasePlotWidget):
|
|||||||
|
|
||||||
|
|
||||||
def _build_widget(self, data: list[Any]) -> QWidget:
|
def _build_widget(self, data: list[Any]) -> QWidget:
|
||||||
|
# TODO: Либо передавать в метод 3 аргумента с аннотацией типа каждого, либо передавать
|
||||||
|
# NamedTuple / dataclass.
|
||||||
|
# TODO: Исходя из сигнатуры метода, он создает и возвращает виджет с графиками.
|
||||||
|
# Простыня "result_widget, reg_items, curve_items, qt_items" не похожа на виджет с графиком.
|
||||||
|
# TODO: Данный метод должен содержать только ту логику, которая относится непосредственно к графикам.
|
||||||
|
# Все остальное - вынести. Оставшийся метод декомпозировать - очень трудно вообще понять, что тут происходит.
|
||||||
"""
|
"""
|
||||||
Собирает графический виджет для одного набора данных.
|
Собирает графический виджет для одного набора данных.
|
||||||
Параметр `data` предполагается списком: [dataframe, points_pocket, useful_data].
|
Параметр `data` предполагается списком: [dataframe, points_pocket, useful_data].
|
||||||
@ -353,19 +364,26 @@ class PlotWidget(BasePlotWidget):
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
|
# TODO: Про входные аргументы см. выше.
|
||||||
try:
|
try:
|
||||||
|
# TODO: Про инициализацию атрибутов класса где-то уже писал. На всякий случай: она делается в конструкторе.
|
||||||
self._datalen = len(data)
|
self._datalen = len(data)
|
||||||
widgets_datapack = [self._build_widget(data_sample) for self._datastep, data_sample in enumerate(data)]
|
widgets_datapack = [self._build_widget(data_sample) for self._datastep, data_sample in enumerate(data)]
|
||||||
except:
|
except:
|
||||||
|
# TODO: Добавить конкретные исключения.
|
||||||
tb = sys.exc_info()[2]
|
tb = sys.exc_info()[2]
|
||||||
tbinfo = traceback.format_tb(tb)[0]
|
tbinfo = traceback.format_tb(tb)[0]
|
||||||
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
|
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
|
||||||
|
# TODO: Логи должны быть понятны обычному пользователю. Traceback что-то там - это не информативно и может
|
||||||
|
# быть непонятно, поэтому пишем осознанные сообщения. Если надо, то создаем собственные
|
||||||
|
# классы исключений. (Касается всего проекта - уже не первый раз натыкаюсь на это.)
|
||||||
logger.error(pymsg)
|
logger.error(pymsg)
|
||||||
widgets_datapack = [QLabel(pymsg)]
|
widgets_datapack = [QLabel(pymsg)]
|
||||||
finally:
|
finally:
|
||||||
self._mediator.notify(self, widgets_datapack)
|
self._mediator.notify(self, widgets_datapack)
|
||||||
|
|
||||||
def _update_status(self, widgsteps:int, pointsteps:int, cur_widg:int, cur_point:int):
|
def _update_status(self, widgsteps:int, pointsteps:int, cur_widg:int, cur_point:int):
|
||||||
|
# TODO: if self._datalen: ...
|
||||||
if self._datalen != 0:
|
if self._datalen != 0:
|
||||||
sycle_start = self._datastep/self._datalen*100 + 1
|
sycle_start = self._datastep/self._datalen*100 + 1
|
||||||
period1 = 100/self._datalen
|
period1 = 100/self._datalen
|
||||||
@ -377,6 +395,7 @@ class PlotWidget(BasePlotWidget):
|
|||||||
period3 = period2/pointsteps if pointsteps != 0 else period2
|
period3 = period2/pointsteps if pointsteps != 0 else period2
|
||||||
|
|
||||||
progress = sycle_start + period2*cur_widg + period3*cur_point
|
progress = sycle_start + period2*cur_widg + period3*cur_point
|
||||||
|
# TODO: см. модуль mediator.py
|
||||||
self._mediator.update_status(progress)
|
self._mediator.update_status(progress)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@ class settingsWindow(QWidget):
|
|||||||
for i, (_, items) in enumerate(self._data.items()):
|
for i, (_, items) in enumerate(self._data.items()):
|
||||||
for j, item in enumerate(items):
|
for j, item in enumerate(items):
|
||||||
self._param_table.setItem(i, j, QTableWidgetItem(str(item)))
|
self._param_table.setItem(i, j, QTableWidgetItem(str(item)))
|
||||||
|
# TODO: if isinstance()...
|
||||||
if type(item) == int:
|
if type(item) == int:
|
||||||
self._param_table.setItemDelegateForRow(i, int_delegate)
|
self._param_table.setItemDelegateForRow(i, int_delegate)
|
||||||
elif type(item) == float:
|
elif type(item) == float:
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import sys
|
import sys
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
|
# TODO: Правило хорошего тона при оформлении импортов: сначала встроенные библиотеки,
|
||||||
|
# через одну пустую строку - дополнительно установленные, и еще через одну пустую строку - собственные импорты.
|
||||||
|
|
||||||
from gui.mainGui import MainWindow
|
from gui.mainGui import MainWindow
|
||||||
from controller.monitor import DirectoryMonitor
|
from controller.monitor import DirectoryMonitor
|
||||||
@ -10,6 +12,8 @@ from gui.plotter import PlotWidget
|
|||||||
from controller.controller import Controller
|
from controller.controller import Controller
|
||||||
from controller.passportFormer import PassportFormer
|
from controller.passportFormer import PassportFormer
|
||||||
|
|
||||||
|
# TODO: Актуализировать requirements.txt!!! Делать это регулярно после добавления новых библиотек!
|
||||||
|
# TODO: Именование модулей: lowercase / snake_case.
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
pg.setConfigOptions(useOpenGL=False, antialias=False)
|
pg.setConfigOptions(useOpenGL=False, antialias=False)
|
||||||
|
|||||||
@ -4,15 +4,21 @@ from typing import Optional
|
|||||||
import os
|
import os
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
# TODO: Оформление импортов
|
||||||
|
|
||||||
|
|
||||||
class BasePerformanceFactory(ABC):
|
class BasePerformanceFactory(ABC):
|
||||||
|
# TODO: Абстракция делается с целью определения интерфейса и только: внутри никаких реализаций быть не должно.
|
||||||
|
# Сначала делаешь абстракцию, затем от нее наследуешься и делаешь нечто базовое, затем конкретную реализацию.
|
||||||
|
# Есть вариант не делать базовое нечто, а сразу переходить к реализации.
|
||||||
|
|
||||||
|
# TODO: Убрать из этого класса все реализации и имплементировать соответствующие методы уже в наследниках.
|
||||||
|
# TODO: Возможно стоит отдельно вынести все касающееся парсинга в отдельный класс (на подумать).
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def factory_method(self):
|
def factory_method(self):
|
||||||
...
|
...
|
||||||
|
# TODO: @abstractmethod
|
||||||
def job(self):
|
def job(self):
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -30,17 +36,25 @@ class BasePerformanceFactory(ABC):
|
|||||||
inside_channel = False
|
inside_channel = False
|
||||||
channels = 0
|
channels = 0
|
||||||
for line in file:
|
for line in file:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
# TODO: if line in ("#BEGINCHANNELHEADER", "#BEGINGLOBALHEADER")...
|
||||||
if line == '#BEGINCHANNELHEADER' or line == "#BEGINGLOBALHEADER":
|
if line == '#BEGINCHANNELHEADER' or line == "#BEGINGLOBALHEADER":
|
||||||
inside_channel = True
|
inside_channel = True
|
||||||
|
# TODO: if line in ("#ENDCHANNELHEADER", "#ENDGLOBALHEADER")...
|
||||||
elif line == '#ENDCHANNELHEADER' or line == "#ENDGLOBALHEADER":
|
elif line == '#ENDCHANNELHEADER' or line == "#ENDGLOBALHEADER":
|
||||||
inside_channel = False
|
inside_channel = False
|
||||||
|
# TODO: для чего тут pass?
|
||||||
pass
|
pass
|
||||||
|
# TODO: inside_channel = line in ("#BEGINCHANNELHEADER", "#BEGINGLOBALHEADER")
|
||||||
# Формирование словаря
|
# Формирование словаря
|
||||||
|
# TODO: Предыдущие условия связаны были с line, а теперь под elif уже абсолютно другая логика.
|
||||||
|
# Не надо так делать.
|
||||||
elif inside_channel:
|
elif inside_channel:
|
||||||
|
# TODO: нижним подчеркиванием обозначается переменная, которая как бы есть, но не нужна.
|
||||||
|
# Ты же на что-то проверяешь, т.е. используешь. Назови нормально.
|
||||||
_, data = line.split(',')
|
_, data = line.split(',')
|
||||||
match _:
|
match _:
|
||||||
|
# TODO: тут default case не нужен?
|
||||||
case '102':
|
case '102':
|
||||||
head['rob_id'] = data
|
head['rob_id'] = data
|
||||||
case '200':
|
case '200':
|
||||||
@ -58,9 +72,11 @@ class BasePerformanceFactory(ABC):
|
|||||||
case '241':
|
case '241':
|
||||||
head[ch_name]['multiplyer'] = float(data)
|
head[ch_name]['multiplyer'] = float(data)
|
||||||
head['channels'] = int(channels)
|
head['channels'] = int(channels)
|
||||||
|
# TODO: Метод декомпозировать и переписать.
|
||||||
return head, file
|
return head, file
|
||||||
|
|
||||||
def _r64_parser(self, path: str, head: dict) -> Optional[list[pd.Series, pd.DataFrame]]:
|
def _r64_parser(self, path: str, head: dict) -> Optional[list[pd.Series, pd.DataFrame]]:
|
||||||
|
# TODO: Метод декомпозировать и переписать.
|
||||||
ch = head['channels']
|
ch = head['channels']
|
||||||
keys = list(head.keys())[-ch:]
|
keys = list(head.keys())[-ch:]
|
||||||
len_timestamps = head['Zeit']['len']
|
len_timestamps = head['Zeit']['len']
|
||||||
@ -80,6 +96,8 @@ class BasePerformanceFactory(ABC):
|
|||||||
|
|
||||||
|
|
||||||
class BaseProduct(ABC):
|
class BaseProduct(ABC):
|
||||||
|
# TODO: Аналогично BasePerformanceFactory: снова в абстракции куча реализаций.
|
||||||
|
# TODO: Может стоит дать классу название осмысленное, а не из статьи по паттернам? =)
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._signals = []
|
self._signals = []
|
||||||
|
|
||||||
@ -117,10 +135,16 @@ class BaseProduct(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def operation(self):
|
def operation(self):
|
||||||
|
# TODO: Аналогично: что за операция? Что она делает? Не понятно.
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class Performance(BasePerformanceFactory):
|
class Performance(BasePerformanceFactory):
|
||||||
|
# TODO: где реализация factory_method? Если не нужен, то надо его убрать из интерфейса.
|
||||||
|
# TODO: данный класс, простите, лебедь раком щуку. На все руки мастер. И то создает, и это создает... И принцип
|
||||||
|
# единичной ответственности нарушает, и принцип подстановки...
|
||||||
|
# Если реализуешь паттерн "Фабричный метод", то реализуй его правильно: для каждого порождаемого объекта свой
|
||||||
|
# класс - создатель, классы наследники в полной мере имплементируют методы класса - родителя...
|
||||||
|
|
||||||
def robot_method(self) -> BaseProduct:
|
def robot_method(self) -> BaseProduct:
|
||||||
return RobotData()
|
return RobotData()
|
||||||
@ -132,10 +156,12 @@ class Performance(BasePerformanceFactory):
|
|||||||
return CommData()
|
return CommData()
|
||||||
|
|
||||||
def job(self, path:str, TWC_raw:pd.DataFrame) -> list[pd.DataFrame, list[pd.DataFrame], list[pd.DataFrame]]:
|
def job(self, path:str, TWC_raw:pd.DataFrame) -> list[pd.DataFrame, list[pd.DataFrame], list[pd.DataFrame]]:
|
||||||
|
# TODO: сигнатура метода не соответствует той, которая определена в родительском классе.
|
||||||
robot = self.robot_method()
|
robot = self.robot_method()
|
||||||
TWC = self.TWC_method()
|
TWC = self.TWC_method()
|
||||||
comm=self.comm_method()
|
comm=self.comm_method()
|
||||||
dataframe = self._get_file_data(path)
|
dataframe = self._get_file_data(path)
|
||||||
|
# TODO: operation в родителе определен без входных параметров, используется с ними. Не надо так делать.
|
||||||
rob_comm, rob_df = robot.operation(dataframe)
|
rob_comm, rob_df = robot.operation(dataframe)
|
||||||
TWC_comm, TWC_df = TWC.operation(TWC_raw)
|
TWC_comm, TWC_df = TWC.operation(TWC_raw)
|
||||||
comm_df = comm.operation(rob_comm, TWC_comm)
|
comm_df = comm.operation(rob_comm, TWC_comm)
|
||||||
@ -156,6 +182,8 @@ class RobotData(BaseProduct):
|
|||||||
communication_sig = {'sent':events["$OUT3244"]["fall"], 'received':events[""][""]}
|
communication_sig = {'sent':events["$OUT3244"]["fall"], 'received':events[""][""]}
|
||||||
point_interval = self._form_intervals(start=events["$OUT3012"]["rise"], end=events["$OUT3012"]["fall"])
|
point_interval = self._form_intervals(start=events["$OUT3012"]["rise"], end=events["$OUT3012"]["fall"])
|
||||||
movement_interval = self._form_intervals(start=events["$OUT3244"]["rise"], end=events["$OUT3244"]["fall"])
|
movement_interval = self._form_intervals(start=events["$OUT3244"]["rise"], end=events["$OUT3244"]["fall"])
|
||||||
|
# TODO: возвращаемый результат не соответствует аннотированному.
|
||||||
|
# TODO: зачем возвращать DataFrame? В этом есть какой-то философский смысл?
|
||||||
return communication_sig, pd.DataFrame({'in_point':point_interval,
|
return communication_sig, pd.DataFrame({'in_point':point_interval,
|
||||||
'in_move':movement_interval})
|
'in_move':movement_interval})
|
||||||
|
|
||||||
@ -177,6 +205,8 @@ class TWC_Data(BaseProduct):
|
|||||||
squeeze_interval = self._form_intervals(start=events["Squeeze"]["rise"], end=events["Squeeze"]["fall"])
|
squeeze_interval = self._form_intervals(start=events["Squeeze"]["rise"], end=events["Squeeze"]["fall"])
|
||||||
relief_interval = self._form_intervals(start=events["Relief"]["rise"], end=events["Relief"]["fall"])
|
relief_interval = self._form_intervals(start=events["Relief"]["rise"], end=events["Relief"]["fall"])
|
||||||
oncoming_interval = self._form_intervals(start=events["Oncoming"]["rise"], end=events["Oncoming"]["fall"])
|
oncoming_interval = self._form_intervals(start=events["Oncoming"]["rise"], end=events["Oncoming"]["fall"])
|
||||||
|
# TODO: возвращаемый результат не соответствует аннотированному.
|
||||||
|
# TODO: зачем возвращать DataFrame? В этом есть какой-то философский смысл?
|
||||||
return communication_sig, pd.DataFrame({'in_closing':closing_interval,
|
return communication_sig, pd.DataFrame({'in_closing':closing_interval,
|
||||||
'in_squeeze':squeeze_interval,
|
'in_squeeze':squeeze_interval,
|
||||||
'in_relief':relief_interval,
|
'in_relief':relief_interval,
|
||||||
|
|||||||
@ -1,29 +1,52 @@
|
|||||||
|
# TODO: Тесты должны лежать в директории tests.
|
||||||
|
|
||||||
|
# TODO: Правило хорошего тона при оформлении импортов: сначала встроенные библиотеки,
|
||||||
|
# через одну пустую строку - дополнительно установленные, и еще через одну пустую строку - собственные импорты.
|
||||||
from src.OptAlgorithm.OptAlgorithm import OptAlgorithm
|
from src.OptAlgorithm.OptAlgorithm import OptAlgorithm
|
||||||
from src.utils import read_json
|
from src.utils import read_json
|
||||||
|
|
||||||
from matplotlib import pyplot as plt, use
|
from matplotlib import pyplot as plt, use
|
||||||
|
|
||||||
from numpy import cos, sin, sqrt, cbrt, arcsin, linspace, array
|
from numpy import cos, sin, sqrt, cbrt, arcsin, linspace, array
|
||||||
|
# TODO: Отступ перед блоком if __name__ == "__main__" - 2 пустых строки.
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# TODO: Что означают переменные ниже? Имена переменных должны быть осмысленными, чтобы их смысл был понятен не
|
||||||
|
# не только автору кода.
|
||||||
tq = 1
|
tq = 1
|
||||||
ts = linspace(0, tq, 200000)
|
ts = linspace(0, tq, 200000)
|
||||||
|
|
||||||
operator_params = read_json("params/operator_params.json")
|
operator_params = read_json("../params/operator_params.json")
|
||||||
system_params = read_json("params/system_params.json")
|
system_params = read_json("../params/system_params.json")
|
||||||
|
|
||||||
|
# TODO: 2 блока кода ниже (с циклами) нарушают принцип DRY (Don't Repeat Yourself):
|
||||||
|
# повторяющийся блок кода следует вынести в отдельную функцию.
|
||||||
|
# Например, в такую:
|
||||||
|
# def foo(params_dict: dict) -> dict:
|
||||||
|
# result_dict = {}
|
||||||
|
# i = 1
|
||||||
|
# for key, value in params_dict.items():
|
||||||
|
# if isinstance(value, list):
|
||||||
|
# result_dict[key] = value[i] if len(value) > i else value[0]
|
||||||
|
# else:
|
||||||
|
# result_dict[key] = value
|
||||||
|
# return result_dict
|
||||||
|
# И потом вызвать ее как-то так:
|
||||||
|
# non_array_operator_params, non_array_system_params = foo(operator_params), foo(system_params)
|
||||||
|
|
||||||
non_array_operator_params = {}
|
non_array_operator_params = {}
|
||||||
i = 1
|
i = 1
|
||||||
for key, value in operator_params.items():
|
for key, value in operator_params.items():
|
||||||
|
# TODO: Не совсем понятен смысл такой проверки. Если хотим проверить принадлежность value к list,
|
||||||
|
# то лучше использовать для этого isinstance.
|
||||||
if hasattr(value, "__len__"):
|
if hasattr(value, "__len__"):
|
||||||
|
# TODO: Можно заменить однострочным выражением: result_dict[key] = value[i] if len(value) > i else value[0]
|
||||||
if len(value) > i:
|
if len(value) > i:
|
||||||
non_array_operator_params[key] = value[i]
|
non_array_operator_params[key] = value[i]
|
||||||
else:
|
else:
|
||||||
non_array_operator_params[key] = value[0]
|
non_array_operator_params[key] = value[0]
|
||||||
else:
|
else:
|
||||||
non_array_operator_params[key] = value
|
non_array_operator_params[key] = value
|
||||||
|
|
||||||
non_array_system_params = {}
|
non_array_system_params = {}
|
||||||
for key, value in system_params.items():
|
for key, value in system_params.items():
|
||||||
if hasattr(value, "__len__"):
|
if hasattr(value, "__len__"):
|
||||||
@ -33,7 +56,7 @@ if __name__ == "__main__":
|
|||||||
non_array_system_params[key] = value[0]
|
non_array_system_params[key] = value[0]
|
||||||
else:
|
else:
|
||||||
non_array_system_params[key] = value
|
non_array_system_params[key] = value
|
||||||
|
|
||||||
|
|
||||||
opt = OptAlgorithm(non_array_operator_params, non_array_system_params)
|
opt = OptAlgorithm(non_array_operator_params, non_array_system_params)
|
||||||
Xs = array([opt.getVar("X1", t) for t in ts])
|
Xs = array([opt.getVar("X1", t) for t in ts])
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class UMLCreator:
|
|||||||
real_data = []
|
real_data = []
|
||||||
ideal_data = []
|
ideal_data = []
|
||||||
if not self.theor_mode:
|
if not self.theor_mode:
|
||||||
|
# TODO: Писать так условия - моветон. Как вариант, можно применить конструкцию match - case.
|
||||||
for key, items in self.timings_dict.items():
|
for key, items in self.timings_dict.items():
|
||||||
if key == 'closure': ideal_time = self._ideal_time[0]
|
if key == 'closure': ideal_time = self._ideal_time[0]
|
||||||
elif key == 'compression': ideal_time = self._ideal_time[1]
|
elif key == 'compression': ideal_time = self._ideal_time[1]
|
||||||
@ -98,7 +98,7 @@ class UMLCreator:
|
|||||||
timings_dict: dict,
|
timings_dict: dict,
|
||||||
mode: bool,
|
mode: bool,
|
||||||
name:str):
|
name:str):
|
||||||
|
# TODO: Это надо в конструкторе класса делать!
|
||||||
self._ideal_time = ideal_time
|
self._ideal_time = ideal_time
|
||||||
self.bool_dict = bool_dict
|
self.bool_dict = bool_dict
|
||||||
self.float_dict = float_dict
|
self.float_dict = float_dict
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
from plantuml import PlantUML
|
from plantuml import PlantUML
|
||||||
from os.path import abspath
|
from os.path import abspath
|
||||||
|
# TODO: Навести порядок в импортах.
|
||||||
|
|
||||||
class Request:
|
class Request:
|
||||||
|
# TODO: Имя класса не соответствует тому, что он делает.
|
||||||
def __init__(self, server_url: str):
|
def __init__(self, server_url: str):
|
||||||
self._server_url = server_url
|
self._server_url = server_url
|
||||||
|
|
||||||
@ -13,12 +14,15 @@ class Request:
|
|||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
def _startUML(self):
|
def _startUML(self):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.reqArr.append('@startuml')
|
self.reqArr.append('@startuml')
|
||||||
|
|
||||||
def _endUML(self):
|
def _endUML(self):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.reqArr.append('@enduml')
|
self.reqArr.append('@enduml')
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
# TODO: Атрибуты класса инициализируются в конструкторе класса.
|
||||||
self.timestamps = {}
|
self.timestamps = {}
|
||||||
self.reqArr = []
|
self.reqArr = []
|
||||||
self.variables = []
|
self.variables = []
|
||||||
@ -27,28 +31,36 @@ class Request:
|
|||||||
self._startUML()
|
self._startUML()
|
||||||
|
|
||||||
def addAnalog(self, name, string):
|
def addAnalog(self, name, string):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.variables.append(f'analog "{string}" as {name}')
|
self.variables.append(f'analog "{string}" as {name}')
|
||||||
|
|
||||||
def addBinary(self, name, string, style = ''):
|
def addBinary(self, name, string, style = ''):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
if style: name = name + '<<' + style + '>>'
|
if style: name = name + '<<' + style + '>>'
|
||||||
self.variables.append(f'binary "{string}" as {name}')
|
self.variables.append(f'binary "{string}" as {name}')
|
||||||
|
|
||||||
def addClock(self, name, string):
|
def addClock(self, name, string):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.variables.append(f'clock "{string}" as {name}')
|
self.variables.append(f'clock "{string}" as {name}')
|
||||||
|
|
||||||
def addConcise(self, name, string):
|
def addConcise(self, name, string):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.variables.append(f'concise "{string}" as {name}')
|
self.variables.append(f'concise "{string}" as {name}')
|
||||||
|
|
||||||
def addRobust(self, name, string):
|
def addRobust(self, name, string):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.variables.append(f'robust "{string}" as {name}')
|
self.variables.append(f'robust "{string}" as {name}')
|
||||||
|
|
||||||
def appendStr(self, string = ''):
|
def appendStr(self, string = ''):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.variables.append(string)
|
self.variables.append(string)
|
||||||
|
|
||||||
def addLineStyle(self, name, color = 'green', thicknes = 1):
|
def addLineStyle(self, name, color = 'green', thicknes = 1):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
|
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
|
||||||
|
|
||||||
def generateSVG(self, name):
|
def generateSVG(self, name):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self._compileUML(name)
|
self._compileUML(name)
|
||||||
filename = abspath(f'{name}.txt')
|
filename = abspath(f'{name}.txt')
|
||||||
self.server.processes_file(filename, outfile=f'{name}.svg')
|
self.server.processes_file(filename, outfile=f'{name}.svg')
|
||||||
@ -56,6 +68,7 @@ class Request:
|
|||||||
#return result
|
#return result
|
||||||
|
|
||||||
def setTimestamps(self, name, input):
|
def setTimestamps(self, name, input):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
for time, state in input:
|
for time, state in input:
|
||||||
try:
|
try:
|
||||||
self.timestamps[f'@{time}'].append(f'{name} is {state}')
|
self.timestamps[f'@{time}'].append(f'{name} is {state}')
|
||||||
@ -63,11 +76,13 @@ class Request:
|
|||||||
self.timestamps[f'@{time}'] = [f'{name} is {state}']
|
self.timestamps[f'@{time}'] = [f'{name} is {state}']
|
||||||
|
|
||||||
def _addTimestamp(self, timecode, vars):
|
def _addTimestamp(self, timecode, vars):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.reqArr.append(timecode)
|
self.reqArr.append(timecode)
|
||||||
for var in vars:
|
for var in vars:
|
||||||
self.reqArr.append(var)
|
self.reqArr.append(var)
|
||||||
|
|
||||||
def _addHeader(self):
|
def _addHeader(self):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self.reqArr.append('<style>')
|
self.reqArr.append('<style>')
|
||||||
if self.lineStyles:
|
if self.lineStyles:
|
||||||
self.reqArr.append('timingDiagram {')
|
self.reqArr.append('timingDiagram {')
|
||||||
@ -80,9 +95,11 @@ class Request:
|
|||||||
self.reqArr.append('</style>')
|
self.reqArr.append('</style>')
|
||||||
|
|
||||||
def _addVariables(self):
|
def _addVariables(self):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
for var in self.variables: self.reqArr.append(str(var))
|
for var in self.variables: self.reqArr.append(str(var))
|
||||||
|
|
||||||
def _compileUML(self, name):
|
def _compileUML(self, name):
|
||||||
|
# TODO: Единый стиль именования методов! (snake_case / lowercase).
|
||||||
self._addHeader()
|
self._addHeader()
|
||||||
self._addVariables()
|
self._addVariables()
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
from __future__ import annotations
|
# TODO: Почему каталог с базовыми классами лежит внутри каталога с утилитами? Это странно.
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
# TODO: Правило хорошего тона при оформлении импортов: сначала встроенные библиотеки,
|
||||||
|
# через одну пустую строку - дополнительно установленные, и еще через одну пустую строку - собственные импорты.
|
||||||
import os
|
import os
|
||||||
from typing import Optional, Union, Any
|
from typing import Optional, Union, Any
|
||||||
from cachetools import LRUCache
|
from cachetools import LRUCache
|
||||||
@ -13,12 +17,15 @@ from OptAlgorithm import OptAlgorithm
|
|||||||
from utils.qt_settings import dark_style
|
from utils.qt_settings import dark_style
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Глобальное замечание: Базовые классы создаются для обозначения неких базовых свойств объектов.
|
||||||
|
# Конкретная же реализация возлагается на классы - наследники.
|
||||||
|
|
||||||
|
|
||||||
class BaseMediator:
|
class BaseMediator:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
monitor: BaseDirectoryMonitor,
|
monitor: BaseDirectoryMonitor,
|
||||||
converter: BaseDataConverter,
|
converter: BaseDataConverter,
|
||||||
|
# TODO: passport_former
|
||||||
passportFormer: BasePointPassportFormer,
|
passportFormer: BasePointPassportFormer,
|
||||||
plot: BasePlotWidget,
|
plot: BasePlotWidget,
|
||||||
controller: BaseController):
|
controller: BaseController):
|
||||||
@ -37,12 +44,13 @@ class BaseMediator:
|
|||||||
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget],
|
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget],
|
||||||
data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
|
data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
|
||||||
...
|
...
|
||||||
|
# TODO: Отступы между методами класса - одна пустая строка.
|
||||||
def update_settings (self, data: list[dict]):
|
def update_settings (self, data: list[dict]):
|
||||||
...
|
...
|
||||||
|
|
||||||
def update_status(self, msg: Union[str, float]) -> None:
|
def update_status(self, msg: Union[str, float]) -> None:
|
||||||
...
|
...
|
||||||
|
# TODO: Отступы между классами - две пустых строки.
|
||||||
class BaseDirectoryMonitor:
|
class BaseDirectoryMonitor:
|
||||||
|
|
||||||
update_timer = QTimer()
|
update_timer = QTimer()
|
||||||
@ -88,7 +96,8 @@ class BaseDirectoryMonitor:
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
self.isActive = False
|
self.isActive = False
|
||||||
self.update_timer.stop()
|
self.update_timer.stop()
|
||||||
|
# TODO: Почему монитор директорий обновляет какие-то настройки, графики и т.д.? Помним про
|
||||||
|
# принцип единичной ответственности.
|
||||||
def update_settings(self, data: list[dict]) -> None:
|
def update_settings(self, data: list[dict]) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -96,6 +105,7 @@ class BaseDirectoryMonitor:
|
|||||||
...
|
...
|
||||||
|
|
||||||
def force_all_dir(self):
|
def force_all_dir(self):
|
||||||
|
# TODO: По сигнатуре метода вообще не понятно, что он должен делать.
|
||||||
...
|
...
|
||||||
|
|
||||||
class BaseDataConverter:
|
class BaseDataConverter:
|
||||||
@ -230,6 +240,9 @@ class BasePlotWidget:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
def set_style(self, object: Union[QTabWidget, QWidget]) -> None:
|
def set_style(self, object: Union[QTabWidget, QWidget]) -> None:
|
||||||
|
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
|
||||||
|
# TODO: object - зарезервированное слово. Использовать эти слова для именования переменных не правильно.
|
||||||
|
# Если хочется использовать именно такое слово, можно написать "object_".
|
||||||
object.setStyleSheet(
|
object.setStyleSheet(
|
||||||
"""QLabel {
|
"""QLabel {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
@ -239,6 +252,8 @@ class BasePlotWidget:
|
|||||||
}""")
|
}""")
|
||||||
|
|
||||||
def _downsample_data(self, x, y, max_points=5000):
|
def _downsample_data(self, x, y, max_points=5000):
|
||||||
|
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
|
||||||
|
# TODO: Какой тип данных у 'x'? Какой тип данных у 'y'? Надо добавить аннотации типов.
|
||||||
"""
|
"""
|
||||||
Понижает разрешение данных до заданного количества точек для улучшения производительности навигатора.
|
Понижает разрешение данных до заданного количества точек для улучшения производительности навигатора.
|
||||||
"""
|
"""
|
||||||
@ -273,12 +288,13 @@ class BasePlotWidget:
|
|||||||
|
|
||||||
# Связываем изменение региона навигатора с обновлением области просмотра основного графика
|
# Связываем изменение региона навигатора с обновлением области просмотра основного графика
|
||||||
ROI_region.sigRegionChanged.connect(lambda: self._sync_main_plot_with_navigator(main_plot, ROI_region))
|
ROI_region.sigRegionChanged.connect(lambda: self._sync_main_plot_with_navigator(main_plot, ROI_region))
|
||||||
|
# TODO: Возвращаемый результат не соответствует аннотированному в сигнатуре метода.
|
||||||
return navigator, ROI_region
|
return navigator, ROI_region
|
||||||
|
|
||||||
def _sync_main_plot_with_navigator(self,
|
def _sync_main_plot_with_navigator(self,
|
||||||
main_plot: pg.PlotItem,
|
main_plot: pg.PlotItem,
|
||||||
region: pg.LinearRegionItem):
|
region: pg.LinearRegionItem):
|
||||||
|
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
|
||||||
"""
|
"""
|
||||||
Синхронизирует область просмотра основного графика с регионом навигатора.
|
Синхронизирует область просмотра основного графика с регионом навигатора.
|
||||||
"""
|
"""
|
||||||
@ -288,11 +304,14 @@ class BasePlotWidget:
|
|||||||
main_plot.setXRange(x_min, x_max, padding=0)
|
main_plot.setXRange(x_min, x_max, padding=0)
|
||||||
main_plot.blockSignals(False)
|
main_plot.blockSignals(False)
|
||||||
|
|
||||||
|
# TODO: Методы _mirror_shift_data и _shift_data нарушают принцип DRY: дублирование кода.
|
||||||
|
# Сделать ОДИН метод, одним из входных параметров которого будет lambda-функция, которая применяется к dataframe.
|
||||||
def _mirror_shift_data(self,
|
def _mirror_shift_data(self,
|
||||||
valid_str: str,
|
valid_str: str,
|
||||||
signals: list[dict],
|
signals: list[dict],
|
||||||
dataframe: pd.DataFrame,
|
dataframe: pd.DataFrame,
|
||||||
shift: float) -> pd.DataFrame:
|
shift: float) -> pd.DataFrame:
|
||||||
|
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
|
||||||
keys = dataframe.keys()
|
keys = dataframe.keys()
|
||||||
for signal in signals:
|
for signal in signals:
|
||||||
if valid_str in signal["name"] and signal["name"] in keys:
|
if valid_str in signal["name"] and signal["name"] in keys:
|
||||||
@ -304,6 +323,7 @@ class BasePlotWidget:
|
|||||||
signals: list[dict],
|
signals: list[dict],
|
||||||
dataframe: pd.DataFrame,
|
dataframe: pd.DataFrame,
|
||||||
shift: float) -> pd.DataFrame:
|
shift: float) -> pd.DataFrame:
|
||||||
|
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
|
||||||
keys = dataframe.keys()
|
keys = dataframe.keys()
|
||||||
for signal in signals:
|
for signal in signals:
|
||||||
if valid_str in signal["name"] and signal["name"] in keys:
|
if valid_str in signal["name"] and signal["name"] in keys:
|
||||||
@ -311,6 +331,7 @@ class BasePlotWidget:
|
|||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def _sync_navigator_with_main(self, main_plot: pg.PlotItem, region:pg.LinearRegionItem):
|
def _sync_navigator_with_main(self, main_plot: pg.PlotItem, region:pg.LinearRegionItem):
|
||||||
|
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
|
||||||
"""
|
"""
|
||||||
Синхронизирует регион навигатора с областью просмотра основного графика.
|
Синхронизирует регион навигатора с областью просмотра основного графика.
|
||||||
"""
|
"""
|
||||||
@ -334,6 +355,8 @@ class BasePlotWidget:
|
|||||||
|
|
||||||
@opt.setter
|
@opt.setter
|
||||||
def opt(self, opt: BaseIdealDataBuilder):
|
def opt(self, opt: BaseIdealDataBuilder):
|
||||||
|
# TODO: Атрибуты класса следует сначала определять в конструкторе класса.
|
||||||
|
# TODO: Что такое opt? Optical? Option? Optimal? Optimus Prime?
|
||||||
self._opt = opt
|
self._opt = opt
|
||||||
|
|
||||||
def build(self, data: list[pd.DataFrame]) -> list[QWidget]:
|
def build(self, data: list[pd.DataFrame]) -> list[QWidget]:
|
||||||
@ -349,9 +372,11 @@ class BaseController(QObject):
|
|||||||
...
|
...
|
||||||
|
|
||||||
def raport_mode (self) -> None:
|
def raport_mode (self) -> None:
|
||||||
|
# TODO: обычно в названиях методов должен присутствовать глагол.
|
||||||
...
|
...
|
||||||
|
|
||||||
def seeking_mode(self) -> None:
|
def seeking_mode(self) -> None:
|
||||||
|
# TODO: обычно в названиях методов должен присутствовать глагол.
|
||||||
...
|
...
|
||||||
|
|
||||||
def open_file(self, filepath: str) -> None:
|
def open_file(self, filepath: str) -> None:
|
||||||
@ -429,6 +454,7 @@ class BaseMainWindow(QMainWindow):
|
|||||||
|
|
||||||
@controller.setter
|
@controller.setter
|
||||||
def controller(self, controller: BaseController) -> None:
|
def controller(self, controller: BaseController) -> None:
|
||||||
|
# TODO: зачем делать setter для контроллера, если ты его экземпляр передаешь в конструктор?
|
||||||
self._controller = controller
|
self._controller = controller
|
||||||
|
|
||||||
def set_style(self, object: Union[QTabWidget, QWidget, QMainWindow]) -> None:
|
def set_style(self, object: Union[QTabWidget, QWidget, QMainWindow]) -> None:
|
||||||
|
|||||||
@ -9,27 +9,34 @@ class DiagramParser:
|
|||||||
system_config["Welding_signal"],
|
system_config["Welding_signal"],
|
||||||
system_config["Release_signal"],
|
system_config["Release_signal"],
|
||||||
system_config["Oncomming_signal"]]
|
system_config["Oncomming_signal"]]
|
||||||
|
# TODO: Переменные в Python именуются в lowercase либо snake_case.
|
||||||
self.boolDict = {}
|
self.boolDict = {}
|
||||||
self.floatDict = {}
|
self.floatDict = {}
|
||||||
self.timingsDict = {}
|
self.timingsDict = {}
|
||||||
self.theor_mode = False
|
self.theor_mode = False
|
||||||
|
|
||||||
def setData(self, path):
|
def setData(self, path):
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
|
# TODO: Данный метод устанавливает какой-то режим, читает CSV, наполняет словари некими данными.
|
||||||
|
# Декомпозировать на разные методы, каждый из которых будет решать какую-то свою задачу.
|
||||||
if not path:
|
if not path:
|
||||||
self.theor_mode = True
|
self.theor_mode = True
|
||||||
else:
|
else:
|
||||||
self.data = pd.read_csv(path)
|
self.data = pd.read_csv(path)
|
||||||
|
|
||||||
|
# TODO: 2 цикла ниже: дублирование кода. Устранить.
|
||||||
for signalName in self.data.columns:
|
for signalName in self.data.columns:
|
||||||
|
# TODO: if isinstance...
|
||||||
if type (self.data[signalName].iloc[0]) == np.bool:
|
if type (self.data[signalName].iloc[0]) == np.bool:
|
||||||
self.boolDict[signalName] = self._getBoolChanges(signalName)
|
self.boolDict[signalName] = self._getBoolChanges(signalName)
|
||||||
|
|
||||||
for signalName in self.data.columns:
|
for signalName in self.data.columns:
|
||||||
|
# TODO: if isinstance...
|
||||||
if type (self.data[signalName].iloc[0]) == np.float64:
|
if type (self.data[signalName].iloc[0]) == np.float64:
|
||||||
self.floatDict[signalName] = self._getFloatChanges(signalName)
|
self.floatDict[signalName] = self._getFloatChanges(signalName)
|
||||||
|
|
||||||
for key, items in self.boolDict.items():
|
for key, items in self.boolDict.items():
|
||||||
|
# TODO: Писать так условия - моветон. Как вариант, можно применить конструкцию match - case.
|
||||||
if key == self.signals[0]: name = "closure"
|
if key == self.signals[0]: name = "closure"
|
||||||
elif key == self.signals[1]: name = "compression"
|
elif key == self.signals[1]: name = "compression"
|
||||||
elif key == self.signals[2]: name = "welding"
|
elif key == self.signals[2]: name = "welding"
|
||||||
@ -46,22 +53,34 @@ class DiagramParser:
|
|||||||
self.timingsDict[name].append([items[i][0], items[i][0]+0.01])
|
self.timingsDict[name].append([items[i][0], items[i][0]+0.01])
|
||||||
|
|
||||||
def getBoolDict (self) -> dict:
|
def getBoolDict (self) -> dict:
|
||||||
|
# TODO: сделать через декоратор @property:
|
||||||
|
# @property
|
||||||
|
# def bool_dict(self) -> dict:
|
||||||
|
# return self._bool_dict
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
return self.boolDict
|
return self.boolDict
|
||||||
|
|
||||||
def getFloatDict (self) -> dict:
|
def getFloatDict (self) -> dict:
|
||||||
|
# TODO: сделать через декоратор @property:
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
return self.floatDict
|
return self.floatDict
|
||||||
|
|
||||||
def getRealTimings(self) -> dict:
|
def getRealTimings(self) -> dict:
|
||||||
|
# TODO: сделать через декоратор @property:
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
return self.timingsDict
|
return self.timingsDict
|
||||||
|
|
||||||
def getMode(self) -> bool:
|
def getMode(self) -> bool:
|
||||||
|
# TODO: сделать через декоратор @property:
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
return self.theor_mode
|
return self.theor_mode
|
||||||
|
|
||||||
def _getBoolChanges(self, signalName) -> list:
|
def _getBoolChanges(self, signalName) -> list:
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
timeCode = self.data['time']
|
timeCode = self.data['time']
|
||||||
signal_values = self.data[signalName]
|
signal_values = self.data[signalName]
|
||||||
changes = []
|
changes = []
|
||||||
|
# TODO: if len(signal_values)...
|
||||||
if len(signal_values) > 0:
|
if len(signal_values) > 0:
|
||||||
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
|
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
|
||||||
|
|
||||||
@ -73,6 +92,7 @@ class DiagramParser:
|
|||||||
|
|
||||||
|
|
||||||
def _getFloatChanges(self, signalName) -> list:
|
def _getFloatChanges(self, signalName) -> list:
|
||||||
|
# TODO: Методы в Python именуются в lowercase либо snake_case.
|
||||||
timeCode = self.data['time']
|
timeCode = self.data['time']
|
||||||
signal_values = self.data[signalName]
|
signal_values = self.data[signalName]
|
||||||
changes = []
|
changes = []
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# TODO: Было бы логично разместить данный модуль в каталоге gui.
|
||||||
|
|
||||||
dark_style = """
|
dark_style = """
|
||||||
/*
|
/*
|
||||||
------------------------------------------------------
|
------------------------------------------------------
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user