JustDreamer7 2 lat temu
commit
999a12df7d

+ 123 - 0
drawing_graphs.py

@@ -0,0 +1,123 @@
+import cycler
+from matplotlib import pyplot as plt
+
+
+class GraphsDrawing:
+    def __init__(self, start_date, end_date, path_to_pic):
+        self.start_date = start_date
+        self.end_date = end_date
+        self.path_to_pic = path_to_pic
+        self.box = {'facecolor': 'white',  # цвет области
+                    'edgecolor': 'red',  # цвет крайней линии
+                    'boxstyle': 'round'}
+
+    @staticmethod
+    def change_design():
+        """Метод, который должен применяться вначале, для изменения размера и веса шрифта. А также для изменения,
+        цвета линий на графиках. Без него цвета, когда количество линий > 10 начнут повторяться"""
+        font = {'weight': 'bold',
+                'size': 18}
+        plt.rc('font', **font)
+        plt.rc('axes',
+               prop_cycle=(
+                   cycler.cycler('color', ['r', 'g', 'b', 'darkblue', 'lawngreen', 'hotpink', 'c', 'y', 'm', 'orange',
+                                           'burlywood', 'darkmagenta', 'grey', 'darkslategray', 'saddlebrown',
+                                           'lightsalmon'])))
+        return None
+
+    def graph_format(self, y_lim, x_label, y_label):
+        """Метод, прописывающий неизменный формат для графиков, желательно добавить смену figsize и fontsize"""
+        plt.figure(figsize=(18, 10))
+        plt.xlabel(x_label, fontsize=40)
+        plt.ylabel(y_label, fontsize=40)
+        plt.grid()
+        plt.minorticks_on()
+        plt.tick_params(axis='both', which='minor', direction='out', length=10, width=2, pad=10)
+        plt.tick_params(axis='both', which='major', direction='out', length=20, width=4, pad=10)
+        plt.grid(which='minor',
+                 color='k',
+                 linestyle=':')
+        plt.ylim(y_lim)
+        plt.xlim([self.start_date, self.end_date])
+
+    def worktime_graph(self, worktime_frame, cluster):
+        """Метод, рисующий график продолжительности работы установки ПРИЗМА-32 для заданного кластера, дает на выходе
+        путь к месту, где в системе лежит график"""
+        self.graph_format(y_lim=[0, 25], x_label='Дата', y_label='Время работы, ч')
+        plt.xticks([i.date() for i in list(worktime_frame['Date'])[::4]],
+                   [i.date() for i in list(worktime_frame['Date'])[::4]])
+        plt.plot(worktime_frame['Date'], worktime_frame['Worktime'], marker='s', markersize=15, color='darkblue',
+                 linewidth='6')
+        plt.title(f"{cluster}-кластер", bbox=self.box, fontsize=20, loc='center')
+        path_pic = f'{self.path_to_pic}\\{self.start_date.year}\\{cluster}worktime{self.start_date.day}-{self.start_date.month}-{self.end_date.day}-{self.end_date.month}.png'
+        plt.savefig(path_pic, bbox_inches='tight')
+        return path_pic
+
+    def amp_5_fr_4_graph(self, amp_5_fr_4_frame, amp_5_fr_4_frame_2, worktime_frame, worktime_frame_2):
+        """Метод, рисующий график количества событий с A>5, Fr>=4 на 1-м и 2-м кластере, дает на выходе путь к месту,
+        где в системе лежит график"""
+        self.graph_format(y_lim=[0, 5], x_label='Дата', y_label='N, соб/час')
+        plt.xticks([i.date() for i in list(amp_5_fr_4_frame['Date'])[::4]],
+                   [i.date() for i in list(amp_5_fr_4_frame['Date'])[::4]])
+        plt.plot(amp_5_fr_4_frame['Date'], amp_5_fr_4_frame['Events'] / worktime_frame['Worktime'], label='1 Кл.',
+                 marker='s', markersize=15, color='darkblue', linewidth='6')
+        plt.plot(amp_5_fr_4_frame_2['Date'], amp_5_fr_4_frame_2['Events'] / worktime_frame_2['Worktime'], label='2 Кл.',
+                 marker='s', markersize=15, color='crimson', linewidth='6')
+        plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
+        path_pic = f'{self.path_to_pic}\\{self.start_date.year}\\amp_5_fr_4{self.start_date.day}-{self.start_date.month}-{self.end_date.day}-{self.end_date.month}.png'
+        plt.savefig(path_pic, bbox_inches='tight')
+        return path_pic
+
+    def neutron_to_0_tr_graph(self, neutron_num_0_tr_frame, cluster):
+        """Метод, рисующий для заданного кластера график нормированного числа импульсов, отобранных установкой
+        ПРИЗМА-32, как нейтрон при самозапуске, происходящем раз в 5 минут. Нормировка произведена на количество
+        самозапусков. Метод дает на выходе путь к месту, где в системе лежит график"""
+        self.graph_format(y_lim=[0, 0.5], x_label='Дата', y_label=r'$(coб)^{-1}$')
+        plt.xticks([i.date() for i in list(neutron_num_0_tr_frame['Date'])[::4]],
+                   [i.date() for i in list(neutron_num_0_tr_frame['Date'])[::4]])
+        plt.title(f"{cluster}-кластер", bbox=self.box, fontsize=20, loc='center')
+        for i in range(1, 17):
+            plt.plot(neutron_num_0_tr_frame['Date'], neutron_num_0_tr_frame[f'n{i}'], label=f'{i}', linewidth=5)
+        plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
+        path_pic = f'{self.path_to_pic}\\{self.start_date.year}\\{cluster}n_to_0_tr{self.start_date.day}-{self.start_date.month}-{self.end_date.day}-{self.end_date.month}.png'
+        plt.savefig(path_pic, bbox_inches='tight')
+        return path_pic
+
+    def amp_distribution_graph(self, amp_distribution_frame, cluster, a_crit, freq):
+        """Метод, рисующий график амплитудного распределений событий с A>5, Fr>=2 для заданного кластера, дает на выходе
+        путь к месту где в системе лежит график"""
+        plt.figure(figsize=(18, 10))
+        plt.xlabel('Амплитуда, код АЦП', fontsize=40)
+        plt.yscale('log')
+        plt.xscale('log')
+        plt.ylabel('N_соб(Fr≥2, A>5)', fontsize=40)
+        plt.minorticks_on()
+        plt.tick_params(axis='both', which='minor', direction='out', length=10, width=2, pad=10)
+        plt.tick_params(axis='both', which='major', direction='out', length=20, width=4, pad=10)
+        plt.xlim([a_crit - 1, 1000])
+        plt.ylim([1, 1000])
+        plt.text(500, 500, "1-кластер", bbox=self.box, fontsize=20)
+        for i in range(1, 17):
+            plt.plot(amp_distribution_frame[amp_distribution_frame[f'amp{i}'] >= a_crit][
+                         f'amp{i}'].value_counts().sort_index().keys().tolist(),
+                     amp_distribution_frame[amp_distribution_frame[f'amp{i}'] >= a_crit][
+                         f'amp{i}'].value_counts().sort_index(), label=f'{i}', linewidth=5)
+        plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
+        path_pic = f'{self.path_to_pic}\\{self.start_date.year}\\{cluster}amp_distr_{a_crit}_fr_{freq}{self.start_date.day}-{self.start_date.month}-{self.end_date.day}-{self.end_date.month}.png'
+        plt.savefig(path_pic, bbox_inches='tight')
+        return path_pic
+
+    def count_rate_graph(self, count_rate_frame, working_frame, cluster, a_crit, freq):
+        """Метод, рисующий скорость счета детекторов для событий с A>5, Fr>=2 для заданного кластера, дает на выходе
+        путь к месту где в системе лежит график"""
+        self.graph_format(y_lim=[0, 8], x_label='Дата', y_label='N, соб/час')
+        plt.title("1-кластер", bbox=self.box, fontsize=20, loc='center')
+        for i in range(1, 17):
+            plt.xticks([i.date() for i in list(count_rate_frame['Date'])[::4]],
+                       [i.date() for i in list(count_rate_frame['Date'])[::4]])
+            plt.plot(count_rate_frame['Date'], count_rate_frame[f'amp{i}'] / working_frame['Worktime'], label=f'{i}',
+                     linewidth=6)
+        plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
+        path_pic = f'{self.path_to_pic}\\{self.start_date.year}\\{cluster}count_rate_{a_crit}_fr_{freq}{self.start_date.day}-{self.start_date.month}-{self.end_date.day}-{self.end_date.month}.png'
+        plt.savefig(path_pic, bbox_inches='tight')
+        return path_pic

+ 0 - 0
interfaces/__init__.py


+ 143 - 0
interfaces/clientui.py

