"""The CNApy OptMDFpathway dialog"""
import cobra
import cobra.util.solver
import copy
import pickle
from contextlib import redirect_stdout, redirect_stderr
from numpy import exp
from qtpy.QtCore import Qt, QThread, Signal, Slot
from qtpy.QtWidgets import (
    QCheckBox,
    QDialog,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QMessageBox,
    QPushButton,
    QVBoxLayout,
    QGroupBox,
)

from cnapy.appdata import AppData
from cnapy.gui_elements.central_widget import CentralWidget
from straindesign import avail_solvers
from straindesign.names import *
from cnapy.sd_class_interface import (
    LinearProgram,
    Result,
    Solver,
    Status,
    ObjectiveDirection,
)
from cnapy.sd_ci_optmdfpathway import create_optmdfpathway_milp, STANDARD_R, STANDARD_T
from typing import Dict
from cobra.util.solver import interface_to_str
from cnapy.core_gui import model_optimization_with_exceptions
import re
from cnapy.gui_elements.solver_buttons import get_solver_buttons
from straindesign.names import CPLEX, GLPK, GUROBI, SCIP
from enum import Enum

class ThermodynamicAnalysisTypes(Enum):
    OPTMDFPATHWAY = 1
    THERMODYNAMIC_FBA = 2
    BOTTLENECK_ANALYSIS = 3

def make_model_irreversible(
    cobra_model: cobra.Model, forward_id: str = "_FWD", reverse_id: str = "_REV"
) -> cobra.Model:
    # Create
    cobra_reaction_ids = [x.id for x in cobra_model.reactions]
    for cobra_reaction_id in cobra_reaction_ids:
        cobra_reaction: cobra.Reaction = cobra_model.reactions.get_by_id(
            cobra_reaction_id
        )

        create_forward = False
        create_reverse = False
        if cobra_reaction.lower_bound < 0:
            create_reverse = True
        elif (cobra_reaction.lower_bound == 0) and (cobra_reaction.upper_bound == 0):
            create_reverse = True
            create_forward = True
        elif cobra_reaction.id.startswith("EX_"):
            create_reverse = True
            create_forward = True
        else:
            continue

        if cobra_reaction.upper_bound > 0:
            create_forward = True

        if create_forward:
            forward_reaction_id = cobra_reaction.id + forward_id
            forward_reaction = copy.deepcopy(cobra_reaction)
            forward_reaction.id = forward_reaction_id
            if cobra_reaction.lower_bound >= 0:
                forward_reaction.lower_bound = cobra_reaction.lower_bound
            else:
                forward_reaction.lower_bound = 0
            cobra_model.add_reactions([forward_reaction])

        if create_reverse:
            reverse_reaction_id = cobra_reaction.id + reverse_id
            reverse_reaction = copy.deepcopy(cobra_reaction)
            reverse_reaction.id = reverse_reaction_id

            metabolites_to_add = {}
            for metabolite in reverse_reaction.metabolites:
                metabolites_to_add[metabolite] = (
                    reverse_reaction.metabolites[metabolite] * -2
                )
            reverse_reaction.add_metabolites(metabolites_to_add)

            if cobra_reaction.upper_bound < 0:
                reverse_reaction.lower_bound = -cobra_reaction.upper_bound
            else:
                reverse_reaction.lower_bound = 0
            reverse_reaction.upper_bound = -cobra_reaction.lower_bound

            cobra_model.add_reactions([reverse_reaction])

        if create_forward or create_reverse:
            cobra_model.remove_reactions([cobra_reaction])

    return cobra_model

class ComputationViewer(QDialog):
    """A dialog that shows the status of an ongoing computation"""

    def __init__(self):
        super().__init__()

        self.setWindowTitle("Computation is running...")

        self.setMinimumWidth(620)
        self.layout = QVBoxLayout()

        buttons_layout = QHBoxLayout()
        cancel = QPushButton("Cancel")
        cancel.setMaximumWidth(120)
        cancel.clicked.connect(self.cancel)
        buttons_layout.addWidget(cancel)
        self.layout.addItem(buttons_layout)

        self.setLayout(self.layout)
        self.show()

    @Slot()
    def close_window(self):
        self.deleteLater()
        self.accept()

    @Slot()
    def cancel(self):
        self.cancel_computation.emit()
        self.deleteLater()
        self.reject()

    cancel_computation = Signal()

class ComputationThread(QThread):
    def __init__(self, linear_program: LinearProgram):
        # super().__init__()
        QThread.__init__(self)
        self.linear_program: LinearProgram = linear_program

    def run(self):
        try:
            with redirect_stdout(self), redirect_stderr(self):
                solution = self.linear_program.run_solve()
                self.send_solution.emit(pickle.dumps(solution))
                self.finished_computation.emit()
        except Exception:
            self.send_solution.emit(pickle.dumps("ERROR"))
            self.finished_computation.emit()

    def flush(self):
        pass

    # the output from the computation needs to be passed as a signal because
    # all Qt widgets must run on the main thread and their methods cannot be safely called
    # from other threads
    send_solution = Signal(bytes)
    finished_computation = Signal()

