Exemple #1
0
 def test_properties(self) -> None:
     temporal = "%s%s" % (config.value("ebcomportamiento/temp_dir"), u"/test_last_modified.txt")
     file_ = types.File(temporal)
     self.assertEqual(file_.path, config.value("ebcomportamiento/temp_dir"))
     self.assertEqual(file_.fullName, temporal)
     self.assertEqual(file_.extension, ".txt")
     self.assertEqual(file_.baseName, "test_last_modified")
     self.assertTrue(file_.exists)
     self.assertEqual(file_.size, 38)
def base_model(name: str) -> Any:
    """Import and return sqlAlchemy model for given table name."""
    # print("Base", name)

    if application.PROJECT.conn_manager is None:
        raise Exception("Project is not connected yet")

    path = _path("%s.mtd" % name, False)
    if path is None:
        return None
    if path.find("system_module/tables") > -1:
        path = path.replace(
            "system_module/tables",
            "%s/cache/%s/sys/file.mtd" % (
                config.value("ebcomportamiento/temp_dir"),
                application.PROJECT.conn_manager.mainConn().DBName(),
            ),
        )
    if path:
        path = "%s_model.py" % path[:-4]
        if os.path.exists(path):
            try:

                # FIXME: load_module is deprecated!
                # https://docs.python.org/3/library/importlib.html#importlib.machinery.SourceFileLoader.load_module
                loader = machinery.SourceFileLoader(name, path)
                return loader.load_module()  # type: ignore
            except Exception as exc:
                LOGGER.warning("Error recargando model base:\n%s\n%s", exc,
                               traceback.format_exc())
                pass

    return None