@@ -0,0 +1,143 @@
+# Form implementation generated from reading ui file 'interface.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.3
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again.  Do not edit this file unless you know what you are doing.
+
+# Забей, этот код описывает интерфейс основной проги
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+    def setupUi(self, MainWindow):
+        MainWindow.setObjectName("MainWindow")
+        MainWindow.resize(485, 527)
+        font = QtGui.QFont()
+        font.setItalic(False)
+        font.setStrikeOut(False)
+        MainWindow.setFont(font)
+        MainWindow.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
+        MainWindow.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.PreventContextMenu)
+        MainWindow.setStyleSheet("background-color: rgb(42, 55, 127);\n"
+                                 "color: rgb(255, 255, 255);\n"
+                                 "")
+        self.centralwidget = QtWidgets.QWidget(MainWindow)
+        self.centralwidget.setObjectName("centralwidget")
+        self.runPassport = QtWidgets.QPushButton(self.centralwidget)
+        self.runPassport.setGeometry(QtCore.QRect(40, 400, 131, 61))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(14)
+        font.setBold(True)
+        font.setWeight(75)
+        self.runPassport.setFont(font)
+        self.runPassport.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                       "color: rgb(0, 0, 0);")
+        self.runPassport.setObjectName("runPassport")
+        self.dateEdit = QtWidgets.QDateEdit(self.centralwidget)
+        self.dateEdit.setGeometry(QtCore.QRect(40, 191, 131, 31))
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        self.dateEdit.setFont(font)
+        self.dateEdit.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                    "color: rgb(0, 0, 0);")
+        self.dateEdit.setObjectName("dateEdit")
+        self.dateEdit_2 = QtWidgets.QDateEdit(self.centralwidget)
+        self.dateEdit_2.setGeometry(QtCore.QRect(40, 240, 131, 31))
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        self.dateEdit_2.setFont(font)
+        self.dateEdit_2.setStyleSheet("color: rgb(0, 0, 0);\n"
+                                      "background-color: rgb(255, 255, 255);")
+        self.dateEdit_2.setObjectName("dateEdit_2")
+        self.label = QtWidgets.QLabel(self.centralwidget)
+        self.label.setGeometry(QtCore.QRect(30, 20, 321, 41))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(19)
+        font.setBold(True)
+        font.setWeight(75)
+        self.label.setFont(font)
+        self.label.setAutoFillBackground(False)
+        self.label.setScaledContents(True)
+        self.label.setObjectName("label")
+        self.line = QtWidgets.QFrame(self.centralwidget)
+        self.line.setGeometry(QtCore.QRect(40, 220, 131, 20))
+        self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+        self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
+        self.line.setObjectName("line")
+        self.label_4 = QtWidgets.QLabel(self.centralwidget)
+        self.label_4.setGeometry(QtCore.QRect(40, 150, 161, 31))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(13)
+        font.setBold(True)
+        font.setItalic(False)
+        font.setWeight(75)
+        self.label_4.setFont(font)
+        self.label_4.setObjectName("label_4")
+        self.openDirectory = QtWidgets.QPushButton(self.centralwidget)
+        self.openDirectory.setGeometry(QtCore.QRect(290, 360, 181, 61))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(13)
+        font.setBold(True)
+        font.setWeight(75)
+        font.setStrikeOut(False)
+        font.setStyleStrategy(QtGui.QFont.StyleStrategy.NoAntialias)
+        self.openDirectory.setFont(font)
+        self.openDirectory.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.ActionsContextMenu)
+        self.openDirectory.setAutoFillBackground(False)
+        self.openDirectory.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                         "color: rgb(0, 0, 0);")
+        self.openDirectory.setAutoExclusive(True)
+        self.openDirectory.setObjectName("openDirectory")
+        self.runSevend = QtWidgets.QPushButton(self.centralwidget)
+        self.runSevend.setGeometry(QtCore.QRect(40, 320, 131, 61))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(14)
+        font.setBold(True)
+        font.setWeight(75)
+        self.runSevend.setFont(font)
+        self.runSevend.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                     "color: rgb(0, 0, 0);")
+        self.runSevend.setObjectName("runSevend")
+        self.openFileDirectory = QtWidgets.QPushButton(self.centralwidget)
+        self.openFileDirectory.setGeometry(QtCore.QRect(290, 200, 181, 61))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(13)
+        font.setBold(True)
+        font.setWeight(75)
+        font.setStrikeOut(False)
+        font.setStyleStrategy(QtGui.QFont.StyleStrategy.NoAntialias)
+        self.openFileDirectory.setFont(font)
+        self.openFileDirectory.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.ActionsContextMenu)
+        self.openFileDirectory.setAutoFillBackground(False)
+        self.openFileDirectory.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                             "color: rgb(0, 0, 0);")
+        self.openFileDirectory.setAutoExclusive(True)
+        self.openFileDirectory.setObjectName("openFileDirectory")
+        MainWindow.setCentralWidget(self.centralwidget)
+        self.statusbar = QtWidgets.QStatusBar(MainWindow)
+        self.statusbar.setObjectName("statusbar")
+        MainWindow.setStatusBar(self.statusbar)
+
+        self.retranslateUi(MainWindow)
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+    def retranslateUi(self, MainWindow):
+        _translate = QtCore.QCoreApplication.translate
+        MainWindow.setWindowTitle(_translate("MainWindow", "Passport PRISMA"))
+        self.runPassport.setText(_translate("MainWindow", "Run \n"
+                                                          " Passport"))
+        self.label.setText(_translate("MainWindow", "<html><head/><body><p>Паспорт PRISMA-32</p></body></html>"))
+        self.label_4.setText(_translate("MainWindow", "Выберите дату:"))
+        self.openDirectory.setText(_translate("MainWindow", "Папка \n"
+                                                            " сохранения"))
+        self.runSevend.setText(_translate("MainWindow", "Run 7d"))
+        self.openFileDirectory.setText(_translate("MainWindow", "Папка \n"
+                                                                " файлов PRISMA"))

+ 70 - 0
interfaces/drctryChoice.py

@@ -0,0 +1,70 @@
+# Form implementation generated from reading ui file 'drctryChoice.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.3
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again.  Do not edit this file unless you know what you are doing.
+
+# Забей, этот код описывает интерфейс формы выбора папки сохранения картинок, паспорта и т.д.
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+from PyQt6.QtWidgets import QFileDialog
+
+
+class Ui_drctryChoice(object):
+    def setupUi(self, drctryChoice):
+        drctryChoice.setObjectName("drctryChoice")
+        drctryChoice.resize(706, 95)
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(10)
+        font.setBold(True)
+        font.setWeight(75)
+        drctryChoice.setFont(font)
+        drctryChoice.setStyleSheet("background-color: rgb(61, 255, 200);\n"
+                                   "border-color: rgb(7, 7, 7);")
+        self.lineEdit = QtWidgets.QLineEdit(drctryChoice)
+        self.lineEdit.setGeometry(QtCore.QRect(200, 27, 381, 41))
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        self.lineEdit.setFont(font)
+        self.lineEdit.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                    "color: rgb(0, 0, 0);")
+        self.lineEdit.setText("")
+        self.lineEdit.setObjectName("lineEdit")
+        self.pushButton = QtWidgets.QPushButton(drctryChoice)
+        self.pushButton.setGeometry(QtCore.QRect(600, 33, 93, 29))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(12)
+        font.setBold(True)
+        font.setWeight(75)
+        self.pushButton.setFont(font)
+        self.pushButton.setStyleSheet("background-color: rgb(255, 255, 255);")
+        self.pushButton.setObjectName("pushButton")
+        self.label = QtWidgets.QLabel(drctryChoice)
+        self.label.setGeometry(QtCore.QRect(10, 37, 161, 21))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(12)
+        font.setBold(True)
+        font.setWeight(75)
+        self.label.setFont(font)
+        self.label.setObjectName("label")
+
+        self.retranslateUi(drctryChoice)
+        QtCore.QMetaObject.connectSlotsByName(drctryChoice)
+
+    def retranslateUi(self, drctryChoice):
+        _translate = QtCore.QCoreApplication.translate
+        drctryChoice.setWindowTitle(_translate("drctryChoice", "Выберите папку для сохранения паспорта"))
+        self.lineEdit.setPlaceholderText(_translate("drctryChoice", "Введите папку для сохранения паспорта"))
+        self.pushButton.setText(_translate("drctryChoice", "Обзор"))
+        self.label.setText(_translate("drctryChoice", "Папка для отчета"))
+
+    def get_report_directory(self):
+        dirlist = QFileDialog.getExistingDirectory()
+        self.lineEdit.setText(dirlist)
+        with open('path_prisma_report.txt', 'w') as f:
+            f.write(dirlist)
+        print(dirlist)

+ 101 - 0
interfaces/drctryChoice.ui

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>drctryChoice</class>
+ <widget class="QWidget" name="drctryChoice">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>706</width>
+    <height>95</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <family>Calibri</family>
+    <pointsize>10</pointsize>
+    <weight>75</weight>
+    <bold>true</bold>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>Выберите папку для сохранения паспорта</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true">background-color: rgb(61, 255, 200);
+border-color: rgb(7, 7, 7);</string>
+  </property>
+  <widget class="QLineEdit" name="lineEdit">
+   <property name="geometry">
+    <rect>
+     <x>200</x>
+     <y>27</y>
+     <width>381</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>10</pointsize>
+    </font>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+   <property name="placeholderText">
+    <string>Введите папку для сохранения паспорта</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="pushButton">
+   <property name="geometry">
+    <rect>
+     <x>600</x>
+     <y>33</y>
+     <width>93</width>
+     <height>29</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <family>Calibri</family>
+     <pointsize>12</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background-color: rgb(255, 255, 255);</string>
+   </property>
+   <property name="text">
+    <string>Обзор</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>37</y>
+     <width>161</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <family>Calibri</family>
+     <pointsize>12</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="text">
+    <string>Папка для отчета</string>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 264 - 0
interfaces/interface.ui