class ThermodynamicDialog(QDialog):
    """A dialog to perform several thermodynamic methods."""

    def __init__(
        self, appdata: AppData, central_widget: CentralWidget, analysis_type: ThermodynamicAnalysisTypes
    ) -> None:
        self.FWDID = "_FWDCNAPY"
        self.REVID = "_REVCNAPY"

        QDialog.__init__(self)
        if analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY:
            window_title = "Perform OptMDFpathway"
        elif analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS:
            window_title = "Perform OptMDFpathway bottleneck analysis"
        elif analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA:
            window_title = "Perform thermodynamic FBA"
        self.setWindowTitle(window_title)

        self.appdata = appdata
        self.central_widget = central_widget
        self.analysis_type = analysis_type

        self.reac_ids = self.appdata.project.cobra_py_model.reactions.list_attr("id")
        self.metabolite_ids = self.appdata.project.cobra_py_model.metabolites.list_attr(
            "id"
        )

        self.layout = QVBoxLayout()
        if analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY:
            l = QLabel(
                "Perform OptMDFpathway. dG'° values and metabolite concentration "
                "ranges have to be given in relevant annotations."
            )
            self.layout.addWidget(l)
        elif analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS:
            l = QLabel(
                "Perform OptMDFpathway bottleneck analysis. dG'° values and metabolite concentration "
                "ranges have to be given in relevant annotations.\nThe minimal amount of bottlenecks and their IDs "
                "to reach the given minimal MDF will be shown in the console afterwards."
            )
            self.layout.addWidget(l)
        elif analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA:
            l = QLabel(
                "Perform thermodynamic Flux Balance Analysis. Based on OptMDFpathway, you can perform an FBA at an OptMDF greater than the given value. "
                "\nE.g., if the OptMDF is greater than 0 kJ/mol, you perform an FBA with enforced thermodynamic feasibility."
                "\nFor this analysis, dG'° values and metabolite concentration "
                "ranges have to be given in relevant annotations.\nThe minimal amount of bottlenecks and their IDs "
                "to reach the given minimal MDF will be shown in the console afterwards."
            )
            self.layout.addWidget(l)

        if (analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS) or (analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA):
            lineedit_text = QLabel("MDF to reach [in kJ/mol]:")

            min_mdf_layout = QHBoxLayout()
            self.min_mdf = QLineEdit()
            self.min_mdf.setText("0.01")

            min_mdf_layout.addWidget(lineedit_text)
            min_mdf_layout.addWidget(self.min_mdf)
            self.layout.addItem(min_mdf_layout)

        self.at_objective = QCheckBox(
            "Set optimized value of current objective as lower boundary constraint"
        )
        self.at_objective.setChecked(True)
        self.layout.addWidget(self.at_objective)

        if analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA:
            self.at_objective.hide()

        met_concs_text = QLabel(
            "Default 'Cmin' and 'Cmax' values [in M] that will be used when not defined in a metabolite's annotation:"
        )
        self.layout.addWidget(met_concs_text)

        default_concs_layout = QHBoxLayout()

        text_min_default_conc = QLabel("Default Cmin [in M]:")
        self.min_default_conc = QLineEdit()
        self.min_default_conc.setText("1e-6")
        default_concs_layout.addWidget(text_min_default_conc)
        default_concs_layout.addWidget(self.min_default_conc)

        text_max_default_conc = QLabel(" Default Cmax [in M]:")
        self.max_default_conc = QLineEdit()
        self.max_default_conc.setText("0.2")
        default_concs_layout.addWidget(text_max_default_conc)
        default_concs_layout.addWidget(self.max_default_conc)

        self.layout.addItem(default_concs_layout)

        solver_group = QGroupBox("Solver:")
        solver_buttons_layout, self.solver_buttons = get_solver_buttons(appdata)
        solver_group.setLayout(solver_buttons_layout)
        self.layout.addWidget(solver_group)

        l3 = QHBoxLayout()
        self.button_optmdf = QPushButton("Compute")
        self.cancel = QPushButton("Close")
        l3.addWidget(self.button_optmdf)
        l3.addWidget(self.cancel)
        self.layout.addItem(l3)

        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button_optmdf.clicked.connect(self.compute_optmdf)

    def get_solution_from_thread(self, solution) -> None:
        solution = pickle.loads(solution)

        if solution == "ERROR":
            QMessageBox.warning(
                self,
                "Computational error",
                "Something went really wrong. The computation could not run.",
            )
        elif solution.status == Status.INFEASIBLE:
            QMessageBox.warning(
                self, "Infeasible", "No solution exists, the problem is either stoichiometrically or thermodynamically (e.g., the minimal MDF is too high) infeasible"
            )
        elif solution.status == Status.TIME_LIMIT:
            QMessageBox.warning(
                self,
                "Time limit hit",
                "No solution could be calculated as the time limit was hit.",
            )
        elif solution.status == Status.UNBOUNDED:
            QMessageBox.warning(
                self,
                "Unbounded",
                "The solution is unbounded (inf) so that no optimization solution can be shown.",
            )
        elif solution.status == Status.OPTIMAL:
            self.set_boxes(solution=solution.values, objective_value=solution.objective_value)

        self.setCursor(Qt.ArrowCursor)
        self.accept()

    @Slot()
    def compute_in_thread(
        self, linear_program: LinearProgram
    ) -> None:
        # launch progress viewer and computation thread
        self.computation_viewer = ComputationViewer()
        # connect signals to update progress
        self.computation_thread = ComputationThread(linear_program)
        self.computation_thread.finished_computation.connect(
            self.computation_viewer.close_window, Qt.QueuedConnection
        )
        self.computation_thread.send_solution.connect(
            self.get_solution_from_thread, Qt.QueuedConnection
        )
        self.computation_viewer.cancel_computation.connect(
            self.computation_thread.terminate, Qt.QueuedConnection
        )
        # show dialog and launch process
        self.computation_viewer.show()
        self.computation_thread.start()
        self.hide()

    @Slot()
    def compute_optmdf(self):
        self.setCursor(Qt.BusyCursor)

        dG0_values: Dict[str, Dict[str, float]] = {}
        for reaction in self.appdata.project.cobra_py_model.reactions:
            reaction: cobra.Reaction = reaction
            if "dG0" in reaction.annotation.keys():
                dG0_values[reaction.id] = {}
                dG0_values[reaction.id + self.FWDID] = {}
                dG0_values[reaction.id + self.REVID] = {}
                dG0_values[reaction.id]["dG0"] = float(reaction.annotation["dG0"])
                try:
                    dG0_values[reaction.id + self.FWDID]["dG0"] = float(
                        reaction.annotation["dG0"]
                    )
                    dG0_values[reaction.id + self.REVID]["dG0"] = -float(
                        reaction.annotation["dG0"]
                    )
                except ValueError:
                    QMessageBox.warning(
                        self,
                        "Wrong dG'° data type",
                        f"The dG'° given in reaction {reaction.id} is set as {reaction.annotation['dG0']} "
                        "and does not seem to be a valid number. Please correct this entry.",
                    )
                    return
                if "dG0_uncertainty" in reaction.annotation.keys():
                    dG0_values[reaction.id]["uncertainty"] = float(
                        reaction.annotation["dG0_uncertainty"]
                    )
                    try:
                        dG0_values[reaction.id + self.FWDID]["uncertainty"] = float(
                            reaction.annotation["dG0_uncertainty"]
                        )
                        dG0_values[reaction.id + self.REVID]["uncertainty"] = float(
                            reaction.annotation["dG0_uncertainty"]
                        )
                    except ValueError:
                        QMessageBox.warning(
                            self,
                            "Wrong dG'° uncertainty data type",
                            f"The dG'° uncertainty given in reaction {reaction.id} is set as {reaction.annotation['dG0_uncertainty']} "
                            "and does not seem to be a valid number. Please correct this entry.",
                        )
                        return
                else:
                    dG0_values[reaction.id]["uncertainty"] = 0.0
                    dG0_values[reaction.id + self.FWDID]["uncertainty"] = 0.0
                    dG0_values[reaction.id + self.REVID]["uncertainty"] = 0.0

        if dG0_values == {}:
            QMessageBox.warning(
                self,
                "No dG'° set",
                f"No reaction has a given standard Gibbs free energy (i.e., a dG'° which "
                "is set through the reaction annotation 'dG0'. This means that no thermodynamic "
                "constraints are set so that no thermodynamic OptMDFpathway can be calculated as "
                "at least one reaction needs a given dG'°. "
                "In order to solve this, please set or calculate (e.g., with eQuilibrator) dG'° "
                "values and either type them directly in as 'dG0' annotations or load them as "
                "JSON or Excel XLSX.",
            )
            return

        concentration_values: Dict[str, Dict[str, float]] = {}
        for metabolite in self.appdata.project.cobra_py_model.metabolites:
            metabolite: cobra.Metabolite = metabolite
            concentration_values[metabolite.id] = {}
            for conc_bound in ("Cmin", "Cmax"):
                if (conc_bound in metabolite.annotation.keys()):
                    try:
                        concentration_values[metabolite.id][conc_bound.replace("C", "")] = float(
                            metabolite.annotation[conc_bound]
                        )
                    except ValueError:
                        QMessageBox.warning(
                            self,
                            f"Wrong {conc_bound} data type",
                            f"The {conc_bound} given in metabolite {metabolite.id} is set as "
                            f"{metabolite.annotation['Cmin']} and does not seem to be a valid number."
                            " Please correct this entry so that it becomes a valid number.",
                        )
                        return
                else:
                    try:
                        if conc_bound == "Cmin":
                            bound_value = float(self.max_default_conc.text())
                        else:
                            bound_value = float(self.max_default_conc.text())
                    except ValueError:
                        QMessageBox.warning(
                            self,
                            f"Wrong default {conc_bound} data type",
                            f"The default {conc_bound} given by you in the dialog is set as "
                            f"{bound_value} and does not seem to be a valid number."
                            " Please correct this entry so that it becomes a valid number.",
                        )
                        return
                    concentration_values[metabolite.id][conc_bound.replace("C", "")] = bound_value

        with self.appdata.project.cobra_py_model as model:
            self.appdata.project.load_scenario_into_model(model)

            if self.at_objective.isChecked():
                solution = model_optimization_with_exceptions(model)
                if solution.status == "optimal":
                    extra_constraint = {}
                    for (
                        reaction,
                        coefficient,
                    ) in cobra.util.solver.linear_reaction_coefficients(model).items():
                        if reaction.reversibility:
                            extra_constraint[reaction.id + self.FWDID] = coefficient
                            extra_constraint[reaction.id + self.REVID] = -coefficient
                        else:
                            extra_constraint[reaction.id] = coefficient
                    extra_constraint["lb"] = solution.objective_value
                    extra_constraints = [extra_constraint]
                else:
                    QMessageBox.warning(
                        self,
                        solution.status,
                        f"No objective solution exists, the problem status is."
                        "Try the objective optimization with FBA alone for finding the problem, "
                        "or try a different objective.",
                    )
                    return
            else:
                extra_constraints = []

            for constraint in self.appdata.project.scen_values.constraints:
                # e.g., [({'EDD': 1.0}, '>=', 1.0)]
                extra_constraint = {key: value for key, value in constraint[0].items()}

                direction = constraint[1]
                rhs = constraint[2]
                if direction == ">=":
                    extra_constraint["lb"] = rhs
                elif direction == "<=":
                    extra_constraint["ub"] = rhs
                else:  # == "="
                    extra_constraint["lb"] = rhs
                    extra_constraint["ub"] = rhs

                extra_constraints.append(extra_constraint)

            solver_name = self.solver_buttons["group"].checkedButton().property("name")
            if solver_name == CPLEX:
                solver = Solver.CPLEX
            elif solver_name == GUROBI:
                solver = Solver.GUROBI
            elif solver_name == SCIP:
                solver = Solver.SCIP
            elif solver_name == GLPK:
                solver = Solver.GLPK

            if self.analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY:
                minimal_optmdf = -float("inf")
            else:
                minimal_optmdf_str = self.min_mdf.text()
                try:
                    minimal_optmdf = float(minimal_optmdf_str)
                except ValueError:
                    QMessageBox.warning(
                        self,
                        "Invalid minimal OptMDF",
                        f"The given minimal OptMDF could not be converted into a valid number (such as, e.g., 1.231)."
                        "Aborting calculation...",
                    )
                    return

            R = STANDARD_R
            T = STANDARD_T
            optmdfpathway_lp = create_optmdfpathway_milp(
                cobra_model=model,
                dG0_values=dG0_values,
                concentration_values=concentration_values,
                extra_constraints=extra_constraints,
                ratio_constraints=[],
                R=R,
                T=T,
                add_bottleneck_constraints=self.analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS,
                minimal_optmdf=minimal_optmdf,
            )
            if self.analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY:
                optmdfpathway_lp.set_objective(
                    {"var_B": 1},
                    direction=ObjectiveDirection.MAX,
                )
            elif self.analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS:
                optmdfpathway_lp.set_objective(
                    {"bottleneck_z_sum": 1},
                    direction=ObjectiveDirection.MIN,
                )
            elif self.analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA:
                objective_dict = {}
                for (
                    reaction,
                    coefficient,
                ) in cobra.util.solver.linear_reaction_coefficients(model).items():
                    if reaction.reversibility:
                        objective_dict[reaction.id + self.FWDID] = coefficient
                        objective_dict[reaction.id + self.REVID] = -coefficient
                    else:
                        objective_dict[reaction.id] = coefficient

                optmdfpathway_lp.set_objective(
                    objective_dict,
                    direction=ObjectiveDirection.MIN,
                )
            optmdfpathway_lp.construct_solver_object(
                solver=solver,
            )
            # solution = optmdfpathway_lp.run_solve()
            self.compute_in_thread(
                linear_program=optmdfpathway_lp,
            )

    def set_boxes(self, solution: Dict[str, float], objective_value: float):
        # Combine FWD and REV flux solutions
        combined_solution = {}
        for var_id in solution.keys():
            if var_id.endswith(self.FWDID):
                key = var_id.replace(self.FWDID, "")
                multiplier = 1.0
            elif var_id.endswith(self.REVID):
                key = var_id.replace(self.REVID, "")
                multiplier = -1.0
            else:
                key = var_id
                multiplier = 1.0
            if key not in combined_solution.keys():
                combined_solution[key] = 0.0
            combined_solution[key] += multiplier * solution[var_id]

        # write results into comp_values
        for search_key in self.reac_ids:
            if search_key in combined_solution.keys():
                self.appdata.project.comp_values[search_key] = (
                    float(combined_solution[search_key]),
                    float(combined_solution[search_key]),
                )
            if "f_var_" + search_key in combined_solution.keys():
                rounded_df = round(
                    combined_solution["f_var_" + search_key], self.appdata.rounding
                )
                self.appdata.project.df_values[search_key] = rounded_df

        # Write metabolite concentrations
        for metabolite_id in self.metabolite_ids:
            var_id = f"x_{metabolite_id}"
            if var_id in solution.keys():
                rounded_conc = round(exp(solution[var_id]), 9)
                self.appdata.project.conc_values[var_id[2:]] = rounded_conc

        # Show selected reaction-dependent values
        self.appdata.project.comp_values_type = 0
        self.central_widget.update()

        # Show OptMDF
        console_text = "print('\\n"
        if self.analysis_type == ThermodynamicAnalysisTypes.OPTMDFPATHWAY:
            optmdf = combined_solution["var_B"]
            console_text += f"OptMDF: {optmdf} kJ/mol"
        elif self.analysis_type == ThermodynamicAnalysisTypes.THERMODYNAMIC_FBA:
            optmdf = combined_solution["var_B"]
            console_text += f"Reached objective value: {objective_value}"
            console_text += f"\\nReached MDF @ optimum of objective: {optmdf} kJ/mol"
        elif self.analysis_type == ThermodynamicAnalysisTypes.BOTTLENECK_ANALYSIS:
            bottleneck_z_sum = combined_solution["bottleneck_z_sum"]
            console_text += f"Number of deactivated bottlenecks to reach minimal MDF: {bottleneck_z_sum}"

            for key in combined_solution.keys():
                if key.startswith("bottleneck_z_"):
                    if key == "bottleneck_z_sum":
                        continue
                    if combined_solution[key] > 0.1:
                        console_text += f"\\n* {key.replace('bottleneck_z_', '')}"
        console_text += "')"
        self.central_widget.kernel_client.execute(console_text)
        self.central_widget.show_bottom_of_console()