Exemple #3
0
    def editarFicheroXML(self) -> None:
        """Edit xml file."""
        cursor = self.cursor()
        util = qsa.FLUtil()
        if cursor.checkIntegrity():
            temporal = qsa.System.getenv(u"TMP")
            if temporal == "":
                temporal = qsa.System.getenv(u"TMPDIR")
            if temporal == "":
                temporal = qsa.System.getenv(u"HOME")
            if temporal == "":
                from pineboolib.core.settings import config

                temporal = config.value("ebcomportamiento/temp_dir")
            temporal = qsa.ustr(temporal, "/", cursor.valueBuffer(u"nombre"))
            comando = ""
            contenido = self.child("contenido").toPlainText()
            if util.getOS() == "MACX":
                qsa.FileStatic.write(temporal, qsa.ustr(contenido, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n"))
                comando = qsa.ustr(qsa.sys.installPrefix(), "/bin/teddy.app/Contents/MacOS/teddy")
            else:
                qsa.FileStatic.write(temporal, contenido)
                comando = qsa.ustr(qsa.sys.installPrefix(), "/bin/teddy")

            self.setDisabled(True)
            qsa.ProcessStatic.execute([comando, temporal])
            self.child("contenido").setText(qsa.FileStatic.read(temporal))
            self.setDisabled(False)
def load_model(nombre):
    """Import and return sqlAlchemy model for given table name."""

    if nombre is None:
        return

    # if nombre in processed_:
    #    return None

    # processed_.append(nombre)

    # nombre_qsa = nombre.replace("_model", "")
    # model_name = nombre_qsa[0].upper() + nombre_qsa[1:]
    # mod = getattr(qsa_dict_modules, model_name, None)
    # if mod is None:
    #    mod = base_model(nombre)
    #    if mod:
    #        setattr(qsa_dict_modules, model_name, mod)  # NOTE: Use application.qsadictmodules

    db_name = application.PROJECT.conn_manager.mainConn().DBName()

    module = None
    file_path = filedir(
        config.value("ebcomportamiento/temp_dir"),
        "cache",
        db_name,
        "models",
        "%s_model.py" % nombre,
    )
    if os.path.exists(file_path):

        module_path = "%s_model" % (nombre)
        # if module_path in sys.modules:
        #    # print("Recargando", module_path)
        #    try:
        #        module = importlib.reload(sys.modules[module_path])
        #    except Exception as exc:
        #        logger.warning("Error recargando módulo:\n%s\n%s", exc, traceback.format_exc())
        #        pass
        # else:
        # print("Cargando", module_path)
        try:
            spec = importlib.util.spec_from_file_location(
                module_path, file_path)  # type: ignore
            module = importlib.util.module_from_spec(spec)  # type: ignore
            sys.modules[spec.name] = module
            spec.loader.exec_module(module)
        except Exception as exc:
            LOGGER.warning("Error cargando módulo:\n%s\n%s", exc,
                           traceback.format_exc())
            pass
            # models_[nombre] = mod

    # if mod:
    #    setattr(qsa_dict_modules, model_name, mod)

    # print(3, nombre, mod)
    return module
Exemple #5
0
    def test_mkdir_rmdir(self) -> None:
        """Test mkdir and rmdir."""

        tmp_dir = config.value("ebcomportamiento/temp_dir")
        my_dir = types.Dir(tmp_dir)
        my_dir.mkdir("test")
        self.assertTrue(os.path.exists("%s/test" % tmp_dir))
        my_dir.rmdirs("test")
        self.assertFalse(os.path.exists("%s/test" % tmp_dir))
Exemple #6
0
    def test_last_modified(self) -> None:
        """Test lastModified."""

        temporal = "%s%s" % (config.value("ebcomportamiento/temp_dir"), u"/test_last_modified.txt")
        contenido = 'QT_TRANSLATE_NOOP("MetaData","Código")'
        file_ = types.File(temporal)
        file_.write(contenido)
        file_.close()
        self.assertNotEqual(file_.lastModified(), "")
Exemple #7
0
    def setUpClass(cls) -> None:
        """Ensure pineboo is initialized for testing."""
        config.set_value("application/isDebuggerMode", True)
        config.set_value("application/dbadmin_enabled", True)
        cls.prev_main_window_name = config.value(
            "ebcomportamiento/main_form_name", "eneboo")
        config.set_value("ebcomportamiento/main_form_name", "eneboo_mdi")

        init_testing()
Exemple #8
0
    def test_write_read_byte_1(self) -> None:
        """Check that you read the same as you write."""

        temporal = "%s%s" % (
            config.value("ebcomportamiento/temp_dir"),
            u"/test_types_file_bytes.txt",
        )
        contenido = "Texto\n".encode("utf-8")
        types.File(temporal).write(contenido)
        contenido_2 = types.File(temporal).read(True)
        self.assertEqual(contenido, contenido_2.encode("utf-8"))
        os.remove(temporal)
Exemple #9
0
    def test_write_read_values_2(self) -> None:
        """Check that you read the same as you write."""

        temporal = "%s%s" % (
            config.value("ebcomportamiento/temp_dir"),
            u"/test_types_file_static.txt",
        )
        contenido = 'QT_TRANSLATE_NOOP("MetaData","Código")'
        types.FileStatic.write(temporal, contenido)
        contenido_2 = types.FileStatic.read(temporal)
        self.assertEqual(contenido, contenido_2)
        os.remove(temporal)
Exemple #10
0
    def test_full_name_and_readable(self) -> None:
        """Check fullName"""

        temporal = "%s%s" % (
            config.value("ebcomportamiento/temp_dir"),
            u"/test_types_file_full_name.txt",
        )
        contenido = 'QT_TRANSLATE_NOOP("MetaData","Código")'
        file_ = types.File(temporal)
        file_.write(contenido)
        self.assertEqual(file_.fullName, temporal)
        self.assertTrue(file_.readable())
def error_manager(e: str) -> str:
    """Process an error text and return a better version for GUI."""
    from pineboolib.core.settings import config

    tmpdir = config.value("ebcomportamiento/temp_dir")
    e = e.replace(tmpdir, "...")
    e = re.sub(r"/[0-9a-f]{35,60}\.qs\.py", ".qs.py", e)

    text = translate("scripts", "Error ejecutando un script")
    text += ":\n%s" % e
    text += process_error(e)
    logger.error(text)
    return text
Exemple #12
0
    def test_change_dir(self) -> None:
        """Test change dir."""

        tmp_dir = config.value("ebcomportamiento/temp_dir")
        my_dir = types.Dir(tmp_dir)
        original_dir = my_dir.current
        # my_dir.mkdir("test_change_dir")
        # my_dir.cd("%s/test_change_dir" % tmp_dir)
        my_dir.cd(original_dir)
        self.assertEqual(my_dir.current, original_dir)
        my_dir.cdUp()
        # self.assertEqual(os.path.realpath(my_dir.current), tmp_dir)
        # my_dir.rmdirs("test_change_dir")
        my_dir.cd(original_dir)
Exemple #13
0
    def test_basic(self) -> None:
        """Test basic functions."""
        from pineboolib.application.parsers.mtdparser import pnmtdparser
        from pineboolib.core.utils.utils_base import filedir
        import os

        file = filedir(
            "%s/cache/temp_db/sys/file.mtd/flmodules_model.py"
            % config.value("ebcomportamiento/temp_dir")
        )
        if os.path.exists(file):
            os.remove(file)
        pnmtdparser.mtd_parse("flmodules")
        self.assertTrue(os.path.exists(file))
Exemple #14
0
def mtd_parse(table_name: str) -> Optional[str]:
    """
    Parse MTD into SqlAlchemy model.
    """

    if table_name.endswith(".mtd"):
        table_name = table_name[:-4]

    if table_name.find("alteredtable") > -1 or table_name.startswith("fllarge_"):
        return None

    if application.PROJECT.conn_manager is None:
        raise Exception("Project is not connected yet")
    mtd_ = application.PROJECT.conn_manager.manager().metadata(table_name, True)
    if mtd_ is None:
        return None
    mtd: PNTableMetaData = cast(PNTableMetaData, mtd_)
    if mtd.isQuery():
        return None

    mtd_file = _path("%s.mtd" % table_name)

    if mtd_file is None:
        LOGGER.warning("No se encuentra %s.mtd", table_name)
        return None

    dest_file = "%s_model.py" % mtd_file[: len(mtd_file) - 4]
    if dest_file.find("system_module") > -1:
        sys_dir = "%s/cache/%s/sys" % (
            config.value("ebcomportamiento/temp_dir"),
            application.PROJECT.conn_manager.mainConn().DBName(),
        )
        dest_file = "%s/file.mtd/%s_model.py" % (sys_dir, table_name)

        if not os.path.exists(sys_dir):
            os.mkdir(sys_dir)
        if not os.path.exists("%s/file.mtd" % sys_dir):
            os.mkdir("%s/file.mtd" % sys_dir)

    if not os.path.exists(dest_file):
        lines = generate_model(mtd)

        if lines:
            file_ = open(dest_file, "w")
            for line in lines:
                file_.write("%s\n" % line)
            file_.close()

    return dest_file
Exemple #15
0
    def test_write_read_line_1(self) -> None:
        """Check that you read the same as you write."""

        temporal = "%s%s" % (
            config.value("ebcomportamiento/temp_dir"),
            u"/test_types_file_lines.txt",
        )
        contenido = "Esta es la linea"
        types.File(temporal).writeLine("%s 1" % contenido)
        types.File(temporal).writeLine("%s 2" % contenido, 4)
        file_read = types.File(temporal)
        linea_1 = file_read.readLine()
        self.assertEqual("%s 1\n" % contenido, linea_1)
        linea_2 = file_read.readLine()
        self.assertEqual("%s" % contenido[0:4], linea_2)
        os.remove(temporal)
def load_models() -> None:
    """Load all sqlAlchemy models."""

    from pineboolib.application.qsadictmodules import QSADictModules

    if application.PROJECT.conn_manager is None:
        raise Exception("Project is not connected yet")

    main_conn = application.PROJECT.conn_manager.mainConn()

    db_name = main_conn.DBName()
    tables = main_conn.tables()

    QSADictModules.save_other("Base", main_conn.declarative_base())
    QSADictModules.save_other("session", main_conn.session())
    QSADictModules.save_other("engine", main_conn.engine())

    for table in tables:
        try:
            mod = base_model(table)
        except Exception:
            mod = None

        if mod is not None:
            model_name = "%s%s" % (table[0].upper(), table[1:])
            class_ = getattr(mod, model_name, None)
            if class_ is not None:
                QSADictModules.save_other(model_name, class_)

    for root, dirs, files in os.walk(
            filedir(config.value("ebcomportamiento/temp_dir"), "cache",
                    db_name, "models")):
        for nombre in files:  # Buscamos los presonalizados
            if nombre.endswith("pyc"):
                continue
            nombre = nombre.replace("_model.py", "")
            mod = load_model(nombre)
            if mod is not None:
                model_name = "%s%s" % (nombre[0].upper(), nombre[1:])

                class_ = getattr(mod, model_name, None)
                if class_ is not None:
                    QSADictModules.save_other(model_name, class_)
Exemple #17
0
    def __init__(self):
        """Inicialize."""
        splash_path = filedir(
            "./core/images/splashscreen/%s240.png" %
            ("dbadmin"
             if config.value("application/dbadmin_enabled") else "quick"))

        splash_pix = QtGui.QPixmap(splash_path)
        self._splash = QtWidgets.QSplashScreen(splash_pix,
                                               QtCore.Qt.WindowStaysOnTopHint)
        self._splash.setMask(splash_pix.mask())

        frameGm = self._splash.frameGeometry()
        screen = QtWidgets.QApplication.desktop().screenNumber(
            QtWidgets.QApplication.desktop().cursor().pos())
        centerPoint = QtWidgets.QApplication.desktop().screenGeometry(
            screen).center()
        frameGm.moveCenter(centerPoint)
        self._splash.move(frameGm.topLeft())
Exemple #18
0
 def __init__(
     self,
     parent: Optional["QtWidgets.QWidget"] = None,
     name: Optional[str] = None,
     multiLang: bool = False,
     sysTrans: bool = False,
 ) -> None:
     """Inicialize."""
     super(FLTranslator, self).__init__()
     self.logger = logging.getLogger("FLTranslator")
     self._prj = parent
     if not name:
         raise Exception("Name is mandatory")
     self._id_module = name[:name.rfind("_")]
     self._lang = name[name.rfind("_") + 1:]
     self._multi_lang = multiLang
     self._sys_trans = sysTrans
     self._ts_translation_contexts = {}
     self._translation_from_qm = config.value(
         "ebcomportamiento/translations_from_qm", False)
Exemple #19
0
    def __init__(self) -> None:
        """Constructor."""

        check_dependencies({"fpdf": "fpdf2"})

        self._parser_tools = kparsertools.KParserTools()
        self._avalible_fonts = []
        self._page_top: Dict[int, int] = {}
        self._unavalible_fonts = []
        self.design_mode = config.value("ebcomportamiento/kugar_debug_mode",
                                        False)
        self._actual_data_line = None
        self._no_print_footer = False
        self.increase_section_size = 0
        self.actual_data_level = 0
        self.prev_level = -1
        self.draws_at_header = {}
        self.detailn = {}
        self.name_ = ""
        self._actual_append_page_no = -1
        self.reset_page_count = False
        self.new_page = False
Exemple #20
0
def cache_xpm(value: str) -> str:
    """
    Return a path to a file with the content of the specified string.

    @param value. text string with the xpm or path to this.
    @return file path contains Xpm
    """

    if not value:
        LOGGER.warning("the value is empty!")
        return ""

    xpm_name = value[:value.find("[]")]
    xpm_name = xpm_name[xpm_name.rfind(" ") + 1:]

    conn = application.PROJECT.conn_manager.mainConn()
    if conn is None:
        raise Exception("Project is not connected yet")

    cache_dir = "%s/cache/%s/cacheXPM" % (application.PROJECT.tmpdir,
                                          conn.DBName())
    if not os.path.exists(cache_dir):
        os.mkdir(cache_dir)

    if value.find("cacheXPM") > -1:
        file_name = value
    else:
        file_name = "%s/%s.xpm" % (cache_dir, xpm_name)

    if not os.path.exists(file_name) or config.value(
            "ebcomportamiento/no_img_cached", False):
        file_ = open(file_name, "w")
        file_.write(value)
        file_.close()

    return file_name
Exemple #21
0
def function(*args: str) -> Any:
    """
    Load QS string code and create a function from it.

    Parses it to Python and return the pointer to the function.
    """

    import sys as python_sys
    import importlib

    # Leer código QS embebido en Source
    # asumir que es una funcion anónima, tal que:
    #  -> function($args) { source }
    # compilar la funcion y devolver el puntero
    arguments = args[:len(args) - 1]
    source = args[len(args) - 1]
    qs_source = """

function anon(%s) {
    %s
} """ % (
        ", ".join(arguments),
        source,
    )

    # print("Compilando QS en línea: ", qs_source)
    from .parsers.qsaparser import flscriptparse
    from .parsers.qsaparser import postparse
    from .parsers.qsaparser.pytnyzer import write_python_file

    prog = flscriptparse.parse(qs_source)
    if prog is None:
        raise ValueError("Failed to convert to Python")
    tree_data = flscriptparse.calctree(prog, alias_mode=0)
    ast = postparse.post_parse(tree_data)
    dest_filename = "%s/anon.py" % config.value("ebcomportamiento/temp_dir")
    # f1 = io.StringIO()
    if os.path.exists(dest_filename):
        os.remove(dest_filename)

    file_ = open(dest_filename, "w", encoding="UTF-8")

    write_python_file(file_, ast)
    file_.close()
    module = None
    module_path = "tempdata.anon"

    # if module_path in python_sys.modules:
    #    print("**", module_path)
    #    module = importlib.reload(python_sys.modules[module_path])
    # else:
    spec = importlib.util.spec_from_file_location(
        module_path, dest_filename)  # type: ignore
    module = importlib.util.module_from_spec(spec)  # type: ignore

    python_sys.modules[spec.name] = module
    spec.loader.exec_module(module)

    forminternalobj = getattr(module, "FormInternalObj", None)
    # os.remove(dest_filename)
    return getattr(forminternalobj(), "anon", None)
Exemple #22
0
 def isDebuggerEnabled(self) -> bool:
     """Check if this debugger is on."""
     return bool(config.value("application/dbadmin_enabled", False))
Exemple #23
0
 def isDebuggerMode(self) -> bool:
     """Check if running in debugger mode."""
     return bool(config.value("application/isDebuggerMode", False))
Exemple #24
0
    def processText(
        self,
        xml: Element,
        data_row: Optional[Element] = None,
        fix_height: bool = True,
        section_name: Optional[str] = None,
    ) -> None:
        """
        Check tag (calculated, label, special or image).

        @param xml. XML section to process.
        @param fix_height. Revise height from original .kut file except pageFooter.
        """
        is_image = False
        is_barcode = False
        text: str = xml.get("Text") or ""
        # borderColor = xml.get("BorderColor")
        field_name = xml.get("Field") or ""

        # x,y,W,H se calcula y corrigen aquí para luego estar correctos en los diferentes destinos posibles
        width = int(xml.get("Width") or "0")

        height = self._parser_tools.getHeight(xml)

        pos_x = int(xml.get("X") or "0")

        pos_y = (int(xml.get("Y") or "0") + self.topSection()
                 )  # Añade la altura que hay ocupada por otras secciones
        if fix_height:
            pos_y = self._parser_tools.ratio_correction_h(
                pos_y)  # Corrige la posición con respecto al kut original

        data_type = xml.get("DataType")

        if xml.tag == "Field" and data_row is not None:
            text = data_row.get(field_name) or ""

        elif xml.tag == "Special":
            if text == "":
                if xml.get("Type") == "1":
                    text = "PageNo"
            text = self._parser_tools.getSpecial(text,
                                                 self._actual_append_page_no)

        calculation_type = xml.get("CalculationType")

        if calculation_type is not None and xml.tag != "Field":
            if calculation_type == "6":
                function_name = xml.get("FunctionName")
                if function_name and data_row is not None:
                    try:
                        nodo = self._parser_tools.convertToNode(data_row)

                        ret_ = application.PROJECT.call(
                            function_name, [nodo, field_name], None, False)
                        if ret_ is False:
                            return
                        else:
                            text = str(ret_)

                    except Exception:
                        LOGGER.exception(
                            "KUT2FPDF:: Error llamando a function %s",
                            function_name)
                        return
                else:
                    return
            elif calculation_type == "1":
                text = self._parser_tools.calculate_sum(
                    field_name, self.last_data_processed, self._xml_data,
                    self.actual_data_level)

            elif calculation_type in ("5"):
                if data_row is None:
                    data_row = self._xml_data[0]

                text = data_row.get(field_name) or ""

        if data_type is not None:
            text = self._parser_tools.calculated(text, int(data_type),
                                                 xml.get("Precision"),
                                                 data_row)

        if data_type == "5":
            is_image = True

        elif data_type == "6":
            is_barcode = True

        if xml.get("BlankZero") == "1" and text is not None:
            res_ = re.findall(r"\d+", text)
            if res_ == ["0"]:
                return

        if text is not None:
            from pineboolib.core.settings import config

            temporal = config.value("ebcomportamiento/temp_dir")
            if text.startswith(temporal):
                is_image = True

        # negValueColor = xml.get("NegValueColor")
        # Currency = xml.get("Currency")
        #
        # commaSeparator = xml.get("CommaSeparator")
        # dateFormat = xml.get("DateFormat")

        if is_image:
            self.draw_image(pos_x, pos_y, width, height, xml, text)
        elif is_barcode:
            self.draw_barcode(pos_x, pos_y, width, height, xml, text)
        elif data_row is not None:
            level = data_row.get("level") or "0"
            if level and str(level) in self.detailn.keys():
                val = "%s_header_%s_%s" % (self.detailn[str(level)], level,
                                           field_name)

            if xml.get("DrawAtHeader") == "true" and level:
                if section_name == "DetailHeader":
                    val = ""
                    self.drawText(pos_x, pos_y, width, height, xml, val)

                    # print(level, section_name, val, text)

            if section_name == "DetailFooter" and xml.get(
                    "DrawAtHeader") == "true":
                self.draws_at_header[val] = text
                # print("Añadiendo a", val, text, level)

            else:
                self.drawText(pos_x, pos_y, width, height, xml, text)
Exemple #25
0
    def editarFichero(self) -> None:
        """Edit a file."""
        qsa.MessageBox.warning(
            qsa.util.translate("scripts", "Opción no disponible"),
            qsa.MessageBox.Yes,
            qsa.MessageBox.NoButton,
        )
        return
        cursor = self.cursor()
        util = qsa.FLUtil()
        if cursor.checkIntegrity():
            self.child("nombre").setDisabled(True)
            nombre = cursor.valueBuffer("nombre")
            tipo = self.tipoDeFichero(nombre)
            temporal = qsa.System.getenv("TMP")
            if temporal == "":
                temporal = qsa.System.getenv("TMPDIR")
            if temporal == "":
                temporal = qsa.System.getenv("HOME")
            if temporal == "":
                from pineboolib.core.settings import config

                temporal = config.value("ebcomportamiento/temp_dir")

            temporal = qsa.ustr(temporal, "/", cursor.valueBuffer("nombre"))
            contenido = self.child("contenido").toPlainText()
            comando = ""
            if tipo == ".ui":
                if util.getOS() == "MACX":
                    qsa.FileStatic.write(
                        temporal, qsa.ustr(contenido, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
                    )
                    comando = qsa.ustr(
                        qsa.sys.installPrefix(), "/bin/designer.app/Contents/MacOS/designer"
                    )
                else:
                    qsa.FileStatic.write(temporal, contenido)
                    comando = qsa.ustr(qsa.sys.installPrefix(), "/bin/designer")

                self.setDisabled(True)
                qsa.ProcessStatic.execute(qsa.Array([comando, temporal]))
                self.child(u"contenido").setText(qsa.FileStatic.read(temporal))
                self.setDisabled(False)
            elif tipo == ".ts":
                if util.getOS() == "MACX":
                    qsa.FileStatic.write(
                        temporal, qsa.ustr(contenido, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
                    )
                    comando = qsa.ustr(
                        qsa.sys.installPrefix(), "/bin/linguist.app/Contents/MacOS/linguist"
                    )
                else:
                    qsa.FileStatic.write(temporal, contenido)
                    comando = qsa.ustr(qsa.sys.installPrefix(), "/bin/linguist")

                self.setDisabled(True)
                qsa.ProcessStatic.execute(qsa.Array([comando, temporal]))
                self.child("contenido").setText(qsa.FileStatic.read(temporal))
                self.setDisabled(False)
            elif tipo == ".kut":
                if util.getOS() == "MACX":
                    qsa.FileStatic.write(
                        temporal, qsa.ustr(contenido, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
                    )
                    comando = qsa.ustr(
                        qsa.sys.installPrefix(), "/bin/kudesigner.app/Contents/MacOS/kudesigner"
                    )
                else:
                    qsa.FileStatic.write(temporal, contenido)
                    comando = qsa.ustr(qsa.sys.installPrefix(), "/bin/kudesigner")

                self.setDisabled(True)
                qsa.ProcessStatic.execute(qsa.Array([comando, temporal]))
                self.child("contenido").setText(qsa.FileStatic.read(temporal))
                self.setDisabled(False)

            elif tipo in (".qs", ".py"):
                self.setDisabled(True)
                editor_ = qsa.FLScriptEditor(nombre)
                editor_.exec_()
                self.child("contenido").setText(editor_.code())
                self.setDisabled(False)
            else:
                self.setDisabled(True)
                dialog = qsa.Dialog()
                dialog.setWidth(600)
                dialog.cancelButtonText = ""
                editor = qsa.TextEdit()
                if editor is None:
                    raise Exception("editor is empty!")
                editor.textFormat = editor.PlainText
                editor.text = contenido
                dialog.add(editor)
                dialog.exec_()
                self.child("contenido").setText(editor.text)
                self.setDisabled(False)
Exemple #26
0
def generate_model(mtd_table: PNTableMetaData) -> List[str]:
    """
    Create a list of lines from a mtd_table (PNTableMetaData).
    """
    data = []
    pk_found = False
    data.append("# -*- coding: utf-8 -*-")
    # data.append("from sqlalchemy.ext.declarative import declarative_base")
    data.append(
        "from sqlalchemy import Column, Integer, Numeric, String, BigInteger, Boolean, DateTime, ForeignKey, LargeBinary"
    )
    data.append("from sqlalchemy.orm import relationship, validates")
    data.append(
        "from pineboolib.application.parsers.mtdparser.pnormmodelsfactory import Calculated, load_model"
    )
    data.append("from pineboolib import application")
    data.append("")
    # data.append("Base = declarative_base()")
    data.append("Base = application.PROJECT.conn_manager.mainConn().declarative_base()")
    data.append("engine = application.PROJECT.conn_manager.mainConn().engine()")
    data.append("")
    # for field in mtd_table.fieldList():
    #    if field.relationM1():
    #        rel = field.relationM1()
    #        data.append("load_model('%s')" % rel.foreignTable())

    data.append("")
    data.append("class %s%s(Base):" % (mtd_table.name()[0].upper(), mtd_table.name()[1:]))
    data.append("    __tablename__ = '%s'" % mtd_table.name())
    data.append("")

    validator_list: List[str] = []

    data.append("")
    data.append("# --- Fields ---> ")
    data.append("")

    for field in mtd_table.fieldList():  # Crea los campos
        if field.name() in validator_list:
            LOGGER.warning(
                "Hay un campo %s duplicado en %s.mtd. Omitido", field.name(), mtd_table.name()
            )
        else:
            field_data = []
            field_data.append("    ")
            field_data.append(
                "%s" % field.name() + "_" if field.name() in RESERVER_WORDS else field.name()
            )
            field_data.append(" = Column('%s', " % field.name())
            field_data.append(field_type(field))
            field_data.append(")")
            validator_list.append(field.name())
            if field.isPrimaryKey():
                pk_found = True

        data.append("".join(field_data))

    data.append("")
    data.append("# <--- Fields --- ")
    data.append("")

    data.append("")
    data.append("# --- Relations 1:M ---> ")
    data.append("")
    if application.PROJECT.conn_manager is None:
        raise Exception("Project is not connected yet")
    manager = application.PROJECT.conn_manager.manager()
    for field in mtd_table.fieldList():  # Creamos relaciones 1M
        for relation in field.relationList():
            foreign_table_mtd = manager.metadata(relation.foreignTable())
            # if application.PROJECT.conn.manager().existsTable(r.foreignTable()):
            if foreign_table_mtd:
                # comprobamos si existe el campo...
                if foreign_table_mtd.field(relation.foreignField()):

                    foreign_object = "%s%s" % (
                        relation.foreignTable()[0].upper(),
                        relation.foreignTable()[1:],
                    )
                    relation_ = "    %s_%s = relationship('%s'" % (
                        relation.foreignTable(),
                        relation.foreignField(),
                        foreign_object,
                    )
                    relation_ += ", foreign_keys='%s.%s'" % (
                        foreign_object,
                        relation.foreignField(),
                    )
                    relation_ += ")"

                    data.append(relation_)

    data.append("")
    data.append("# <--- Relations 1:M --- ")
    data.append("")
    data.append("")
    data.append("")
    data.append("    @validates('%s')" % "','".join(validator_list))
    data.append("    def validate(self, key, value):")
    data.append(
        "        self.__dict__[key] = value #Chapuza para que el atributo ya contenga el valor"
    )
    data.append("        self.bufferChanged(key)")
    data.append("        return value #Ahora si se asigna de verdad")
    data.append("")
    data.append("    def bufferChanged(self, fn):")
    data.append("        pass")

    data.append("")
    data.append("    def beforeCommit(self):")
    data.append("        return True")
    data.append("")
    data.append("    def afterCommit(self):")
    data.append("        return True")
    data.append("")
    data.append("    def commitBuffer(self):")
    data.append("        if not self.beforeCommit():")
    data.append("            return False")
    data.append("")
    data.append("        aqApp.db().session().commit()")
    data.append("")
    data.append("        if not self.afterCommit():")
    data.append("            return False")

    # for field in mtd_table.fieldList():  # Relaciones M:1
    #     if field.relationList():
    #         rel_data = []
    #         for r in field.relationList():
    #             if r.cardinality() == r.RELATION_1M:
    #                 obj_name = "%s%s" % (r.foreignTable()[0].upper(), r.foreignTable()[1:])
    #                 rel_data.append(
    #                     "    %s = relationship('%s', backref='parent'%s)\n"
    #                     % (r.foreignTable(), obj_name, ", cascade ='all, delete'" if r.deleteCascade() else "")
    #                 )
    #
    #         data.append("".join(rel_data))
    #
    # data.append("if not engine.dialect.has_table(engine.connect(),'%s'):" % mtd_table.name())
    # data.append("    %s%s.__table__.create(engine)" % (mtd_table.name()[0].upper(), mtd_table.name()[1:]))

    if not pk_found:
        from pineboolib.core.settings import config

        if config.value("application/isDebuggerMode", False):
            LOGGER.warning(
                "La tabla %s no tiene definida una clave primaria. No se generará el model."
                % (mtd_table.name())
            )
        data = []

    return data
    def parseKey(self, ref_key: Optional[str] = None) -> Optional[str]:
        """
        Get filename of .png file cached on tempdata. If it does not exist it is created.

        @param. String of related tuple in fllarge.
        @return. Path to the file in tempdata.
        """

        ret = None
        table_name = "fllarge"
        if ref_key is not None:
            from PyQt5.QtGui import QPixmap

            value = None
            tmp_dir = config.value("ebcomportamiento/temp_dir")
            img_file = "%s/%s.png" % (tmp_dir, ref_key)

            if not os.path.exists(img_file) and ref_key[0:3] == "RK@":

                single_query = pnsqlquery.PNSqlQuery()
                single_query.exec_(
                    "SELECT valor FROM flsettings WHERE flkey='FLLargeMode'")
                one_fllarge = True

                if single_query.next():
                    if single_query.value(0) == "True":
                        one_fllarge = False

                if (
                        not one_fllarge
                ):  # Si no es FLLarge modo único añadimos sufijo "_nombre" a fllarge
                    table_name += "_%s" % ref_key.split("@")[1]

                qry = pnsqlquery.PNSqlQuery()
                qry.exec_("SELECT contenido FROM %s WHERE refkey='%s'" %
                          (table_name, ref_key))
                if qry.next():
                    value = xpm.cache_xpm(qry.value(0))

                if value:
                    ret = img_file
                    pix = QPixmap(value)
                    if not pix.save(img_file):
                        self.logger.warning(
                            "%s:refkey2cache No se ha podido guardar la imagen %s"
                            % (__name__, img_file))
                        ret = None
                    else:
                        ret = img_file
            elif ref_key.endswith(".xpm"):
                pix = QPixmap(ref_key)
                img_file = ref_key.replace(".xpm", ".png")
                if not pix.save(img_file):
                    self.logger.warning(
                        "%s:refkey2cache No se ha podido guardar la imagen %s"
                        % (__name__, img_file))
                    ret = None
                else:
                    ret = img_file

            else:

                ret = img_file

        return ret
Exemple #28
0
class ProjectConfig:
    """
    Read and write XML on profiles. Represents a database connection configuration.
    """

    SAVE_VERSION = VERSION_1_2  #: Version for saving

    #: Folder where to read/write project configs.
    profile_dir: str = filedir(
        config.value("ebcomportamiento/profiles_folder",
                     "%s/Pineboo/profiles" % Path.home()))

    version: VersionNumber  #: Version number for the profile read.
    fernet: Optional[Fernet]  #: Cipher used, if any.

    database: str  #: Database Name, file path to store it, or :memory:
    host: Optional[str]  #: DB server Hostname. None for local files.
    port: Optional[int]  #: DB server port. None for local files.
    username: Optional[str]  #: Database User login name.
    password: Optional[str]  #: Database User login password.
    type: str  #: Driver Type name to use when connecting
    project_password: str  #: Password to cipher when load/saving. Empty string for no ciphering.
    password_required: bool  #: True if a password is required to read data. (Was partially loaded.)
    description: str  #: Project name in GUI
    filename: str  #: File path to read / write this project from / to

    def __init__(
        self,
        database: Optional[str] = None,
        host: Optional[str] = None,
        port: Optional[int] = None,
        type: Optional[str] = None,
        username: Optional[str] = None,
        password: Optional[str] = None,
        load_xml: Optional[str] = None,
        connstring: Optional[str] = None,
        description: Optional[str] = None,
        filename: Optional[str] = None,
        project_password: str = "",
    ) -> None:
        """Initialize."""
        self.project_password = project_password
        self.password_required = False
        self.version = self.SAVE_VERSION
        self.fernet = None

        if connstring:
            username, password, type, host, port, database = self.translate_connstring(
                connstring)
        elif load_xml:
            self.filename = os.path.join(
                self.profile_dir,
                load_xml if load_xml.endswith("xml") else "%s.xml" % load_xml)
            self.load_projectxml()
            return
        if database is None:
            raise ValueError(
                "Database is mandatory. Or use load_xml / connstring params")
        if type is None:
            raise ValueError(
                "Type is mandatory. Or use load_xml / connstring params")
        self.database = database
        self.host = host
        self.port = port
        self.type = type
        self.username = username
        self.password = password
        self.description = description if description else "unnamed"
        if filename is None:
            file_basename = self.description.lower().replace(" ", "_")
            self.filename = os.path.join(self.profile_dir,
                                         "%s.xml" % file_basename)
        else:
            self.filename = os.path.join(self.profile_dir, filename)

    def get_uri(self, show_password: bool = False) -> str:
        """Get connection as an URI."""
        host_port = ""
        if self.host:
            host_port += self.host
        if self.port:
            host_port += ":%d" % self.port

        user_pass = ""
        if self.username:
            user_pass += self.username
        if self.password:
            if show_password:
                user_pass += ":%s" % self.password
            else:
                pass_bytes: bytes = hashlib.sha256(
                    self.password.encode()).digest()
                user_pass += ":*" + base64.b64encode(pass_bytes).decode()[:4]

        if user_pass:
            user_pass = "******" % user_pass

        uri = host_port + user_pass

        if self.database:
            if uri:
                uri += "/"
            uri += self.database

        return "[%s]://%s" % (self.type, uri)

    def __repr__(self) -> str:
        """Display the information in text mode."""
        if self.project_password:
            # 4 chars in base-64 is 3 bytes. 256**3 should be enough to know if you have the wrong
            # password.
            pass_bytes: bytes = hashlib.sha256(
                self.project_password.encode()).digest()
            passwd = "-" + base64.b64encode(pass_bytes).decode()[:4]
        else:
            passwd = ""
        return "<ProjectConfig%s name=%r uri=%r>" % (
            passwd,
            self.description,
            self.get_uri(show_password=False),
        )

    def __eq__(self, other: Any) -> bool:
        """Test for equality."""
        if not isinstance(other, ProjectConfig):
            return False
        if other.type != self.type:
            return False
        if other.get_uri(show_password=True) != self.get_uri(
                show_password=True):
            return False
        if other.description != self.description:
            return False
        if other.project_password != self.project_password:
            return False
        return True

    def load_projectxml(self) -> bool:
        """Collect the connection information from an xml file."""

        file_name = self.filename
        if not os.path.isfile(file_name):
            raise ValueError("El proyecto %r no existe." % file_name)

        tree = ET.parse(file_name)
        root = tree.getroot()
        version = VersionNumber(root.get("Version"), default="1.0")
        self.version = version
        self.description = ""
        for xmldescription in root.findall("name"):
            self.description = xmldescription.text or ""
        profile_pwd = ""
        for profile in root.findall("profile-data"):
            profile_pwd = getattr(profile.find("password"), "text", "")
            if profile_pwd:
                break

        self.password_required = True
        self.checkProfilePasswordForVersion(self.project_password, profile_pwd,
                                            version)

        if self.project_password and self.version > VERSION_1_1:
            key_salt = hashlib.sha256(profile_pwd.encode()).digest()
            key = hashlib.pbkdf2_hmac("sha256", self.project_password.encode(),
                                      key_salt, 10000)
            key64 = base64.urlsafe_b64encode(key)
            self.fernet = Fernet(key64)
        else:
            self.fernet = None

        from pineboolib.application.database.pnsqldrivers import PNSqlDrivers

        sql_drivers_manager = PNSqlDrivers()
        self.database = self.retrieveCipherSubElement(root, "database-name")
        for db in root.findall("database-server"):
            self.host = self.retrieveCipherSubElement(db, "host")
            port_text = self.retrieveCipherSubElement(db, "port")
            self.port = int(port_text) if port_text else None
            self.type = self.retrieveCipherSubElement(db, "type")

            # FIXME: Move this to project, or to the connection handler.
            if self.type not in sql_drivers_manager.aliasList():
                LOGGER.warning(
                    "Esta versión de pineboo no soporta el driver '%s'" %
                    self.type)

        for credentials in root.findall("database-credentials"):
            self.username = self.retrieveCipherSubElement(
                credentials, "username")
            self.password = self.retrieveCipherSubElement(
                credentials, "password")
            if self.password and self.fernet is None:
                self.password = base64.b64decode(self.password).decode()
        self.password_required = False

        return True

    @classmethod
    def encodeProfilePasswordForVersion(cls, password: str,
                                        save_version: VersionNumber) -> str:
        """
        Hash a password for a profile/projectconfig using the protocol for specified version.
        """
        if password == "":
            return ""
        if save_version < VERSION_1_1:
            return password

        if save_version < VERSION_1_2:
            return hashlib.sha256(password.encode()).hexdigest()
        # Minimum salt size recommended is 8 bytes
        # multiple of 3 bytes as it is shorter in base64
        salt = os.urandom(9)
        hmac = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 10000)
        dict_passwd = {
            # Algorithm:
            # .. pbkdf2: short algorithm name
            # .. sha256: hash function used
            # .. 4: number of zeroes used on iterations
            "algorithm": "pbkdf2-sha256-4",
            "salt": base64.b64encode(salt).decode(),
            "hash": base64.b64encode(hmac).decode(),
        }
        hashed_password = "******" % dict_passwd

        return hashed_password

    @classmethod
    def checkProfilePasswordForVersion(cls, user_pwd: str, profile_pwd: str,
                                       version: VersionNumber) -> None:
        """
        Check a saved password against a user-supplied one.

        user_pwd: User-supplied password in clear text.
        profile_pwd: Raw data saved as password in projectconfig file.
        version: Version number used for checks.

        This function returns None and raises PasswordMismatchError if the password is wrong.
        We can only check if it is good. It's not a good idea to check if two encoded passwords
        are the same, because most secure methods will store different encoded versions every
        time we try to encode again.
        """
        if not profile_pwd:
            return

        if version < VERSION_1_1:
            if user_pwd == profile_pwd:
                return
            raise PasswordMismatchError("La contraseña es errónea")

        if version < VERSION_1_2:
            user_hash = hashlib.sha256(user_pwd.encode()).hexdigest()
            if profile_pwd == user_hash:
                return
            raise PasswordMismatchError("La contraseña es errónea")

        algo, *algo_extra = profile_pwd.split(":")
        if algo != "pbkdf2-sha256-4":
            raise Exception("Unsupported password algorithm %r" % algo)

        salt64, hash64 = algo_extra

        salt = base64.b64decode(salt64.encode())

        user_hash2 = hashlib.pbkdf2_hmac("sha256", user_pwd.encode(), salt,
                                         10000)
        user_hash64 = base64.b64encode(user_hash2).decode()
        if user_hash64 == hash64:
            return

        raise PasswordMismatchError("La contraseña es errónea")

    def createCipherSubElement(self, parent: ET.Element, tagname: str,
                               text: str) -> ET.Element:
        """Create a XML SubElement ciphered if self.fernet is present."""
        child = ET.SubElement(parent, tagname)
        if self.fernet is None:
            child.text = text
            return child
        # NOTE: This method returns ciphertext even for empty strings! This is intended.
        # ... this is to avoid anyone knowing if a field is empty or not.
        if len(text) < 64:
            # Right Pad with new line at least up to 64 bytes. Avoid giving out field size.
            text = text.ljust(64, "\n")
        encoded_bytes = self.fernet.encrypt(text.encode())
        encoded_text = base64.b64encode(encoded_bytes).decode()
        child.set("cipher-method", "cryptography.Fernet")
        child.set("cipher-text", encoded_text)
        return child

    def retrieveCipherSubElement(self, parent: ET.Element,
                                 tagname: str) -> str:
        """Get a XML SubElement ciphered if self.fernet is present."""
        child = parent.find(tagname)
        if child is None:
            raise ValueError("Tag %r not present" % tagname)

        cipher_method = child.get("cipher-method")
        if cipher_method is None:
            return child.text or ""
        if self.fernet is None:
            raise Exception(
                "Tried to load ciphered tag %r with no loaded cipher" %
                tagname)
        cipher_text = child.get("cipher-text")
        if cipher_method != "cryptography.Fernet":
            raise ValueError("Cipher method %r not supported." % cipher_method)

        if not cipher_text:
            raise ValueError("Missing ciphertext for %r" % tagname)

        cipher_bytes = base64.b64decode(cipher_text.encode())
        text = self.fernet.decrypt(cipher_bytes).decode()
        text = text.rstrip("\n")
        return text

    def save_projectxml(self, overwrite_existing: bool = True) -> None:
        """
        Save the connection.
        """
        profile = ET.Element("Profile")
        profile.set("Version", str(self.SAVE_VERSION))
        description = self.description
        filename = self.filename
        if not os.path.exists(self.profile_dir):
            os.mkdir(self.profile_dir)

        if not overwrite_existing and os.path.exists(filename):
            raise ProfileAlreadyExistsError

        passwDB = self.password or ""

        profile_user = ET.SubElement(profile, "profile-data")
        profile_password = ET.SubElement(profile_user, "password")
        profile_password.text = self.encodeProfilePasswordForVersion(
            self.project_password, self.SAVE_VERSION)
        if self.project_password and self.SAVE_VERSION > VERSION_1_1:
            key_salt = hashlib.sha256(profile_password.text.encode()).digest()
            key = hashlib.pbkdf2_hmac("sha256", self.project_password.encode(),
                                      key_salt, 10000)
            key64 = base64.urlsafe_b64encode(key)
            self.fernet = Fernet(key64)
        else:
            # Mask the password if no cipher is used!
            passwDB = base64.b64encode(passwDB.encode()).decode()
            self.fernet = None
        name = ET.SubElement(profile, "name")
        name.text = description
        dbs = ET.SubElement(profile, "database-server")
        self.createCipherSubElement(dbs, "type", text=self.type)
        self.createCipherSubElement(dbs, "host", text=self.host or "")
        self.createCipherSubElement(dbs,
                                    "port",
                                    text=str(self.port) if self.port else "")

        dbc = ET.SubElement(profile, "database-credentials")
        self.createCipherSubElement(dbc, "username", text=self.username or "")
        self.createCipherSubElement(dbc, "password", text=passwDB)

        self.createCipherSubElement(profile,
                                    "database-name",
                                    text=self.database)

        pretty_print_xml(profile)

        tree = ET.ElementTree(profile)

        tree.write(filename, xml_declaration=True, encoding="utf-8")
        self.version = self.SAVE_VERSION

    @classmethod
    def translate_connstring(
            cls, connstring: str) -> Tuple[str, str, str, str, int, str]:
        """
        Translate a DSN connection string into user, pass, etc.

        Accept a "connstring" parameter that has the form user @ host / dbname
        and returns all parameters separately. It takes into account the
        default values ​​and the different abbreviations that exist.
        """
        user = "******"
        passwd = ""
        host = "127.0.0.1"
        port = "5432"
        driver_alias = "PostgreSQL (PSYCOPG2)"
        user_pass = None
        host_port = None
        if "/" not in connstring:
            dbname = connstring
            if not re.match(r"\w+", dbname):
                raise ValueError("base de datos no valida")
            return user, passwd, driver_alias, host, int(port), dbname

        uphpstring = connstring[:connstring.rindex("/")]
        dbname = connstring[connstring.rindex("/") + 1:]
        up, hp = uphpstring.split("@")
        conn_list = [None, None, up, hp]
        _user_pass, _host_port = conn_list[-2], conn_list[-1]

        if _user_pass:
            user_pass = _user_pass.split(":") + ["", "", ""]
            user, passwd, driver_alias = (
                user_pass[0],
                user_pass[1] or passwd,
                user_pass[2] or driver_alias,
            )
            if user_pass[3]:
                raise ValueError(
                    "La cadena de usuario debe tener el formato user:pass:driver."
                )

        if _host_port:
            host_port = _host_port.split(":") + [""]
            host, port = host_port[0], host_port[1] or port
            if host_port[2]:
                raise ValueError("La cadena de host debe ser host:port.")

        if not re.match(r"\w+", user):
            raise ValueError("Usuario no valido")
        if not re.match(r"\w+", dbname):
            raise ValueError("base de datos no valida")
        if not re.match(r"\d+", port):
            raise ValueError("puerto no valido")
        LOGGER.debug(
            "user:%s, passwd:%s, driver_alias:%s, host:%s, port:%s, dbname:%s",
            user,
            "*" * len(passwd),
            driver_alias,
            host,
            port,
            dbname,
        )
        return user, passwd, driver_alias, host, int(port), dbname