@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>485</width>
+    <height>527</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <italic>false</italic>
+    <strikeout>false</strikeout>
+   </font>
+  </property>
+  <property name="focusPolicy">
+   <enum>Qt::TabFocus</enum>
+  </property>
+  <property name="contextMenuPolicy">
+   <enum>Qt::PreventContextMenu</enum>
+  </property>
+  <property name="windowTitle">
+   <string>Passport PRISMA</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true">background-color: rgb(42, 55, 127);
+color: rgb(255, 255, 255);
+</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <widget class="QPushButton" name="runPassport">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>400</y>
+      <width>131</width>
+      <height>61</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <family>Calibri</family>
+      <pointsize>14</pointsize>
+      <weight>75</weight>
+      <bold>true</bold>
+     </font>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+    </property>
+    <property name="text">
+     <string>Run 
+ Passport</string>
+    </property>
+   </widget>
+   <widget class="QDateEdit" name="dateEdit">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>191</y>
+      <width>131</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <pointsize>10</pointsize>
+     </font>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+    </property>
+   </widget>
+   <widget class="QDateEdit" name="dateEdit_2">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>240</y>
+      <width>131</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <pointsize>10</pointsize>
+     </font>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">color: rgb(0, 0, 0);
+background-color: rgb(255, 255, 255);</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>20</y>
+      <width>321</width>
+      <height>41</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <family>Calibri</family>
+      <pointsize>19</pointsize>
+      <weight>75</weight>
+      <bold>true</bold>
+     </font>
+    </property>
+    <property name="autoFillBackground">
+     <bool>false</bool>
+    </property>
+    <property name="text">
+     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Паспорт PRISMA-32&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+    </property>
+    <property name="scaledContents">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="Line" name="line">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>220</y>
+      <width>131</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="orientation">
+     <enum>Qt::Horizontal</enum>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_4">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>150</y>
+      <width>161</width>
+      <height>31</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <family>Calibri</family>
+      <pointsize>13</pointsize>
+      <weight>75</weight>
+      <italic>false</italic>
+      <bold>true</bold>
+     </font>
+    </property>
+    <property name="text">
+     <string>Выберите дату:</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="openDirectory">
+    <property name="geometry">
+     <rect>
+      <x>290</x>
+      <y>360</y>
+      <width>181</width>
+      <height>61</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <family>Calibri</family>
+      <pointsize>13</pointsize>
+      <weight>75</weight>
+      <bold>true</bold>
+      <strikeout>false</strikeout>
+      <stylestrategy>NoAntialias</stylestrategy>
+     </font>
+    </property>
+    <property name="contextMenuPolicy">
+     <enum>Qt::ActionsContextMenu</enum>
+    </property>
+    <property name="autoFillBackground">
+     <bool>false</bool>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+    </property>
+    <property name="text">
+     <string>Папка 
+ сохранения</string>
+    </property>
+    <property name="autoExclusive">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="runSevend">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>320</y>
+      <width>131</width>
+      <height>61</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <family>Calibri</family>
+      <pointsize>14</pointsize>
+      <weight>75</weight>
+      <bold>true</bold>
+     </font>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+    </property>
+    <property name="text">
+     <string>Run 7d</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="openFileDirectory">
+    <property name="geometry">
+     <rect>
+      <x>290</x>
+      <y>200</y>
+      <width>181</width>
+      <height>61</height>
+     </rect>
+    </property>
+    <property name="font">
+     <font>
+      <family>Calibri</family>
+      <pointsize>13</pointsize>
+      <weight>75</weight>
+      <bold>true</bold>
+      <strikeout>false</strikeout>
+      <stylestrategy>NoAntialias</stylestrategy>
+     </font>
+    </property>
+    <property name="contextMenuPolicy">
+     <enum>Qt::ActionsContextMenu</enum>
+    </property>
+    <property name="autoFillBackground">
+     <bool>false</bool>
+    </property>
+    <property name="styleSheet">
+     <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+    </property>
+    <property name="text">
+     <string>Папка 
+ файлов PRISMA</string>
+    </property>
+    <property name="autoExclusive">
+     <bool>true</bool>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 94 - 0
interfaces/takeFiles.py

@@ -0,0 +1,94 @@
+# Form implementation generated from reading ui file 'takeFiles.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.3
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again.  Do not edit this file unless you know what you are doing.
+from PyQt6.QtWidgets import QFileDialog
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+# Забей, этот код описывает интерфейс формы выбора папки откуда брать файлы, кроме последней функции EE НЕ УДАЛЯТЬ
+class Ui_takeFiles(object):
+    def setupUi(self, takeFiles):
+        takeFiles.setObjectName("takeFiles")
+        takeFiles.resize(745, 163)
+        takeFiles.setStyleSheet("background-color: rgb(51, 252, 255);")
+        self.label = QtWidgets.QLabel(takeFiles)
+        self.label.setGeometry(QtCore.QRect(20, 40, 91, 21))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(12)
+        font.setBold(True)
+        font.setWeight(75)
+        self.label.setFont(font)
+        self.label.setObjectName("label")
+        self.lineEdit = QtWidgets.QLineEdit(takeFiles)
+        self.lineEdit.setGeometry(QtCore.QRect(130, 30, 481, 41))
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        self.lineEdit.setFont(font)
+        self.lineEdit.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                    "color: rgb(0, 0, 0);")
+        self.lineEdit.setText("")
+        self.lineEdit.setObjectName("lineEdit")
+        self.pushButton = QtWidgets.QPushButton(takeFiles)
+        self.pushButton.setGeometry(QtCore.QRect(630, 35, 93, 31))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(12)
+        font.setBold(True)
+        font.setWeight(75)
+        self.pushButton.setFont(font)
+        self.pushButton.setStyleSheet("background-color: rgb(255, 255, 255);")
+        self.pushButton.setObjectName("pushButton")
+        self.label_2 = QtWidgets.QLabel(takeFiles)
+        self.label_2.setGeometry(QtCore.QRect(20, 110, 91, 21))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(12)
+        font.setBold(True)
+        font.setWeight(75)
+        self.label_2.setFont(font)
+        self.label_2.setObjectName("label_2")
+        self.lineEdit_2 = QtWidgets.QLineEdit(takeFiles)
+        self.lineEdit_2.setGeometry(QtCore.QRect(130, 100, 481, 41))
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        self.lineEdit_2.setFont(font)
+        self.lineEdit_2.setStyleSheet("background-color: rgb(255, 255, 255);\n"
+                                      "color: rgb(0, 0, 0);")
+        self.lineEdit_2.setText("")
+        self.lineEdit_2.setObjectName("lineEdit_2")
+        self.pushButton_2 = QtWidgets.QPushButton(takeFiles)
+        self.pushButton_2.setGeometry(QtCore.QRect(630, 105, 93, 31))
+        font = QtGui.QFont()
+        font.setFamily("Calibri")
+        font.setPointSize(12)
+        font.setBold(True)
+        font.setWeight(75)
+        self.pushButton_2.setFont(font)
+        self.pushButton_2.setStyleSheet("background-color: rgb(255, 255, 255);")
+        self.pushButton_2.setObjectName("pushButton_2")
+        self.retranslateUi(takeFiles)
+        QtCore.QMetaObject.connectSlotsByName(takeFiles)
+
+    def retranslateUi(self, takeFiles):
+        _translate = QtCore.QCoreApplication.translate
+        takeFiles.setWindowTitle(_translate("takeFiles", "Выберите папку с файлами PRISMA"))
+        self.label.setText(_translate("takeFiles", "1-кластер"))
+        self.lineEdit.setPlaceholderText(_translate("takeFiles", "Введите папку c нужными файлами"))
+        self.pushButton.setText(_translate("takeFiles", "Обзор"))
+        self.label_2.setText(_translate("takeFiles", "2-кластер"))
+        self.lineEdit_2.setPlaceholderText(_translate("takeFiles", "Введите папку c нужными файлами"))
+        self.pushButton_2.setText(_translate("takeFiles", "Обзор"))
+
+    def get_file_directory(self, filename):
+        dirlist = QFileDialog.getExistingDirectory()
+        if filename == 'path_prisma_1cl_files':
+            self.lineEdit.setText(dirlist)
+        else:
+            self.lineEdit_2.setText(dirlist)
+        with open(filename + '.txt', 'w') as f:
+            f.write(dirlist)
+        print(dirlist)

+ 162 - 0
interfaces/takeFiles.ui

@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>takeFiles</class>
+ <widget class="QWidget" name="takeFiles">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>745</width>
+    <height>163</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Выберите папку с файлами PRISMA</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true">background-color: rgb(51, 252, 255);</string>
+  </property>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>40</y>
+     <width>91</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <family>Calibri</family>
+     <pointsize>12</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="text">
+    <string>1-кластер</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEdit">
+   <property name="geometry">
+    <rect>
+     <x>130</x>
+     <y>30</y>
+     <width>481</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>10</pointsize>
+    </font>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+   <property name="placeholderText">
+    <string>Введите папку c нужными файлами</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="pushButton">
+   <property name="geometry">
+    <rect>
+     <x>630</x>
+     <y>35</y>
+     <width>93</width>
+     <height>31</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <family>Calibri</family>
+     <pointsize>12</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background-color: rgb(255, 255, 255);</string>
+   </property>
+   <property name="text">
+    <string>Обзор</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_2">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>110</y>
+     <width>91</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <family>Calibri</family>
+     <pointsize>12</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="text">
+    <string>2-кластер</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEdit_2">
+   <property name="geometry">
+    <rect>
+     <x>130</x>
+     <y>100</y>
+     <width>481</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <pointsize>10</pointsize>
+    </font>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background-color: rgb(255, 255, 255);
+color: rgb(0, 0, 0);</string>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+   <property name="placeholderText">
+    <string>Введите папку c нужными файлами</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="pushButton_2">
+   <property name="geometry">
+    <rect>
+     <x>630</x>
+     <y>105</y>
+     <width>93</width>
+     <height>31</height>
+    </rect>
+   </property>
+   <property name="font">
+    <font>
+     <family>Calibri</family>
+     <pointsize>12</pointsize>
+     <weight>75</weight>
+     <bold>true</bold>
+    </font>
+   </property>
+   <property name="styleSheet">
+    <string notr="true">background-color: rgb(255, 255, 255);</string>
+   </property>
+   <property name="text">
+    <string>Обзор</string>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 239 - 0
processing_for_report.py