from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_DB_menu(object):
    def setupUi(self, DB_menu):
        DB_menu.setObjectName("DB_menu")
        DB_menu.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(DB_menu)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(30, 20, 141, 21))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(200, 20, 161, 16))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(30, 80, 141, 16))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(30, 120, 131, 21))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.label_4.setFont(font)
        self.label_4.setObjectName("label_4")
        self.comboBox = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox.setGeometry(QtCore.QRect(180, 80, 221, 22))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.comboBox.setFont(font)
        self.comboBox.setObjectName("comboBox")
        self.comboBox_2 = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox_2.setGeometry(QtCore.QRect(180, 120, 221, 22))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.comboBox_2.setFont(font)
        self.comboBox_2.setObjectName("comboBox_2")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(30, 170, 191, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(240, 170, 191, 31))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.pushButton_2.setFont(font)
        self.pushButton_2.setObjectName("pushButton_2")
        self.label_5 = QtWidgets.QLabel(self.centralwidget)
        self.label_5.setGeometry(QtCore.QRect(30, 50, 141, 21))
        font = QtGui.QFont()
        font.setFamily("Times New Roman")
        font.setPointSize(14)
        self.label_5.setFont(font)
        self.label_5.setObjectName("label_5")
        self.comboBox_3 = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox_3.setGeometry(QtCore.QRect(180, 50, 221, 21))
        self.comboBox_3.setObjectName("comboBox_3")
        DB_menu.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(DB_menu)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        DB_menu.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(DB_menu)
        self.statusbar.setObjectName("statusbar")
        DB_menu.setStatusBar(self.statusbar)

        self.retranslateUi(DB_menu)
        QtCore.QMetaObject.connectSlotsByName(DB_menu)

    def retranslateUi(self, DB_menu):
        _translate = QtCore.QCoreApplication.translate
        DB_menu.setWindowTitle(_translate("DB_menu", "DB"))
        self.label.setText(_translate("DB_menu", "Преподаватель:"))
        self.label_2.setText(_translate("DB_menu", "TextLabel"))
        self.label_3.setText(_translate("DB_menu", "Предмет:"))
        self.label_4.setText(_translate("DB_menu", "Вид занятия:"))
        self.pushButton.setText(_translate("DB_menu", "Назад"))
        self.pushButton_2.setText(_translate("DB_menu", "Сформровать отчет"))
        self.label_5.setText(_translate("DB_menu", "Группа:"))


import arcade
import random
from pyglet.math import Vec2
import math
import PIL
from PIL import Image
# pyinstaller --onefile --noconsole --clean main.py

class Player(arcade.AnimatedTimeBasedSprite):
	def __init__(self, filename, screen, scale):
		super().__init__(filename, scale=1.5)
		self.keys = {arcade.key.A:False, arcade.key.D:False}
		self.screen = screen
		self.speed = 4
		self.timer = 0

		self.superlist = [
			'superspeed', 'antigravity', 'teleportation',
			'earthquake', 'big', 'small', 'x-ray',
			'explosion', 'freeze', 'sound',
			'stone', 'telepathy']
		self.used_superlist = []
		self.superpower = 'start'

		self.timer_superpower = 0
		self.timer_change = 0
		self.look = 'right'
		self.shoot = False
		self.animation = 'idle'
		self.hp = 50

		frames_right = []
		frames_left = []
		frames_run_right = []
		frames_run_left = []
		for i in range(4):
			image = 'hero_idle/tile00'+str(i)+'.png'
			texture1, texture2 = arcade.load_texture_pair(image)
			frames_right.append(arcade.AnimationKeyframe(i, 150, texture1))
			frames_left.append(arcade.AnimationKeyframe(i+4, 150, texture2))
		for i in range(10):
			image = 'character_run/tile00'+str(i)+'.png'
			texture1, texture2 = arcade.load_texture_pair(image)
			frames_run_right.append(arcade.AnimationKeyframe(i+8, 50, texture1))
			frames_run_left.append(arcade.AnimationKeyframe(i+16, 50, texture2))

		self.animations = {
			'idle': {'left':frames_left, 	 'right':frames_right},
			'run' : {'left':frames_run_left, 'right':frames_run_right},
		}
		stone = arcade.load_texture('effects/stone.png')
		self.stone = [arcade.AnimationKeyframe(i, 150, stone)]

	def update(self, delta_time = 1/60):
		self.timer_change += delta_time

		if self.timer_change >= 3:
			self.timer_change = 0
			if self.superpower == '-':
				self.get_superpower()
			else:
				self.remove_superpower()

		if arcade.check_for_collision_with_list(self, self.screen.scene['Finish']):
			self.kill()
			self.screen.win = True

		if self.hp <= 0 or self.center_y < 0:
			self.kill()

		# остановка времени
		if self.superpower != 'freeze':
			self.screen.physics_engine.update()
			self.update_animation()

		# телепортация
		if self.superpower == 'teleportation':
			self.timer += delta_time
			if self.timer >= 0.5:
				self.timer = 0
				self.center_x += random.randint(-400, 400)
				self.center_y +=random.randint(-50, 100)

		# антигравитация
		if self.superpower == 'antigravity':
			g = random.randint(-2, 2)/10
			self.screen.physics_engine.gravity_constant = g

		# телепатия
		if self.superpower == 'telepathy':
			g = random.randint(-2, 2)/10
			for sprite in self.screen.scene['Enemies']:
				sprite.physics_engine.gravity_constant = g

		# движение
		self.change_x = 0
		if self.superpower != 'stone':
			if self.keys[arcade.key.D] and not self.keys[arcade.key.A]:
				self.change_x = self.speed
			elif self.keys[arcade.key.A] and not self.keys[arcade.key.D]:
				self.change_x = -self.speed

			# смена анимации
			if self.change_x != 0:
				if self.change_x > 0:
					self.look = 'right'
				else:
					self.look = 'left'
				if self.animation != 'run':
					self.change_animation('run', self.look)
			else:
				if self.animation != 'idle':
					self.change_animation('idle', self.look)

	def get_superpower(self):
		self.remove_superpower()
		if not self.superlist:
			self.superlist, self.used_superlist = self.used_superlist, self.superlist
		self.superpower = random.choice(self.superlist)
		#self.superpower = 'small'

		# суперскорость
		if self.superpower == 'superspeed':
			self.speed = 40

		# размер
		if self.superpower == 'big':
			self.center_y += 100
			self.scale = 5.5
			self.collision_radius =140
			for sp in arcade.check_for_collision_with_list(self, self.screen.scene['Platforms']):
				sp.kill()
			self.collision_radius =100
		elif self.superpower == 'small':
			self.scale = 0.1
			self.center_y += 32

		# рентген
		if self.superpower == 'x-ray':
			for sprite in self.screen.scene['Platforms']:
				sprite.visible = False

		# землетрясение
		if self.superpower == 'earthquake':
			self.screen.mediaplayer = self.screen.earthquake_sound.play(loop=True)

		# камень
		if self.superpower == 'stone':
			self.frames = self.stone
			self.cur_frame_idx = 0
			self.update_animation()
		else:
			self.change_animation(self.animation, self.look)

	def remove_superpower(self):
		for sprite in self.screen.scene['Platforms']:
			sprite.visible = True
		self.scale = 1.5
		self.speed = 4
		self.screen.physics_engine.gravity_constant = 0.5
		for sprite in self.screen.scene['Enemies']:
			sprite.physics_engine.gravity_constant = 0.5
		if self.superpower in ['big']:
			self.center_y += 100
		if self.screen.mediaplayer:
			arcade.stop_sound(self.screen.mediaplayer)
		if self.superpower in self.superlist:
			self.superlist.remove(self.superpower)
			self.used_superlist.append(self.superpower)
		self.change_animation(self.animation, self.look)
		self.superpower = '-'

	def change_animation(self, animation, look):
		if self.animation != animation and self.superpower == 'big':
			self.collision_radius =125
			self.center_y += 25
			for sp in arcade.check_for_collision_with_list(self, self.screen.scene['Platforms']):
				sp.kill()
			self.collision_radius =100
		self.animation, self.look = animation, look
		self.frames = self.animations[self.animation][self.look]
		self.cur_frame_idx = len(self.frames)-1
		self.time_counter = self.frames[self.cur_frame_idx].duration / 1000.0
		self.set_hit_box(self.frames[-2].texture.hit_box_points)

class Enemy(arcade.AnimatedTimeBasedSprite):
	def __init__(self, filename, screen, **kwargs):
		for i in ['flipped_horizontally', 'flipped_vertically', 'flipped_diagonally', 'hit_box_algorithm', 'hit_box_detail']:
			del kwargs[i]
		super().__init__(filename, **kwargs)

		#print(filename, kwargs['image_x'], kwargs['image_y'], kwargs['image_width'], kwargs['image_height'])\
		self.texture_left = arcade.load_texture(
			filename,
			kwargs['image_x'], kwargs['image_y'],
			kwargs['image_width'], kwargs['image_height'])
		self.texture_right = arcade.load_texture(
			filename,
			kwargs['image_x'], kwargs['image_y'],
			kwargs['image_width'], kwargs['image_height'],
			mirrored = True)

		self.screen = screen
		self.speed = 2
		self.pos = 0
		self.hp = 10
		self.timer_shoot = 0

	def update(self, delta_time = 1/60):
		if self.screen.scene['Player']:
			self.physics_engine.update()
			self.timer_shoot += delta_time

			if self. hp <= 0:
				self.kill()

			distance = arcade.get_distance_between_sprites(self, self.screen.player)
			self.change_x = 0
			pos1 = (self.center_x, self.center_y)
			pos2 = (self.screen.player.center_x, self.screen.player.center_y)
			if 0 < distance < 700 and arcade.has_line_of_sight(pos1, pos2, self.screen.scene['Platforms'], 500, 31) :
				if self.timer_shoot >= 1:
					pos3 = (self.screen.player.center_x, self.center_y)
					self.timer_shoot = 0
					if pos3[0] > pos1[0]:
						self.screen.scene['Bullets'].append(Bullet(self.right+1, self.center_y, 40, self.screen))
					else:
						self.screen.scene['Bullets'].append(Bullet(self.left-1, self.center_y, -40, self.screen))
				if distance > 100:
					if self.screen.player.center_x > self.center_x:
						self.change_x = min(self.speed, distance)
						self.texture = self.texture_right
					if self.screen.player.center_x < self.center_x:
						self.change_x = -min(self.speed, distance)
						self.texture = self.texture_left

class Bullet(arcade.Sprite):
	def __init__(self, x, y, speed, screen):
		if speed > 0:
			super().__init__(
				":resources:images/space_shooter/laserBlue01.png",
				center_y=y, hit_box_algorithm=None, scale=0.85)
			self.left = x
		else:
			super().__init__(
				":resources:images/space_shooter/laserBlue01.png",
				center_y=y, hit_box_algorithm=None, scale=0.85,
				flipped_horizontally=True)
			self.right = x
		self.change_x = speed
		self.screen = screen
		if self.screen.player.superpower == 'sound':
			arcade.play_sound(self.screen.shoot_sound, 500)
		else:
			arcade.play_sound(self.screen.shoot_sound, 1)

	def update(self):
		for wall in arcade.check_for_collision_with_list(self, self.screen.scene['Platforms']):
			self.kill()
			if self.screen.scene['Player'] and self.screen.scene['Player'][0].superpower == 'explosion':
				wall.kill()
				Explosion(self)
		for enemy in arcade.check_for_collision_with_list(self, self.screen.scene['Enemies']):
			self.kill()
			enemy.hp -= 1
			if self.screen.player and self.screen.player.superpower == 'explosion':
				enemy.kill()
				Explosion(self)
		if arcade.check_for_collision_with_list(self, self.screen.scene['Player']):
			self.kill()
			self.screen.player.hp -= 1
			if self.screen.player == 'explosion':
				Explosion(self)
		super().update()

class Explosion(arcade.AnimatedTimeBasedSprite):
	def __init__(self, bullet):
		super().__init__()
		self.screen = bullet.screen
		self.frames = bullet.screen.explosion_frames
		self.set_hit_box(self.frames[0].texture.hit_box_points)
		self.center_x = bullet.center_x
		self.center_y = bullet.center_y
		self.screen.scene['Explosions'].append(self)
		self.screen.camera.shake(Vec2(random.randint(-2, 2), random.randint(-5, 5)), speed=1, damping=0.95)
		self.screen.camera.update()
		self.scale = 3.5
		if self.screen.player.superpower == 'sound':
			arcade.play_sound(bullet.screen.explosion_sound, 500)
		else:
			arcade.play_sound(bullet.screen.explosion_sound, 1)

	def update(self):
		for player in arcade.check_for_collision_with_list(self, self.screen.scene['Player']):
			player.hp -= 0.75
		for wall in arcade.check_for_collision_with_list(self, self.screen.scene['Platforms']):
			wall.kill()
		for enemy in arcade.check_for_collision_with_list(self, self.screen.scene['Enemies']):
			enemy.kill()

	def update_animation(self):
		if self.cur_frame_idx >= len(self.frames)-1:
			self.kill()
		super().update_animation()