@@ -0,0 +1,239 @@
+# import datetime
+
+from collections import defaultdict
+
+import pandas as pd
+
+
+class ProccessingPrismaCl:
+    def __init__(self, cluster, start_date, end_date, path_to_files):
+        self.cluster = cluster
+        if cluster == 1:
+            self.cluster_n = ''
+        else:
+            self.cluster_n = '2'
+        # self.a_crit = a_crit
+        # self.freq = freq
+        self.start_date = start_date
+        self.end_date = end_date
+        self.path_to_files = path_to_files
+
+        self.amp_n_cols = []
+        for i in range(1, 17):
+            self.amp_n_cols.append(f'amp{i}')
+            self.amp_n_cols.append(f'n{i}')
+
+    def reading_p_file(self, single_date):
+        """Метод, прочитывающий p-файлы, возвращающий датафрейм дня на выходе. Или возвращающий filenotfounderror, если
+        файла нет"""
+        try:
+            p_file = pd.read_csv(
+                f'{self.path_to_files}\\nv\\{self.cluster}p{single_date.date().month:02}' +
+                f'-{single_date.date().day:02}.{single_date.date().year - 2000:02}',
+                sep='\s[-]*\s*', header=None, skipinitialspace=True, engine='python')
+            p_file.dropna(axis=1, how='all', inplace=True)
+            return p_file
+        except FileNotFoundError as error:
+            print(f"File {self.path_to_files}\\nv\\{self.cluster}p{single_date.date().month:02}-" +
+                  f"{single_date.date().day:02}.{single_date.date().year - 2000:02} does not exist")
+            return error.strerror
+
+    def reading_n_file(self, single_date):
+        """Метод, прочитывающий n-файлы, возвращающий датафрейм дня на выходе. Или возвращающий filenotfounderror, если
+        файла нет"""
+        try:
+            n_file = pd.read_csv(
+                f'{self.path_to_files}\\{self.cluster_n}n_{single_date.date().month:02}' +
+                f'-{single_date.date().day:02}.{single_date.date().year - 2000:02}',
+                sep=' ', header=None, skipinitialspace=True, index_col=False,
+                names=['time', 'number', 'sum_n', 'tr'] + self.amp_n_cols)
+            n_file.dropna(axis=1, how='all', inplace=True)
+            return n_file
+        except FileNotFoundError as error:
+            print(
+                f"File {self.path_to_files}\\{self.cluster_n}n_{single_date.date().month:02}-" +
+                f"{single_date.date().day:02}.{single_date.date().year - 2000:02}', does not exist")
+            return error.strerror
+
+    def day_proccessing(self):
+        """Функция, в которую помещается полная дневная обработка"""
+        worktime_dict = defaultdict(list)
+        n_vs_zero_tr_dict = defaultdict(list)
+        event_counter_fr_4 = defaultdict(list)
+        breaks_dict = defaultdict(list)
+        count_rate_amp_5_fr_2 = defaultdict(list)
+        count_rate_amp_10_fr_1 = defaultdict(list)
+        amp_5_fr_2_frame = pd.DataFrame(columns=[f'amp{i}' for i in range(1, 17)])
+        amp_10_fr_1_frame = pd.DataFrame(columns=[f'amp{i}' for i in range(1, 17)])
+        for single_date in pd.date_range(self.start_date, self.end_date):
+            n_file = self.reading_n_file(single_date)
+            p_file = self.reading_p_file(single_date)
+
+            worktime_dict['Date'].append(single_date)
+            n_vs_zero_tr_dict['Date'].append(single_date)
+            count_rate_amp_5_fr_2['Date'].append(single_date)
+            count_rate_amp_10_fr_1['Date'].append(single_date)
+            event_counter_fr_4['Date'].append(single_date)
+            if type(p_file) != str:
+                corr_p_file = self.correcting_p_file(p_file)
+
+                worktime_dict['Worktime'].append(round(len(corr_p_file.index) * 5 / 60, 2))
+                break_time_dict = self.counting_break_time(corr_p_file)
+                if break_time_dict:
+                    breaks_dict['Date'].append(single_date)
+                    breaks_dict['StartMinutes'].extend(break_time_dict['StartMinutes'])
+                    breaks_dict['EndMinutes'].extend(break_time_dict['EndMinutes'])
+
+            else:
+                worktime_dict['Worktime'].append(0.00)
+            if type(n_file) != str:
+                neutron_to_zero_trigger = self.neutron_to_zero_trigger(n_file)
+                for i in range(16):
+                    n_vs_zero_tr_dict[f'n{i + 1}'].append(neutron_to_zero_trigger[i])
+                    count_rate_amp_5_fr_2[f'amp{i + 1}'].append(
+                        self.set_event_counter(n_file, a_crit=6, freq=2)['count_rate'][i + 1])
+                    count_rate_amp_10_fr_1[f'amp{i + 1}'].append(
+                        self.set_event_counter(n_file, a_crit=11, freq=1)['count_rate'][i + 1])
+
+                event_counter_fr_4['Events'].append(self.set_event_counter(n_file, a_crit=6, freq=4)['sum_events'])
+
+                set_event_counter_frame_1 = self.set_event_counter(n_file, a_crit=6, freq=2)['DataFrame']
+                set_event_counter_frame_2 = self.set_event_counter(n_file, a_crit=11, freq=1)['DataFrame']
+
+                amp_5_fr_2_frame = pd.concat(
+                    [amp_5_fr_2_frame, set_event_counter_frame_1],
+                    ignore_index=True)
+                amp_10_fr_1_frame = pd.concat(
+                    [amp_10_fr_1_frame, set_event_counter_frame_2],
+                    ignore_index=True)
+            else:
+                for i in range(16):
+                    n_vs_zero_tr_dict[f'n{i + 1}'].append(0.00)
+                    count_rate_amp_5_fr_2[f'amp{i + 1}'].append(0.00)
+                    count_rate_amp_10_fr_1[f'amp{i + 1}'].append(0.00)
+                event_counter_fr_4['Events'].append(0.00)
+
+        return worktime_dict, breaks_dict, n_vs_zero_tr_dict, event_counter_fr_4, amp_5_fr_2_frame, amp_10_fr_1_frame, count_rate_amp_5_fr_2, count_rate_amp_10_fr_1
+
+    # worktime_df = pd.DataFrame(worktime_dict, columns=['Date', 'Worktime'])
+    # worktime_df['Date'] = pd.to_datetime(worktime_df["Date"])
+    # # df.loc[16, 'WORKTIME'] = 24 # для января 2013-го, там хуйня с файлом, а именно разное количество колонок
+    # return worktime_df
+    # n_vs_zero_tr_df = pd.DataFrame(n_vs_zero_tr_dict, columns=['Date'] + [f'n{i}' for i in range(1, 17)])
+    # n_vs_zero_tr_df['Date'] = pd.to_datetime(n_vs_zero_tr_df["Date"])
+    # return n_vs_zero_tr_df
+
+    @staticmethod
+    def correcting_p_file(p_file):
+        """Метод, корректирующий старые файлы ПРИЗМА-32, возвращающий скорректированный датафрейм"""
+        p_file['time'] = p_file[0]
+        del p_file[0]
+        p_file = p_file.sort_values(by='time')
+        if len(p_file['time']) > len(p_file['time'].unique()):
+            """Данный костыль нужен для старых p-файлов ПРИЗМА-32(до 14-15 гг.), в которых индексы строк, 
+            по сути обозначающие 5 минут реального времени между ранами, могут повторяться. """
+            p_file.drop_duplicates(keep=False, inplace=True)
+            """После удаления полных дубликатов ищем повторяющиеся индексы. Сначала удаляем строки, 
+            состоящие полностью из нулей и точек (value = len(p_file.columns)), потом ищем множество 
+            дубликатов индексов и множество строк, почти полностью (value > 30) состоящих из нулей и точек. 
+            Берем пересечение этих двух множеств и удаляем находящиеся в пересечении строки"""
+            null_row = dict(p_file.isin([0, '.']).sum(axis=1))  # Проверяем на нули и точки
+            all_null_index = list(
+                {key: value for key, value in null_row.items() if value == len(p_file.columns)}.keys())
+            p_file.drop(index=all_null_index, inplace=True)
+
+            null_index = list(
+                {key: value for key, value in null_row.items() if value > len(p_file.columns) - 5}.keys())
+            same_index = dict(p_file['time'].duplicated(keep=False))
+            same_index_row = list({key: value for key, value in same_index.items() if value is True}.keys())
+            bad_index = list(set(null_index) & set(same_index_row))
+            p_file.drop(index=bad_index, inplace=True)
+            """Также может быть, что после фильтрации осталось больше строк, чем нужно, так как в старых 
+            p-файлах может быть больше индексов, чем минут в дне. Тогда оставляем только 288"""
+            if len(p_file.index) == 289:
+                p_file = p_file.head(288)
+        return p_file
+
+    @staticmethod
+    def counting_break_time(p_file):
+        """Метод, выявляющий в p-file 5-минутки, когда кластер не работал, возвращает начальное время остановки и
+        конечное время остановки"""
+        index = p_file['time'].tolist()
+        daily_breaks_dict = defaultdict(list)
+        if max(index) < 287:
+            start_minutes = max(index) * 5
+            end_minutes = 1435
+            daily_breaks_dict['StartMinutes'].append(start_minutes)
+            daily_breaks_dict['EndMinutes'].append(end_minutes)
+        if min(index) != 0:
+            start_minutes = 0
+            end_minutes = min(index) * 5
+            daily_breaks_dict['StartMinutes'].append(start_minutes)
+            daily_breaks_dict['EndMinutes'].append(end_minutes)
+        for i in range(1, len(index)):
+            if index[i] - index[i - 1] > 1:
+                start_minutes = index[i - 1] * 5
+                end_minutes = index[i] * 5
+                daily_breaks_dict['StartMinutes'].append(start_minutes)
+                daily_breaks_dict['EndMinutes'].append(end_minutes)
+        if daily_breaks_dict:
+            return daily_breaks_dict
+        else:
+            return None
+
+    @staticmethod
+    def neutron_to_zero_trigger(n_file):
+        """Метод, обрабатывающий данные n-файлов ПРИЗМА-32, дающий на выходе нормированное число в событии,
+        отобранных как нейтрон, при самозапуске"""
+        counter_zero_tr = len(n_file[n_file['tr'] == 0].index)
+        zero_tr_frame = n_file[n_file['tr'] == 0]
+        return [round(zero_tr_frame[f'n{i}'].sum() / counter_zero_tr, 3) for i in range(1, 17)]
+
+    # except ZeroDivisionError: Нужно дописать, если допустим нет нулевых триггеров
+
+    @staticmethod
+    def set_event_counter(n_file, a_crit, freq):
+        """Метод, обрабатывающий данные n-файлов ПРИЗМА-32, на вход подаются определенная амплитуда и количество
+        детекторов, на которых амплитуда превышает заданную, на выходе метод возвращает словарь с параметрами:
+        1) суммарное число событий на кластере, подходящих под заданные условия;
+        2) датафрейм с амплитудами детекторов для каждого события, подходящего под заданные условия;
+        3) количество превышений заданной амплитуды у детектора в событиях, подходящих под заданные условия; """
+        cluster_count_rate = {}
+        amp_frame = n_file[[f'amp{i}' for i in range(1, 17)]]
+        amp_frame['fr_sum'] = amp_frame.isin(range(a_crit, 520)).sum(axis=1, skipna=True)  # noqa
+        amp_frame = amp_frame[amp_frame['fr_sum'] >= freq].reset_index(drop=True)
+        for i in range(1, 17):
+            cluster_count_rate[i] = len(amp_frame[amp_frame[f'amp{i}'] >= a_crit])
+        return {'sum_events': len(amp_frame.index),
+                'DataFrame': amp_frame[[f'amp{i}' for i in range(1, 17)]],
+                'count_rate': cluster_count_rate}
+
+
+        # if max(index) < 287:
+        #     start_hour = math.floor(max(index) * 5 / 60)
+        #     start_minutes = max(index) * 5 - start_hour * 60
+        #     end_hour = 23
+        #     end_minutes = 55
+        #     daily_breaks_dict['StartHour'].append(start_hour)
+        #     daily_breaks_dict['StartMinutes'].append(start_minutes)
+        #     daily_breaks_dict['EndHour'].append(end_hour)
+        #     daily_breaks_dict['EndMinutes'].append(end_minutes)
+        # if min(index) != 0:
+        #     start_hour = 0
+        #     start_minutes = 0
+        #     end_hour = math.floor(min(index) * 5 / 60)
+        #     end_minutes = min(index) * 5 - end_hour * 60
+        #     daily_breaks_dict['StartHour'].append(start_hour)
+        #     daily_breaks_dict['StartMinutes'].append(start_minutes)
+        #     daily_breaks_dict['EndHour'].append(end_hour)
+        #     daily_breaks_dict['EndMinutes'].append(end_minutes)
+        # for i in range(1, len(index)):
+        #     if index[i] - index[i - 1] > 1:
+        #         start_hour = math.floor(index[i - 1] * 5 / 60)
+        #         start_minutes = index[i - 1] * 5 - start_hour * 60
+        #         end_hour = math.floor(index[i] * 5 / 60)
+        #         end_minutes = index[i] * 5 - end_hour * 60
+        #         daily_breaks_dict['StartHour'].append(start_hour)
+        #         daily_breaks_dict['StartMinutes'].append(start_minutes)
+        #         daily_breaks_dict['EndHour'].append(end_hour)
+                # daily_breaks_dict['EndMinutes'].append(end_minutes)

+ 94 - 0
report_prisma_32.py

@@ -0,0 +1,94 @@
+import os
+import datetime
+from PyQt6 import QtCore, QtWidgets
+
+
+from interfaces.clientui import Ui_MainWindow
+from interfaces.drctryChoice import Ui_drctryChoice
+from interfaces.takeFiles import Ui_takeFiles
+
+
+class ReportPrisma32(QtWidgets.QMainWindow, Ui_MainWindow):
+    def __init__(self):
+        super().__init__()
+        self.widget = QtWidgets.QWidget()
+        """Описание работы UI приложения, связь кнопок с методами, выставление даты."""
+        self.setupUi(self)
+        self.runPassport.pressed.connect(self.report_on_click)
+        self.openDirectory.pressed.connect(self.open_report_directory)
+        self.openFileDirectory.pressed.connect(self.open_file_directory)
+        self.dateEdit_2.setDate(QtCore.QDate(int(str(datetime.datetime.today()).split(' ')[0].split('-')[0]),
+                                             int(str(datetime.datetime.today()).split(' ')[0].split('-')[1]),
+                                             int(str(datetime.datetime.today()).split(' ')[0].split('-')[2])))
+        self.dateEdit_2.setDisplayFormat("dd.MM.yyyy")
+        self.dateEdit.setDate(QtCore.QDate(2020, 1, 1))
+        self.dateEdit.setDisplayFormat("dd.MM.yyyy")
+
+    def open_file_directory(self):
+        """Описание работы всплывающей формы проводника с выбором папки с файлами ПРИЗМА.
+         Производится запись в 2 файла -> 2 кластера, чтобы данные о папке сохранялись в отрыве от работы программы"""
+        ui_file_drctry = Ui_takeFiles()
+        ui_file_drctry.setupUi(self.widget)
+        """Чтение пути папки с файлами ПРИЗМА"""
+        try:
+            with open('path_prisma_1cl_files.txt', 'r') as f:
+                ui_file_drctry.lineEdit.setText(f.read())
+        except FileNotFoundError:
+            ui_file_drctry.lineEdit.setText("")
+        try:
+            with open('path_prisma_2cl_files.txt', 'r') as f2:
+                ui_file_drctry.lineEdit_2.setText(f2.read())
+        except FileNotFoundError:
+            ui_file_drctry.lineEdit_2.setText("")
+        """Запись в файл пути папки с файлами ПРИЗМА"""
+        ui_file_drctry.pushButton.clicked.connect(
+            lambda: Ui_takeFiles.get_file_directory(ui_file_drctry, 'path_prisma_1cl_files'))
+        ui_file_drctry.pushButton_2.clicked.connect(
+            lambda: Ui_takeFiles.get_file_directory(ui_file_drctry, 'path_prisma_2cl_files'))
+        self.widget.show()
+
+    def open_report_directory(self):
+        """Описание работы всплывающей формы проводника с выбором папки сохранения справки о работе установки,
+         картинок, файлов и т.д."""
+        ui_report_drctry = Ui_drctryChoice()
+        ui_report_drctry.setupUi(self.widget)
+        ui_report_drctry.pushButton.clicked.connect(lambda: Ui_drctryChoice.get_report_directory(ui_report_drctry))
+        try:
+            with open('path_prisma_report.txt', 'r') as f:
+                ui_report_drctry.lineEdit.setText(f.read())
+        except FileNotFoundError:
+            ui_report_drctry.lineEdit.setText("")
+        self.widget.show()
+
+    def report_on_click(self):
+        """Метод, описывающий получение паспорта с помощью данных UI"""
+        start_date = self.dateEdit.dateTime()
+        end_date = self.dateEdit_2.dateTime()
+        print(f'start_date - {start_date}, \n end_date - {end_date}')
+        with open('path_prisma_report.txt', 'r') as f:
+            report_path = f.read()
+        picture_path = report_path + '/Pics'
+        with open('path_prisma_1cl_files.txt', 'r') as f:
+            file1cl = f.read()
+        with open('path_prisma_2cl_files.txt', 'r') as f:
+            file2cl = f.read()
+        if ~os.path.exists(picture_path):
+            try:
+                os.mkdir(picture_path)
+            except OSError:
+                print(f"Создать директорию {picture_path} не удалось")
+            else:
+                print(f"Успешно создана директория {picture_path}")
+
+        # проверяем нормальные даты или нет, если да, то графики и word файл строятся
+        try:
+            pass
+        except ZeroDivisionError:
+            pass
+
+
+# запуск основного окна
+app = QtWidgets.QApplication([])
+window = ReportPrisma32()
+window.show()
+app.exec()