class Game(arcade.Window):
	def __init__(self):
		super().__init__(1280, 720, vsync=False, title='Inconvenient Superpowers')
		self.center_window()
		self.set_mouse_visible(False)
		arcade.set_background_color((40, 44, 65))
		arcade.enable_timings()

		self.camera = arcade.Camera()
		lp = {
			"Platforms": {"use_spatial_hash": True, 'spatial_hash_cell_size' : 8},
			"Hero":{"custom_class": Player, "custom_class_args": {"screen": self}},
			"Enemies":{'hit_box_algorithm' : None, "custom_class": Enemy, "custom_class_args": {"screen": self}},
			"Finish":{"use_spatial_hash": True},
		}
		self.tile_map = arcade.load_tilemap('map.tmx', 1.5, lp)
		self.scene = arcade.Scene.from_tilemap(self.tile_map)
		self.player = self.scene["Hero"][0]

		self.scene.add_sprite('Player',self.player)
		for sprite in self.scene['Enemies']:
			sprite.physics_engine = arcade.PhysicsEnginePlatformer(sprite, walls = self.scene['Platforms'], gravity_constant=0.5)
		self.physics_engine = arcade.PhysicsEnginePlatformer(self.player, walls = self.scene['Platforms'], gravity_constant=0.5)
		self.scene.add_sprite_list('Bullets')
		self.scene.add_sprite_list('Explosions')

		self.superpower_txt = arcade.Text(
			self.player.superpower,
			self.camera.position[0]+self.width/2,
			self.height-100,
			font_size=30,
			bold = True,
			width = 1200,
			multiline=True,
			align = 'center',
			anchor_x = 'center')

		self.hp_bar = arcade.Texture.create_filled('hpbar', (200, 20), (192, 57, 43))
		self.win = False

		self.explosion_frames = []
		for id in range(1,10):
			frame = 'effects/explosion-animation'+str(id)+'.png'
			texture = arcade.Texture(frame+str(id), PIL.Image.open(frame).convert('RGBA'))
			keyframe = arcade.AnimationKeyframe(10+id, 50, texture)
			self.explosion_frames.append(keyframe)

		self.shoot_sound = arcade.load_sound('sounds/shoot.ogg')
		self.explosion_sound = arcade.load_sound('sounds/explosion.ogg')
		self.earthquake_sound = arcade.load_sound('sounds/earthquake.ogg')
		self.mediaplayer = None

		self.descriptions = {
			'superspeed' : 'ты супер-быстрый!',
			'antigravity' : 'ты не подчиняешься гравитации!',
			'teleportation' : 'ты можешь телепортироваться!',
			'earthquake' : 'ты можешь вызывать землетрясения!',
			'big' : 'ты супер-большой!',
			'small' : 'ты супер-маленький!',
			'x-ray' : 'ты можешь видеть сквозь стены!',
			'explosion' : 'пули взрываются!',
			'freeze' : 'ты останавливаешь время!',
			'sound' : 'у тебя супер-слух!',
			'-' : '',
			'start' : 'каждые 3 секунды ты будешь получать супер-силу\nнадеюсь, это поможет тебе пройти уровень!',
			'stone' : 'ты можешь превращаться в камень!',
			'telepathy' : 'ты можешь поднимать врагов в воздух!'
		}

	def on_update(self, delta_time):
		self.camera.use()
		if self.player and self.player.superpower != 'freeze':
			self.scene['Bullets'].update()
			self.scene['Player'].update()
			self.scene['Enemies'].update()
			self.scene['Explosions'].update()
			for exp in self.scene['Explosions']:
				exp.update_animation()
			position = Vec2(self.player.center_x - self.width / 2, self.player.center_y - self.height/3)
			self.camera.move_to(position, 0.05)
			if self.player.superpower == 'earthquake':
				self.camera.shake(Vec2(random.randint(-2, 2), random.randint(-5, 5)), speed=1, damping=0.95)
				self.camera.update()
		else:
			self.player.update()
		self.superpower_txt.text = self.descriptions[self.player.superpower]
		#self.superpower_txt.text = int(arcade.get_fps())
		if not self.scene['Player']:
			if self.win:
				self.superpower_txt.text = 'Ты прошел игру!'
			else:
				self.superpower_txt.text = 'Ты умер'
		self.superpower_txt.x = self.camera.position[0]+self.width/2
		self.superpower_txt.y = self.camera.position[1]+self.height-100

	def on_draw(self):
		self.clear()
		self.scene.draw()
		self.superpower_txt.draw()
		if self.scene['Player']:
			self.hp_bar.draw_sized(
				self.camera.position[0]+self.width/2,
				self.camera.position[1]+20,
				self.player.hp*10, 10)
		#self.scene.draw_hit_boxes()

	def on_key_press(self, key, modifiers):
		if key == arcade.key.W and self.physics_engine.can_jump() and self.player.superpower != 'stone':
			self.physics_engine.jump(8)
		elif key in self.player.keys:
			self.player.keys[key] = True
		elif key == arcade.key.E:
			self.player.get_superpower()
		elif key == arcade.key.F:
			self.player.remove_superpower()
		elif key == arcade.key.SPACE and self.player.superpower not in ['freeze', 'stone']:
			if self.player.look == 'right':
				self.scene['Bullets'].append(Bullet(self.player.right+1, self.player.center_y, 40, self))
			else:
				self.scene['Bullets'].append(Bullet(self.player.left-1, self.player.center_y, -40, self))

	def on_key_release(self, key, modifiers):
		if key in self.player.keys:
			self.player.keys[key] = False

Game().run()


#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
# created_on: 2022-01-16 16:18

"""Wordcount."""

__author__ = 'Toran Sahu <toran.sahu@yahoo.com>'
__license__ = 'Distributed under terms of the MIT license'

import argparse
import logging
import re

import apache_beam as beam
from apache_beam.io import ReadFromText
from apache_beam.io import WriteToText
from apache_beam.options.pipeline_options import PipelineOptions

class WordExtractingDoFn(beam.DoFn):
    """Parse each line of input text into words."""

    def process(self, element):
        """Returns an iterator over the words of this element.

        The element is a line of text.  If the line is blank, note that, too.

        Args:
          element: the element being processed

        Returns:
          The processed element.
        """
        return re.findall(r'[\w\']+', element, re.UNICODE)

def run(argv=None):
    """Main entry point; defines and runs the wordcount pipeline."""
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--input',
        dest='input',
        default='gs://dataflow-samples/shakespeare/kinglear.txt',
        help='Input file to process.')
    parser.add_argument(
        '--output',
        dest='output',
        required=True,
        help='Output file to write results to.')
    known_args, pipeline_args = parser.parse_known_args(argv)

    # create a pipeline option
    pipeline_options = PipelineOptions(pipeline_args)

    # The pipeline will be run on exiting the with block.
    with beam.Pipeline(options=pipeline_options) as p:

        # Read the text file[pattern] into a PCollection.
        lines = p | 'Read' >> ReadFromText(known_args.input)

        counts = (
            lines
            | 'Split-test' >> (beam.ParDo(WordExtractingDoFn()).with_output_types(str))
            # | 'LowerCaseWay1' >> beam.Map(lambda x: str.lower(x))
            # | 'PairWithOne' >> beam.Map(lambda x: (x, 1))

            # | 'LowerCaseWay2' >> beam.Map(str.lower)
            # | 'PairWithOne' >> beam.Map(lambda x: (x, 1))

            # | 'PairLowerCaseWithOne' >> beam.Map(lambda x: (x.lower(), 1))

            | 'PairWithOne' >> beam.Map(lambda x: (x, 1))
            | 'GroupAndSum' >> beam.CombinePerKey(sum))

        # Format the counts into a PCollection of strings.
        def format_result(word, count):
            return '%s: %d' % (word, count)

        output = counts | 'Format' >> beam.MapTuple(format_result)

        # Write the output using a "Write" transform that has side effects.
        # pylint: disable=expression-not-assigned
        output | 'Write' >> WriteToText(known_args.output)

if __name__ == '__main__':
    logging.getLogger().setLevel(logging.INFO)
    run()


'''
Game configuration screen.
'''

import json