+ 307 - 0
testing.py

@@ -0,0 +1,307 @@
+import datetime
+import time
+import warnings
+from matplotlib import pyplot as plt
+import pandas as pd
+from docx import Document
+from docx.enum.text import WD_BREAK
+
+from drawing_graphs import GraphsDrawing
+from processing_for_report import ProccessingPrismaCl
+from word_addition import *
+
+t1 = time.time()
+
+warnings.filterwarnings(action='ignore')
+
+# Следующее должно быть передающимися переменными
+start_date = datetime.date(2021, 1, 1)
+end_date = datetime.date(2021, 1, 31)
+days_amount = len(pd.date_range(start_date, end_date))
+
+process_1 = ProccessingPrismaCl(1, start_date=start_date, end_date=end_date,
+                                path_to_files='D:\\PRISMA20\\P1')
+process_2 = ProccessingPrismaCl(2, start_date=start_date, end_date=end_date,
+                                path_to_files='D:\\PRISMA20\\P2')
+graphs = GraphsDrawing(start_date=start_date, end_date=end_date,
+                       path_to_pic='C:\\Users\\pad_z\\OneDrive\\Рабочий стол\\PrismaPassport')
+
+worktime_1, breaks_1, n_vs_zero_tr_1, event_counter_fr_4_1, amp_5_fr_2_frame_1, amp_10_fr_1_frame_1, count_rate_amp_5_fr_2_1, count_rate_amp_10_fr_1_1 = process_1.day_proccessing()
+worktime_frame_1 = pd.DataFrame(worktime_1)
+n_vs_zero_tr_frame_1 = pd.DataFrame(n_vs_zero_tr_1)
+breaks_frame_1 = pd.DataFrame(breaks_1)
+event_counter_fr_4_1 = pd.DataFrame(event_counter_fr_4_1)
+count_rate_amp_5_fr_2_1 = pd.DataFrame(count_rate_amp_5_fr_2_1)
+count_rate_amp_10_fr_1_1 = pd.DataFrame(count_rate_amp_10_fr_1_1)
+
+worktime_2, breaks_2, n_vs_zero_tr_2, event_counter_fr_4_2, amp_5_fr_2_frame_2, amp_10_fr_1_frame_2, count_rate_amp_5_fr_2_2, count_rate_amp_10_fr_1_2 = process_2.day_proccessing()
+worktime_frame_2 = pd.DataFrame(worktime_2)
+n_vs_zero_tr_frame_2 = pd.DataFrame(n_vs_zero_tr_2)
+breaks_frame_2 = pd.DataFrame(breaks_2)
+event_counter_fr_4_2 = pd.DataFrame(event_counter_fr_4_2)
+count_rate_amp_5_fr_2_2 = pd.DataFrame(count_rate_amp_5_fr_2_2)
+count_rate_amp_10_fr_1_2 = pd.DataFrame(count_rate_amp_10_fr_1_2)
+
+brake_both_cl_time = 0
+for i in range(len(breaks_frame_1.index)):
+    for j in range(len(breaks_frame_2.index)):
+        if breaks_frame_1['Date'][i] == breaks_frame_2['Date'][j]:
+            if breaks_frame_1['StartMinutes'][i] <= breaks_frame_2['StartMinutes'][j] < breaks_frame_1['EndMinutes'][i]:
+                brake_both_cl_time += min(breaks_frame_2['EndMinutes'][j], breaks_frame_1['EndMinutes'][i]) - max(
+                    breaks_frame_2['StartMinutes'][j], breaks_frame_1['StartMinutes'][i])
+            elif breaks_frame_2['StartMinutes'][j] <= breaks_frame_1['StartMinutes'][i] < breaks_frame_2['EndMinutes'][
+                j]:
+                brake_both_cl_time += min(breaks_frame_2['EndMinutes'][j], breaks_frame_1['EndMinutes'][i]) - max(
+                    breaks_frame_2['StartMinutes'][j], breaks_frame_1['StartMinutes'][i])
+
+real_worktime = worktime_frame_2['Worktime'].sum() - 24 * days_amount + worktime_frame_1[
+    'Worktime'].sum() + brake_both_cl_time / 60
+print(brake_both_cl_time)
+
+graphs.change_design()
+
+worktime_pic_path_1 = graphs.worktime_graph(cluster=1, worktime_frame=worktime_frame_1)
+worktime_pic_path_2 = graphs.worktime_graph(cluster=2, worktime_frame=worktime_frame_2)
+
+n_vs_zero_tr_pic_path_1 = graphs.neutron_to_0_tr_graph(cluster=1, neutron_num_0_tr_frame=n_vs_zero_tr_frame_1)
+n_vs_zero_tr_pic_path_2 = graphs.neutron_to_0_tr_graph(cluster=2, neutron_num_0_tr_frame=n_vs_zero_tr_frame_2)
+
+event_counter_fr_4_pic_path = graphs.amp_5_fr_4_graph(amp_5_fr_4_frame=event_counter_fr_4_1,
+                                                      amp_5_fr_4_frame_2=event_counter_fr_4_2,
+                                                      worktime_frame=worktime_frame_1,
+                                                      worktime_frame_2=worktime_frame_2)
+
+amp_distribution_pic_path_1 = graphs.amp_distribution_graph(cluster=1, amp_distribution_frame=amp_5_fr_2_frame_1,
+                                                            a_crit=6, freq=2)
+amp_distribution_pic_path_2 = graphs.amp_distribution_graph(cluster=2, amp_distribution_frame=amp_5_fr_2_frame_2,
+                                                            a_crit=6, freq=2)
+
+count_rate_amp_5_fr_2_pic_path_1 = graphs.count_rate_graph(cluster=1, count_rate_frame=count_rate_amp_5_fr_2_1,
+                                                  working_frame=worktime_frame_1,
+                                                  a_crit=5, freq=2)
+count_rate_amp_5_fr_2_pic_path_2 = graphs.count_rate_graph(cluster=2, count_rate_frame=count_rate_amp_5_fr_2_2,
+                                                  working_frame=worktime_frame_2,
+                                                  a_crit=5, freq=2)
+
+count_rate_amp_10_fr_1_pic_path_1 = graphs.count_rate_graph(cluster=1, count_rate_frame=count_rate_amp_10_fr_1_1,
+                                                   working_frame=worktime_frame_1,
+                                                   a_crit=10, freq=1)
+count_rate_amp_10_fr_1_pic_path_2 = graphs.count_rate_graph(cluster=2, count_rate_frame=count_rate_amp_10_fr_1_2,
+                                                   working_frame=worktime_frame_2,
+                                                   a_crit=10, freq=1)
+
+doc = Document()
+section_choice(doc)
+add_new_styles(doc)
+
+head = doc.add_paragraph(
+    f'Справка о работе установки ПРИЗМА-32 в период с {start_date} по {end_date} ', style='Head-style')
+head.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+table_title = doc.add_paragraph('Таблица 1: Время работы установки ПРИЗМА-32.', style='PItalic')
+table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+worktime_table = doc.add_table(4, 4, doc.styles['Table Grid'])
+worktime_table.cell(0, 0).text = '№ кластера'
+worktime_table.cell(0, 1).text = 'Экспозиции, ч.'
+worktime_table.cell(0, 2).text = 'Календарное время, ч.'
+worktime_table.cell(0, 3).text = 'Экспозиция, %'
+worktime_table.cell(1, 0).text = '1'
+worktime_table.cell(1, 1).text = str(round(worktime_frame_1['Worktime'].sum(), 2))
+worktime_table.cell(1, 2).text = str(24 * days_amount)
+worktime_table.cell(1, 3).text = str(
+    round(worktime_frame_1['Worktime'].sum() / (24 * days_amount) * 100, 3)) + '%'
+
+worktime_table.cell(2, 0).text = '2'
+worktime_table.cell(2, 1).text = str(round(worktime_frame_2['Worktime'].sum(), 2))
+worktime_table.cell(2, 2).text = str(24 * days_amount)
+worktime_table.cell(2, 3).text = str(
+    round(worktime_frame_2['Worktime'].sum() / (24 * days_amount) * 100, 3)) + '%'
+
+worktime_table.cell(3, 0).text = '1&2'
+worktime_table.cell(3, 1).text = str(round(real_worktime, 2))
+worktime_table.cell(3, 2).text = str(24 * days_amount)
+worktime_table.cell(3, 3).text = str(round(real_worktime / (24 * days_amount) * 100, 3)) + '%'
+
+make_table_bold(worktime_table, cols=4, rows=4)
+
+doc.add_paragraph()
+
+# В РАЗРАБОТКЕ
+fail_str_begin_1, fail_str_end_1, lost_minutes_1, break_1 = time_breaks_counter(brake_frame=breaks_frame_1)
+fail_str_begin_2, fail_str_end_2, lost_minutes_2, break_2 = time_breaks_counter(brake_frame=breaks_frame_2)
+brake_table_title = doc.add_paragraph('Таблица 2: Сводная таблица остановок и работ установки ПРИЗМА-32.', style='PItalic')
+brake_table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+brake_table = doc.add_table(len(fail_str_begin_1) + len(fail_str_begin_2) + 2, 5, doc.styles['Table Grid'])
+brake_table.alignment = WD_TABLE_ALIGNMENT.CENTER
+brake_table.cell(0, 0).text = '№ кластера'
+brake_table.cell(0, 0).merge(brake_table.cell(1, 0))
+brake_table.cell(0, 1).text = 'Время простоя'
+brake_table.cell(1, 1).text = 'c'
+brake_table.cell(1, 2).text = 'по'
+brake_table.cell(0, 1).merge(brake_table.cell(0, 2))
+brake_table.cell(0, 3).text = 'Кол-во потерянных минут (период)'
+brake_table.cell(0, 3).merge(brake_table.cell(1, 3))
+brake_table.cell(0, 4).text = 'Примечание'
+brake_table.cell(0, 4).merge(brake_table.cell(1, 4))
+
+for i in range(2, len(fail_str_begin_1) + 2):
+    brake_table.cell(i, 0).text = '1'
+    brake_table.cell(i, 1).text = str(fail_str_begin_1[i - 2])
+    brake_table.cell(i, 2).text = str(fail_str_end_1[i - 2])
+    brake_table.cell(i, 3).text = str(lost_minutes_1[i - 2])
+    brake_table.cell(i, 4).text = ' '
+
+for i in range(2 + len(fail_str_begin_1), len(fail_str_begin_2) + 2 + len(fail_str_begin_1)):
+    brake_table.cell(i, 0).text = '2'
+    brake_table.cell(i, 1).text = str(fail_str_begin_2[i - 2 - len(fail_str_begin_1)])
+    brake_table.cell(i, 2).text = str(fail_str_end_2[i - 2 - len(fail_str_begin_1)])
+    brake_table.cell(i, 3).text = str(lost_minutes_2[i - 2 - len(fail_str_begin_1)])
+    brake_table.cell(i, 4).text = ' '
+
+make_table_bold(brake_table, cols=5, rows=len(fail_str_begin_1) + len(fail_str_begin_2)+2)
+doc.add_paragraph()
+
+table_title = doc.add_paragraph(
+    'Таблица 3: Сводная таблица темпов счета событий и сигналов, отобранных как нейтрон кластеров установки ПРИЗМА-32.',
+    style='PItalic')
+table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+neut_stat_info_1, neut_stat_info_2 = statistical_table(n_vs_zero_tr_frame_1, n_vs_zero_tr_frame_2, dimension='100/соб')
+
+neutron_table = doc.add_table(3, 3, doc.styles['Table Grid'])
+neutron_table.cell(0, 0).text = 'Счет/кластер'
+neutron_table.cell(0, 1).text = 'Кл1'
+neutron_table.cell(0, 2).text = 'Кл2'
+neutron_table.cell(1, 0).text = 'События (Fr ≥ 4, A ≥ 5), N соб./ч.'
+neutron_table.cell(1, 1).text = str(round((event_counter_fr_4_1['Events'] / worktime_frame_1['Worktime']).mean(), 2))
+neutron_table.cell(1, 2).text = str(round((event_counter_fr_4_2['Events'] / worktime_frame_2['Worktime']).mean(), 2))
+neutron_table.cell(2, 0).text = 'Нейтроны, (Nn)/соб.'
+neutron_table.cell(2, 1).text = str(round(neut_stat_info_1.iloc[0].sum(), 2))
+neutron_table.cell(2, 2).text = str(round(neut_stat_info_2.iloc[0].sum(), 2))
+
+make_table_bold(neutron_table, cols=3, rows=3)
+change_cell_size(neutron_table, column_num=3, size_arr=[2.5, 0.5, 0.5])
+
+doc.add_paragraph()
+
+notes = doc.add_paragraph('')
+notes.add_run('Примечание:').bold = True
+notes_description = doc.add_paragraph(
+    '        В таблице 4 представлена сводная информация о неисправностях в работе детекторов кластера.')
+table_title = doc.add_paragraph('Таблица 4: Неисправности.', style='PItalic')
+table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+notes_table = doc.add_table(3, 5, doc.styles['Table Grid'])
+notes_table.cell(0, 0).text = '№'
+notes_table.cell(0, 1).text = 'Кластер'
+notes_table.cell(0, 2).text = '№ Детектора'
+notes_table.cell(0, 3).text = 'Период'
+notes_table.cell(0, 4).text = 'Примечание'
+notes_table.cell(1, 0).text = '1'
+notes_table.cell(2, 0).text = '2'
+
+change_cell_size(notes_table, column_num=5, size_arr=[0.3, 0.8, 1.2, 1, 4.2])
+
+run = doc.add_paragraph().add_run()
+run.add_break(WD_BREAK.PAGE)
+
+graphic_header = doc.add_paragraph('Продолжительность работы кластеров установки ПРИЗМА-32.', style='Head-graphic')
+graphic_header.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+adding_graphic(doc, title='Рис. 1 - Продолжительность работы 1-го кластера в сутки', width=6,
+               picture_path=worktime_pic_path_1)
+adding_graphic(doc, title='Рис. 2 - Продолжительность работы 2-го кластера в сутки', width=6,
+               picture_path=worktime_pic_path_2)
+
+run = doc.add_paragraph().add_run()
+run.add_break(WD_BREAK.PAGE)
+
+graphic_header = doc.add_paragraph('Скорость счета событий установки ПРИЗМА-32.', style='Head-graphic')
+graphic_header.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+adding_graphic(doc, title='Рис. 3 - Скорость счета событий Fr ≥ 4, A ≥ 5', width=7,
+               picture_path=event_counter_fr_4_pic_path)
+
+run = doc.add_paragraph().add_run()
+run.add_break(WD_BREAK.PAGE)
+
+adding_graphic(doc, title='Рис. 4 - Число импульсов в событии, отобранных как нейтрон, при самозапуске кластер 1',
+               width=6, picture_path=n_vs_zero_tr_pic_path_1)
+adding_graphic(doc, title='Рис. 5 - Число импульсов в событии, отобранных как нейтрон, при самозапуске кластер 2',
+               width=6, picture_path=n_vs_zero_tr_pic_path_2)
+
+table_title = doc.add_paragraph(
+    'Таблица 5: Среднее число нейтронов (Nn) для детекторов установки ПРИЗМА-32 за месяц работы, нормированное на количество событий (Ns).(при самозапуске), ',
+    style='PItalic')
+table_title.add_run('(100/соб)').bold = True
+table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+
+neutron_stat_table = doc.add_table(neut_stat_info_1.shape[0] + neut_stat_info_2.shape[0] + 2,
+                                   neut_stat_info_1.shape[1] + 2,
+                                   doc.styles['Table Grid'])
+
+draw_stat_table(neutron_stat_table, neut_stat_info_1, neut_stat_info_2)
+make_stat_table_bold(neutron_stat_table, cols=18, rows=6)
+
+run = doc.add_paragraph().add_run()
+run.add_break(WD_BREAK.PAGE)
+
+# Переделать, чтобы амплитуда и кратность были переменными.
+adding_graphic(doc, title='Рис. 6 - Скорость счета  детекторов в 1-м кластере Fr ≥ 2, A > 5',
+               width=6, picture_path=count_rate_amp_5_fr_2_pic_path_1)
+adding_graphic(doc, title='Рис. 7 - Скорость счета  детекторов в 2-м кластере Fr ≥ 2, A > 5',
+               width=6, picture_path=count_rate_amp_5_fr_2_pic_path_2)
+
+table_title = doc.add_paragraph(
+    'Таблица 6: Среднемесячные число срабатываний детекторов установки ПРИЗМА-32, cоб./час.',
+    style='PItalic')
+table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+# print(f'{count_rate_amp_5_fr_2_1=}')
+# print(f'{count_rate_amp_5_fr_2_2=}')
+count_rate_stat_info_1, count_rate_stat_info_2 = statistical_table(count_rate_amp_5_fr_2_1, count_rate_amp_5_fr_2_2,
+                                                                   dimension='cоб./ч.')
+
+count_stat_table = doc.add_table(count_rate_stat_info_1.shape[0] + count_rate_stat_info_2.shape[0] + 2,
+                                 count_rate_stat_info_1.shape[1] + 2, doc.styles['Table Grid'])
+
+draw_stat_table(count_stat_table, count_rate_stat_info_1, count_rate_stat_info_2)
+make_stat_table_bold(count_stat_table, cols=18, rows=6)
+
+run = doc.add_paragraph().add_run()
+run.add_break(WD_BREAK.PAGE)
+
+adding_graphic(doc, title='Рис. 8 - Скорость счета  детекторов в 1-м кластере Fr ≥ 1, A > 10',
+               width=6, picture_path=count_rate_amp_10_fr_1_pic_path_1)
+adding_graphic(doc, title='Рис. 9 - Скорость счета  детекторов в 2-м кластере Fr ≥ 1, A > 10',
+               width=6, picture_path=count_rate_amp_10_fr_1_pic_path_2)
+
+table_title = doc.add_paragraph(
+    'Таблица 7: Среднемесячные число срабатываний детекторов установки ПРИЗМА-32, cоб./час.',
+    style='PItalic')
+table_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+count_rate_stat_info_1, count_rate_stat_info_2 = statistical_table(count_rate_amp_10_fr_1_1, count_rate_amp_10_fr_1_2,
+                                                                   dimension='cоб./ч.')
+count_stat_table_2 = doc.add_table(count_rate_stat_info_1.shape[0] + count_rate_stat_info_2.shape[0] + 2,
+                                   count_rate_stat_info_1.shape[1] + 2, doc.styles['Table Grid'])
+
+draw_stat_table(count_stat_table_2, count_rate_stat_info_1, count_rate_stat_info_2)
+make_stat_table_bold(count_stat_table_2, cols=18, rows=6)
+
+run = doc.add_paragraph().add_run()
+run.add_break(WD_BREAK.PAGE)
+
+graphic_header = doc.add_paragraph('На рисунке 8, 9 представлено число сигналов с А>5 кодов АЦП в час для 16 детекторов.',
+                         style='Head-graphic')
+graphic_header.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+adding_graphic(doc, title='Рис. 10 - Амплитудное распределение сигналов от детекторов, кластер 1 (Fr ≥ 2 и А > 5)',
+               width=6, picture_path=amp_distribution_pic_path_1)
+adding_graphic(doc, title='Рис. 11 - Амплитудное распределение сигналов от детекторов, кластер 2 (Fr ≥ 2 и А > 5)',
+               width=6, picture_path=amp_distribution_pic_path_2)
+add_page_number(doc.sections[0].footer.paragraphs[0])
+
+doc.save(f'C:\\Users\\pad_z\\OneDrive\\Рабочий стол\\PrismaPassport\\2021\\{start_date}-{end_date}.docx')
+
+plt.close('all')
+print(time.time() - t1)

+ 195 - 0
word_addition.py

@@ -0,0 +1,195 @@
+from docx.enum.style import WD_STYLE_TYPE
+from docx.enum.table import WD_TABLE_ALIGNMENT
+from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
+from docx.oxml import OxmlElement, ns
+from docx.shared import Cm
+from docx.shared import Inches
+from docx.shared import Pt
+
+
+def create_element(name):
+    return OxmlElement(name)
+
+
+def create_attribute(element, name, value):
+    element.set(ns.qn(name), value)
+
+
+def add_page_number(paragraph):
+    """Метод, добавляющий номера страниц в word."""
+    # выравниваем параграф по центру
+    paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+    # запускаем динамическое обновление параграфа
+    page_num_run = paragraph.add_run()
+    # обозначаем начало позиции вывода
+    fld_char1 = create_element('w:fldChar')
+    create_attribute(fld_char1, 'w:fldCharType', 'begin')
+    # задаем вывод текущего значения страницы PAGE (всего страниц NUM PAGES)
+    instr_text = create_element('w:instrText')
+    create_attribute(instr_text, 'xml:space', 'preserve')
+    instr_text.text = "PAGE"
+    # обозначаем конец позиции вывода
+    fld_char2 = create_element('w:fldChar')
+    create_attribute(fld_char2, 'w:fldCharType', 'end')
+    # добавляем все в наш параграф (который формируется динамически)
+    page_num_run._r.append(fld_char1)
+    page_num_run._r.append(instr_text)
+    page_num_run._r.append(fld_char2)
+
+
+def add_new_styles(document):
+    """Метод, добавляющий стили текста."""
+    styles = document.styles
+    styles.add_style('PItalic', WD_STYLE_TYPE.PARAGRAPH)
+    style = document.styles['PItalic']
+    font = style.font
+    font.name = 'Times New Roman'
+    font.size = Pt(11)
+    font.italic = True
+
+    styles.add_style('Head-style', WD_STYLE_TYPE.PARAGRAPH)
+    head_style = document.styles['Head-style']
+    font = head_style.font
+    font.name = 'Times New Roman'
+    font.size = Pt(14)
+    font.bold = True
+
+    styles.add_style('Head-graphic', WD_STYLE_TYPE.PARAGRAPH)
+    head_graphic = document.styles['Head-graphic']
+    font = head_graphic.font
+    font.name = 'Times New Roman'
+    font.size = Pt(13)
+    font.bold = True
+    font.italic = True
+
+
+def section_choice(document):
+    """Метод, добавляющий отступы в документе word."""
+    sections = document.sections
+    for section in sections:
+        section.top_margin = Cm(1.5)
+        section.bottom_margin = Cm(1.5)
+        section.left_margin = Cm(1.5)
+        section.right_margin = Cm(1.5)
+
+
+def make_table_bold(table, cols, rows):
+    """Метод, изменяющий вес шрифтов в таблицах и выравнивающий таблицу по центру."""
+    for row in range(1):
+        for col in range(cols):
+            # получаем ячейку таблицы
+            cell = table.cell(row, col)
+            # записываем в ячейку данные
+            run = cell.paragraphs[0].runs[0]
+            run.font.bold = True
+
+    for row in range(1, rows):
+        for col in range(1):
+            # получаем ячейку таблицы
+            cell = table.cell(row, col)
+            # записываем в ячейку данные
+            run = cell.paragraphs[0].runs[0]
+            run.font.bold = True
+    table.alignment = WD_TABLE_ALIGNMENT.CENTER
+
+
+def make_stat_table_bold(stat_table, cols, rows):
+    """Метод, изменяющий вес шрифтов в статистических таблицах и выравнивающий таблицу по центру."""
+    for row in stat_table.rows:
+        for cell in row.cells:
+            paragraphs = cell.paragraphs
+            for paragraph in paragraphs:
+                for run in paragraph.runs:
+                    font = run.font
+                    font.size = Pt(8)
+
+    for row in range(2):
+        for col in range(cols):
+            # получаем ячейку таблицы
+            cell = stat_table.cell(row, col)
+            # записываем в ячейку данные
+            run = cell.paragraphs[0].runs[0]
+            run.font.bold = True
+            run.font.size = Pt(9)
+
+    for row in range(2, rows):
+        for col in range(2):
+            # получаем ячейку таблицы
+            cell = stat_table.cell(row, col)
+            # записываем в ячейку данные
+            run = cell.paragraphs[0].runs[0]
+            run.font.bold = True
+            run.font.size = Pt(8.5)
+    stat_table.alignment = WD_TABLE_ALIGNMENT.CENTER
+
+
+def draw_stat_table(stat_table, stat_info_1, stat_info_2):
+    """Метод для построения статистических таблиц с информацией о средних значениях и о стандартных отклонениях
+    параметра."""
+    stat_table.cell(0, 0).text = "№"
+    stat_table.cell(0, 1).text = "Стат-ка"
+    stat_table.cell(0, 2).text = "№ детектора"
+    stat_table.cell(0, 2).merge(stat_table.cell(0, 17))
+    stat_table.cell(0, 0).merge(stat_table.cell(1, 0))
+    stat_table.cell(0, 1).merge(stat_table.cell(1, 1))
+    for j in range(stat_info_1.shape[-1]):
+        stat_table.cell(1, j + 2).text = stat_info_1.columns[j]
+    stat_table.cell(2, 0).text = '1'
+    stat_table.cell(2, 0).merge(stat_table.cell(3, 0))
+    stat_table.cell(4, 0).text = '2'
+    stat_table.cell(4, 0).merge(stat_table.cell(5, 0))
+    for i in range(stat_info_1.shape[0]):
+        stat_table.cell(i + 2, 1).text = stat_info_1.index[i]
+        for j in range(stat_info_1.shape[-1]):
+            stat_table.cell(i + 2, j + 2).text = str(round(stat_info_1.values[i, j], 2))
+
+    for i in range(stat_info_2.shape[0]):
+        stat_table.cell(i + 2 + stat_info_1.shape[0], 1).text = stat_info_2.index[i]
+        for j in range(stat_info_2.shape[-1]):
+            stat_table.cell(i + 2 + stat_info_1.shape[0], j + 2).text = str(round(stat_info_2.values[i, j], 2))
+
+
+def change_cell_size(table, column_num, size_arr):
+    """Метод, меняющий размер клеток в таблице."""
+    for i in range(column_num):
+        for cell in table.columns[i].cells:
+            cell.width = Inches(size_arr[i])
+
+
+def adding_graphic(document, title, picture_path, width):
+    """Метод, добавляющий в word график."""
+    document.add_picture(picture_path, width=Inches(width))
+    last_paragraph = document.paragraphs[-1]
+    last_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+    picture_title = document.add_paragraph(title, style='PItalic')
+    picture_title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
+
+
+def statistical_table(frame, frame_2, dimension):
+    """Метод, на вход которого, идут два датафрейма 1-го и 2-го кластера ПРИЗМА-32 с одинаковым параметром. Метод
+    возвращает датафрейм с информацией о средних значениях и о стандартных отклонениях заданного параметра"""
+    stat_info = frame.describe().tail(7).head(2)
+    stat_info_2 = frame_2.describe().tail(7).head(2)
+    stat_info.index = [f'mean({dimension})', f'std({dimension})']
+    stat_info_2.index = [f'mean({dimension})', f'std({dimension})']
+    return stat_info, stat_info_2
+
+
+def time_breaks_counter(brake_frame):
+    breaks = len(brake_frame.index)
+    fail_str_begin = []
+    fail_str_end = []
+    lost_minutes = []
+    for i in range(len(brake_frame.index)):
+        fail_str_begin.append(
+            f" {brake_frame['Date'][i].date()}  {brake_frame['StartMinutes'][i] // 60:02}:{brake_frame['StartMinutes'][i] % 60:02}")
+        fail_str_end.append(
+            f" {brake_frame['Date'][i].date()}  {brake_frame['EndMinutes'][i] // 60:02}:{brake_frame['EndMinutes'][i] % 60:02}")
+        lost_minutes.append(brake_frame['EndMinutes'][i] - brake_frame['StartMinutes'][i])
+
+    for i in range(1, len(brake_frame.index)):
+        if brake_frame['StartMinutes'][i] == 0 and brake_frame['EndMinutes'][i - 1] == 1435 and \
+                (brake_frame['Date'][i] - brake_frame['Date'][i-1]).days == 1:
+            breaks -= 1
+
+    return fail_str_begin, fail_str_end, lost_minutes, breaks