from kivy.app import App
from kivy.properties import NumericProperty, StringProperty
from kivy.metrics import dp
from kivy.animation import Animation

from screens.config import ConfigScreen

from widgets.widgets import (
    DictionaryCarouselItem
)

class GameConfigScreen(ConfigScreen):
    '''
    Screen where game options are choosen and new main game is created.

    Attributes
    ----------
    dictionaries : list[dict]
        list of available dictionaries with information about each one

    round_duration : int
        round duration in seconds

    selected_dictionary : str
        name of the selected dictionary

    selected_dictionary_index : int
        index of the selected dictionary

    Methods
    -------
    on_kv_post(_):
        kivy method

    change_round_duration(delta):
        change game round duration

    select_dictionary(carousel):
        select game dictionary

    show_dictionaries():
        display all the dictionaries available in the game

    fill_dictionaries():
        load all dictionaries shown on the screen

    toggle_carousel_buttons():
        toggle carousel button when the carousel appear or disappear

    hide_dictionaries():
        remove from the screen all the dictionaries available in the game

    start_main_game(self, _):
        start new main game
    '''
    dictionaries = []
    round_duration = NumericProperty(60)
    selected_dictionary = StringProperty("")
    selected_dictionary_index = None

    def on_kv_post(self, _):
        '''
        Kivy method overridden to bind function to the screen button and
        load all available game dictionaries from the file
        '''
        self.ids.screen_bottom_button.ids.button.bind(
            on_press=self.start_main_game)

        def change_time_button_callback(wid, pos, delta):
            if wid.collide_point(*pos):
                self.change_round_duration(delta)

        self.ids.minus_time.bind(
            on_touch_down=lambda wid, touch: change_time_button_callback(wid, touch.pos, -10)
        )
        self.ids.plus_time.bind(
            on_touch_down=lambda wid, touch: change_time_button_callback(wid, touch.pos, +10)
        )

        with open("./constants/dicts.json", encoding="utf-8") as f:
            dicts = json.load(f)
            for key, value in dicts.items():
                self.dictionaries.append({
                    "title": key, "name": value["name"],
                    "desc": value["description"],
                    "volume": value["volume"]
                })
        self.fill_dictionaries()

    def change_round_duration(self, delta):
        '''
        Change round duration game option

        Parameters
        ----------
        delta : int
            changes that should be applied to round duration
        '''
        if self.round_duration + delta > 0 and self.round_duration + delta <= 180:
            self.round_duration = self.round_duration + delta

    def select_dictionary(self, carousel):
        '''
        Select game dictionary for new main game

        Parameters
        ----------
        carousel : Carousel
            instance of the kivy Carousel
        '''
        if self.dictionaries[carousel.index]["name"]:
            self.selected_dictionary = carousel.current_slide.ids.dict_name.text
            self.selected_dictionary_index = carousel.index
            self.hide_dictionaries()

    def show_dictionaries(self, _):
        '''
        Open carousel with dictionaries to choose
        '''
        dict_layout = self.ids.dictionaries_layout
        dict_label = self.ids.dictionaries_label

        dict_layout.y = dict_label.y - dict_label.height - 20

        anim = Animation(height=dp(100), d=.3)
        anim.bind(on_complete=lambda anim, wid: self.toggle_carousel_buttons())
        anim.start(dict_layout.parent)

    def fill_dictionaries(self):
        '''
        Fill kivy Carousel with items with dictionaries
        '''
        carousel = self.ids.dictionaries_layout.ids.carousel
        for _, dictionary in enumerate(self.dictionaries):
            carousel.add_widget(
                DictionaryCarouselItem(
                    title=dictionary["title"],
                    description=dictionary["desc"],
                    amount_of_words=dictionary["volume"],
                    on_press=lambda _: self.select_dictionary(carousel)
                )
            )

    def toggle_carousel_buttons(self):
        '''
        Toggle carousel button
        '''
        self.ids.dictionaries_open.disabled = not self.ids.dictionaries_open.disabled
        self.ids.carousel_right.disabled = not self.ids.carousel_right.disabled
        self.ids.carousel_left.disabled = not self.ids.carousel_left.disabled
        self.ids.dictionaries_open.opacity = int(
            not self.ids.dictionaries_open.opacity)
        self.ids.carousel_right.opacity = int(
            not self.ids.carousel_right.opacity)
        self.ids.carousel_left.opacity = int(
            not self.ids.carousel_left.opacity)

    def hide_dictionaries(self):
        '''
        Hide carousel with dictionaries to choose
        '''
        dict_layout = self.ids.dictionaries_layout
        anim = Animation(height=0, d=.3)
        anim.bind(on_complete=lambda anim, wid: self.toggle_carousel_buttons())
        anim.start(dict_layout.parent)

    def start_main_game(self, _):
        '''
        Start main game and call App creating main game method
        '''
        if self.selected_dictionary_index is not None:
            App.get_running_app().create_main_game(
                round_duration=self.round_duration,
                selected_dictionary=self.dictionaries[self.selected_dictionary_index]["name"],
                teams=self.manager.get_screen("team_config").team_list,
                penalty=self.ids.penalty_checkbox.active,
                last_word=self.ids.last_word_checkbox.active,
                points_to_win=int(self.ids.points_to_win_label.text)
            )


"""
日志配置及装饰器
@filename: log.py
@author: hanzhichao
@date: 2018/12/27 16:13
"""
import sys;sys.path.append("..")

import os
import logging as l
import time
from functools import wraps

from utils.config import PROJECT_ROOT, Config

class Log(object):
    logger = l.getLogger()

    @classmethod
    def config_logger(cls):
        log_config = Config.get_log_config()
        log_dir = log_config.get('log_dir') or 'log'
        log_level = log_config.get('log_level') or 'debug'
        log_level = eval("l.{}".format(log_level.upper()))

        date = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        log_dir = os.path.join(PROJECT_ROOT, log_dir)

        # 总日志，按天分割
        total_log_file = os.path.join(log_dir, '{}.log'.format(date))
        # 单次运行日志
        task_log_dir = Config.task_dir or log_dir
        print(Config.task_dir)
        task_log_file = os.path.join(task_log_dir, 'log.log')

        cls.logger.setLevel(log_level)

        fh1 = l.FileHandler(total_log_file, mode='a', encoding='utf-8')
        fh1.setLevel(l.DEBUG)

        fh2 = l.FileHandler(task_log_file, mode='a', encoding='utf-8')
        fh2.setLevel(log_level)

        ch = l.StreamHandler()
        ch.setLevel(log_level)

        formatter = l.Formatter("%(asctime)s %(filename)s[%(lineno)d] %(levelname)s: %(message)s", "%Y/%m/%d %H:%M:%S")
        fh1.setFormatter(formatter)
        fh2.setFormatter(formatter)
        ch.setFormatter(formatter)

        cls.logger.addHandler(fh1)
        cls.logger.addHandler(fh2)
        cls.logger.addHandler(ch)

    @classmethod
    def get_logger(cls):
        cls.config_logger()
        return cls.logger

def log_time(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        t0 = time.time()
        back = func(*args, **kwargs)
        exec_time = time.time()-t0
        logging.debug("调用: {}() 执行时间: {}s".format(func.__name__,exec_time))
        return back
    return wrapper

def log_action(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.debug("调用: {}() 参数: {}".format(func.__name__, args or kwargs))
        return func(*args, **kwargs)
    return wrapper

def log_docstring(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        doc = func.__doc__
        doc = doc and doc.strip().split("\n")[0].strip() or func.__name__
        logging.info("------ {} ------".format(doc))
        return func(*args, **kwargs)
    return wrapper

logging = Log.get_logger()

if __name__ == '__main__':
    logging = Log.get_logger()
    logging.info("hello 中文")

    @log_action
    def func(a, b):
        print("...")

    @log_time
    def func2(a, b):
        print("...")

    @log_docstring
    def func3(a, b):
        """hello"""
        print("i run")

    func(a=1, b=2)
    func2(1, 2)
    func3(1,2)


from typing import Optional

from soda.sodacl.check_cfg import CheckCfg
from soda.sodacl.location import Location

class UserDefinedFailedRowsExpressionCheckCfg(CheckCfg):
    def __init__(
        self,
        source_header: str,
        source_line: str,
        source_configurations: Optional[dict],
        location: Location,
        name: str,
        fail_condition_sql_expr: Optional[str],
        samples_limit: Optional[int] = 100,
    ):
        super().__init__(source_header, source_line, source_configurations, location, name)
        self.samples_limit = samples_limit
        self.fail_condition_sql_expr: Optional[str] = fail_condition_sql_expr


import json
import os

with open('../data/team.txt') as f:
	data = {}
	team = ""
	player = {}
	players = []
	i = 0
	for line in f:
		if "TEAM" in line:
			data = {}
			players = []
			team = line.split("TEAM")[1].split("#")[0].strip()
			data['team'] = team
			#print(team)
		elif "--" in line:
			i = 0
			player['number'] = line.split("--")[1].strip()
			i += 1
		elif i == 1:
			player['role'] = line.strip()
			i += 1
		elif i == 2:
			player['name'] = line.strip()
			i += 1
		elif i == 3:
			player['age'] = line.split("-")[1].strip()
			i += 1
		elif i == 7:
			player['club'] = line.split("-")[1].strip()
			players.append(player)
			data['players'] = players
			#print(player)
			player = {}
			i += 1
			with open(os.getcwd() + '../data/'+team+'.json', 'w+') as out:
				json.dump(data, out, indent = 4)
		else:
			i += 1


# coding:utf-8
import requests
from lib.core.common import url_handle,get_random_ua
from lib.core.poc import POCBase
# ...
import urllib3
import ftplib
urllib3.disable_warnings()

class POC(POCBase):

    _info = {
        "author" : "jijue",                      # POC作者
        "version" : "1",                    # POC版本，默认是1
        "CreateDate" : "2022-01-01",        # POC创建时间
        "UpdateDate" : "2022-01-01",        # POC创建时间
        "PocDesc" : """
            v1: 后续可酌情考虑是否加上ftplib.FTP_TLS()
        """,                                # POC描述，写更新描述，没有就不写

        "name" : "FTP匿名访问",                        # 漏洞名称
        "VulnID" : "oFx-2022-0001",                      # 漏洞编号，以CVE为主，若无CVE，使用CNVD，若无CNVD，留空即可
        "AppName" : "",                     # 漏洞应用名称
        "AppVersion" : "",                  # 漏洞应用版本
        "VulnDate" : "2022-01-01",                    # 漏洞公开的时间,不知道就写今天，格式：xxxx-xx-xx
        "VulnDesc" : """
            账号是anonymous，密码随意
        """,                                # 漏洞简要描述

        "fofa-dork":"""
            protocol="ftp"
        """,                     # fofa搜索语句
        "example" : "",                     # 存在漏洞的演示url，写一个就可以了
        "exp_img" : "",                      # 先不管
    }

    def _verify(self):
        """
        返回vuln

        存在漏洞：vuln = [True,html_source] # html_source就是页面源码

        不存在漏洞：vuln = [False,""]
        """
        vuln = [False,""]
        host = self.host
        port = int(self.port)

        try:
            """
            检测逻辑，漏洞存在则修改vuln值为True，漏洞不存在则不动
            """
            # try:
            ftp = ftplib.FTP()
            ftp.connect(host=host,port=port,timeout=self.timeout)
            # except:
            #     ftp = ftplib.FTP_TLS()
            #     ftp.connect(host=host,port=port,timeout=self.timeout)
            ftp.login()
            ftp.close()

            vuln = [True,"FTP Anonymous Login Success!"]

        except Exception as e:
            raise e

        # 以下逻辑酌情使用
        if self._honeypot_check(vuln[1]) == True:
            vuln[0] = False

        return vuln

    def _attack(self):
        return self._verify()


import contextlib
import datetime
import json
import logging
import os
import shutil
import tempfile
import time
from pathlib import Path
from typing import Optional, Union, List, Dict

import boto3.session
import mlflow
import sagemaker
from sagemaker.estimator import Estimator

from deepcell.aws_utils import get_sagemaker_execution_role_arn, \
    create_bucket_if_not_exists, download_from_s3
from deepcell.data_splitter import DataSplitter
from deepcell.datasets.model_input import ModelInput, \
    copy_model_inputs_to_dir, write_model_input_metadata_to_disk
from deepcell.tracking.mlflow_utils import MLFlowTrackableMixin

class KFoldTrainingJobRunner(MLFlowTrackableMixin):
    """
    A wrapper on sagemaker Estimator. Starts a training job using the docker
    image given by image_uri
    """
    def __init__(self,
                 image_uri: str,
                 bucket_name: str,
                 profile_name='default',
                 region_name='us-west-2',
                 instance_type: Optional[str] = None,
                 instance_count=1,
                 timeout=24 * 60 * 60,
                 volume_size=30,
                 output_dir: Optional[Union[Path, str]] = None,
                 seed=None,
                 mlflow_server_uri=None,
                 mlflow_experiment_name: str = 'deepcell-train',
                 s3_data_key: Optional[str] = None,
                 ):
        """
        Parameters
        ----------
        image_uri
            The container image to run
        bucket_name
            The bucket to upload data to
        profile_name
            AWS profile name to use
        region_name
            AWS region to use
        instance_type
            Instance type to use
        instance_count
            Instance count to use
        timeout
            Training job timeout in seconds
        volume_size
            Volume size to allocate in GB
        output_dir
            Where to write output. Only used in local mode
        seed
            Seed to control reproducibility
        mlflow_server_uri
            MLFlow server URI. If provided, will log to MLFlow during training
        mlflow_experiment_name
            MLFlow experiment name to create runs under.
            Only used if mlflow_server_uri provided.
        s3_data_key: If provided, loads data from this key rather than
            uploading new data
            This is only used if not in "local mode"
        """
        super().__init__(server_uri=mlflow_server_uri,
                         experiment_name=mlflow_experiment_name)
        local_mode = instance_type == 'local'
        if local_mode and output_dir is None:
            raise ValueError('Must provide output_dir if in local mode')

        self._image_uri = image_uri
        self._local_mode = local_mode
        self._instance_type = instance_type
        self._instance_count = instance_count
        self._profile_name = profile_name
        self._bucket_name = bucket_name
        self._timeout = timeout
        self._volume_size = volume_size
        self._output_dir = output_dir
        self._seed = seed
        self._logger = logging.getLogger(__name__)
        sh = logging.StreamHandler()
        sh.setLevel(level=logging.INFO)
        sh.setFormatter(fmt=logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
        self._logger.addHandler(sh)

        self._s3_data_key = s3_data_key
        self._input_data_root_dir = os.path.join(
            'input_data',
            s3_data_key if s3_data_key is not None else
            datetime.datetime.now().strftime('%Y-%m-%d_%H:%m:%S-%f'))

        if mlflow_server_uri is not None:
            mlflow.set_tracking_uri(mlflow_server_uri)

        boto_session = boto3.session.Session(profile_name=profile_name,
                                             region_name=region_name)
        self._sagemaker_session = sagemaker.session.Session(
            boto_session=boto_session, default_bucket=bucket_name)

    def run(self,
            train_params: dict,
            model_inputs: Optional[List[ModelInput]] = None,
            k_folds=5,
            is_trial_run=False):
        """
        Train the model using `model_inputs` on sagemaker

        Parameters
        ----------
        model_inputs: the input data. If provided, data_load_path should not be
        train_params: Training parameters to log to MLFlow
        k_folds: The number of CV splits.
        is_trial_run: Set this to True to only run on a single fold. Useful
                    for trying out a new idea without running on every fold

        Returns
        -------
        None
        """
        if self._local_mode:
            if model_inputs is None:
                raise ValueError('model_inputs is required in local mode')
        if model_inputs is not None and self._s3_data_key is not None:
            raise ValueError('Supply either `model_inputs` or s3_data_key, '
                             'but not both')
        if model_inputs is None and self._s3_data_key is None:
            raise ValueError('Supply one of `model_inputs` or s3_data_key')
        sagemaker_session = None if self._local_mode else \
            self._sagemaker_session

        sagemaker_role_arn = get_sagemaker_execution_role_arn()

        s3_uri = f's3://{self._bucket_name}/{self._input_data_root_dir}'
        if self._s3_data_key is not None:
            model_inputs = self._get_model_inputs_from_s3(s3_uri=s3_uri)
        else:
            if not self._local_mode:
                # If we are not running locally and we are not loading data
                # from s3, upload all model_input metadata to S3
                with tempfile.TemporaryDirectory() as temp_dir:
                    write_model_input_metadata_to_disk(
                        path=Path(temp_dir) / 'all' / 'model_inputs.json',
                        model_inputs=model_inputs
                    )
                    self._sagemaker_session.upload_data(
                        path=str(Path(temp_dir) / 'all' / 'model_inputs.json'),
                        key_prefix=f'{self._input_data_root_dir}/all',
                        bucket=self._bucket_name)

        if self._is_mlflow_tracking_enabled:
            def _get_channel_order(model_inputs: List[ModelInput]):
                channel_order = set(tuple(x.channel_order)
                                    for x in model_inputs)
                if len(channel_order) != 1:
                    raise ValueError(f'Expected to find 1 channel order but '
                                     f'found {channel_order}')
                return list(channel_order)[0]

            train_params['data_params']['channel_order'] = \
                _get_channel_order(model_inputs=model_inputs)
            run_name = (
                f'CV-{int(time.time())}'
                if train_params['tracking_params']['mlflow_run_name'] is None
                else train_params['tracking_params']['mlflow_run_name']
            )
            mlflow_run = self._create_parent_mlflow_run(
                run_name=run_name,
                hyperparameters=train_params,
                hyperparameters_exclude_keys=['tracking_params', 'save_path',
                                              'model_load_path',
                                              'model_inputs_path',
                                              'model_inputs', 'n_folds',
                                              'data_load_path',
                                              's3_data_key'])
        else:
            mlflow_run = contextlib.nullcontext()
        channels = ('train', 'validation')

        training_jobs = []

        with mlflow_run:
            for k, (train_idx, test_idx) in enumerate(
                    DataSplitter.get_cross_val_split_idxs(
                        model_inputs=model_inputs, seed=self._seed,
                        n_splits=k_folds)):
                if is_trial_run:
                    if k != 0:
                        continue
                train = [model_inputs[i] for i in train_idx]
                test = [model_inputs[i] for i in test_idx]

                output_dir = f'file://{Path(self._output_dir) / str(k)}' if \
                    self._local_mode else None
                job_name = None if self._local_mode else \
                    f'deepcell-train-fold-{k}-{int(time.time())}'
                env_vars = {
                    'fold': f'{k}',
                    'mlflow_server_uri': self._mlflow_server_uri,
                    'mlflow_experiment_name': self._mlflow_experiment_name,
                    'sagemaker_job_name': job_name,
                    'mlflow_parent_run_id':
                        (mlflow_run.info.run_id if
                         self._is_mlflow_tracking_enabled else None)
                }

                # Due to
                # https://github.com/aws/sagemaker-python-sdk/issues/2930
                hyperparameters = env_vars if self._local_mode else {}

                with tempfile.TemporaryDirectory() as temp_dir:
                    local_data_path = {c: Path(temp_dir) / c / str(k)
                                       for c in channels}
                    if self._s3_data_key is None:
                        self._logger.info(f'Copying train model inputs to '
                                          f'{local_data_path["train"]}')
                        copy_model_inputs_to_dir(
                            destination=local_data_path['train'],
                            model_inputs=train
                        )
                        self._logger.info(
                            f'Copying validation model inputs '
                            f'to {local_data_path["validation"]}')
                        copy_model_inputs_to_dir(
                            destination=local_data_path['validation'],
                            model_inputs=test
                        )
                    if self._local_mode:
                        # In local mode, we are just reading local data
                        data_path = {k: f'file://{v}'
                                     for k, v in local_data_path.items()}
                    elif self._s3_data_key is None:
                        # If not reading data from S3,
                        # we need to upload data to s3
                        data_path = self._upload_local_data_to_s3(
                            k=k, local_data_path=local_data_path)
                    else:
                        # We are loading data from s3
                        data_path = {c: f'{s3_uri}/{c}/{k}' for c in channels}

                    estimator = Estimator(
                        sagemaker_session=sagemaker_session,
                        role=sagemaker_role_arn,
                        instance_count=self._instance_count,
                        instance_type=self._instance_type,
                        image_uri=self._image_uri,
                        output_path=output_dir,
                        hyperparameters=hyperparameters,
                        volume_size=self._volume_size,
                        max_run=self._timeout,
                        environment=env_vars
                    )

                    estimator.fit(
                        inputs=data_path,
                        wait=False,
                        job_name=job_name
                    )
                    training_jobs.append(job_name)

                    # sleeps to avoid Sagemaker Training Job API throttling
                    time.sleep(5)
            self._wait_until_training_jobs_have_finished(
                training_jobs=training_jobs)
            self._log_cross_validation_end_metrics_to_mlflow()

    def _upload_local_data_to_s3(
            self,
            local_data_path: Dict[str, Path],
            k: int
    ) -> Dict[str, str]:
        """
        Uploads local data to s3

        Parameters
        ----------
        local_data_path: dict mapping sagemaker "channel" to Path on disk
        k: the current fold

        Returns
        -------
        Dict mapping channel name to path on s3
        """
        create_bucket_if_not_exists(
            bucket=self._bucket_name,
            region_name=self._sagemaker_session.boto_session
            .region_name)
        s3_paths = {}
        for channel in local_data_path:
            self._logger.info(f'Compressing {local_data_path[channel]}')
            compressed = shutil.make_archive(
                base_name=str(local_data_path[channel]),
                format='gztar',
                root_dir=str(local_data_path[channel])
            )
            self._logger.info(f'Done compressing {local_data_path[channel]}')

            s3_key_prefix = f'{self._input_data_root_dir}/{channel}/{k}'
            self._logger.info(f'Uploading {compressed} to '
                              f's3://{self._bucket_name}/{s3_key_prefix}')
            s3_path = self._sagemaker_session.upload_data(
                path=compressed,
                key_prefix=s3_key_prefix,
                bucket=self._bucket_name)
            s3_paths[channel] = s3_path

        return s3_paths

    @staticmethod
    def _get_model_inputs_from_s3(s3_uri: str,
                                  fold: Optional[int] = None,
                                  is_train=False) \
            -> List[ModelInput]:
        """
        Get model inputs from s3
        @param s3_uri: Path on s3 to read data from
        @param fold: Optional fold, to get model inputs for just that fold
        @param is_train: Whether to get train or validation model inputs
            (only applicable with `fold` passed)
        @return: List[ModelInput]
        """
        if fold is not None:
            train_or_validation = 'train/' if is_train else 'validation/'
        else:
            train_or_validation = ''
        with download_from_s3(
                uri=f'{s3_uri}/'
                    f'{fold or "all"}/'
                    f'{train_or_validation}model_inputs.json')['Body'] as f:
            model_inputs = json.load(f)
        model_inputs = [ModelInput(**x) for x in model_inputs]
        return model_inputs

    def _wait_until_training_jobs_have_finished(
            self, training_jobs: List[str]):
        """Blocks until all training jobs have completed

          Args:
             training_jobs: array of submitted training job names

        """
        all_jobs_done = False
        while not all_jobs_done:
            completed_jobs = 0
            for job in training_jobs:
                job_detail = self._sagemaker_session.sagemaker_client\
                    .describe_training_job(TrainingJobName=job)
                job_status = job_detail['TrainingJobStatus']
                if job_status.lower() in ('completed', 'failed', 'stopped'):
                    completed_jobs += 1
            if completed_jobs == len(training_jobs):
                all_jobs_done = True
            else:
                time.sleep(30)


# -*- coding: utf-8 -*-
"""
Created on Fri Jun 03 15:29:08 2016

@author: dell
"""
import math
import matplotlib.pyplot as plt

class ocillatory:
    def __init__(self,q=0,OmegaD=0,FD=0,dt=0):
        self.t=[0]
        self.omega=[0]
        self.OmegaD=OmegaD
        self.dt=dt
        self.gl=9.8
        self.theta=[0.2]
        self.q=q
        self.FD=FD
    def pendulum(self):
        i=0
        while self.t[i]<=10:
            self.omega.append(self.omega[i]-self.gl*self.theta[i]*self.dt)
            self.theta.append(self.theta[i]+self.omega[i+1]*self.dt)
            self.t.append(self.t[i]+self.dt)
            i=i+1
    def forcedpendulum(self):
        i=0
        while self.t[i]<=15:
            self.omega.append(self.omega[i]-self.gl*self.theta[i]*self.dt-self.q*self.omega[i]*self.dt+self.FD*math.sin(self.OmegaD*self.t[i])*self.dt)
            self.theta.append(self.theta[i]+self.omega[i+1]*self.dt)
            self.t.append(self.t[i]+self.dt)
            i=i+1
    def pl(self,style='',slogan=''):
        plt.plot(self.t,self.theta,style,label=slogan)

A=ocillatory(1,OmegaD=2,FD=0,dt=0.04)
A.forcedpendulum()
A.pl(slogan='FD=0')

B=ocillatory(1,OmegaD=2,FD=0.2,dt=0.04)
B.forcedpendulum()
B.pl(slogan='FD=0.2')

c=ocillatory(1,OmegaD=2,FD=1,dt=0.04)
c.forcedpendulum()
c.pl(slogan='FD=1')

plt.title('forced pendulum')
plt.xlabel('t/s')
plt.ylabel('$\theta$/rad')
plt.legend()
plt.show()